diff options
author | Mariusz Felisiak <felisiak.mariusz@gmail.com> | 2022-04-01 13:48:47 +0200 |
---|---|---|
committer | Mariusz Felisiak <felisiak.mariusz@gmail.com> | 2022-04-11 08:59:58 +0200 |
commit | 6723a26e59b0b5429a0c5873941e01a2e1bdbb81 (patch) | |
tree | 59bbe514736c482903de4d92046e9f58594680d3 /tests/queries | |
parent | 93cae5cb2f9a4ef1514cf1a41f714fef08005200 (diff) | |
download | django-6723a26e59b0b5429a0c5873941e01a2e1bdbb81.tar.gz |
Fixed CVE-2022-28347 -- Protected QuerySet.explain(**options) against SQL injection on PostgreSQL.
Diffstat (limited to 'tests/queries')
-rw-r--r-- | tests/queries/test_explain.py | 33 |
1 files changed, 31 insertions, 2 deletions
diff --git a/tests/queries/test_explain.py b/tests/queries/test_explain.py index ab845f992a..9eb4347323 100644 --- a/tests/queries/test_explain.py +++ b/tests/queries/test_explain.py @@ -59,8 +59,8 @@ class ExplainTests(TestCase): @skipUnlessDBFeature("validates_explain_options") def test_unknown_options(self): - with self.assertRaisesMessage(ValueError, "Unknown options: test, test2"): - Tag.objects.explain(test=1, test2=1) + with self.assertRaisesMessage(ValueError, "Unknown options: TEST, TEST2"): + Tag.objects.explain(**{"TEST": 1, "TEST2": 1}) def test_unknown_format(self): msg = "DOES NOT EXIST is not a recognized format." @@ -94,6 +94,35 @@ class ExplainTests(TestCase): option = "{} {}".format(name.upper(), "true" if value else "false") self.assertIn(option, captured_queries[0]["sql"]) + def test_option_sql_injection(self): + qs = Tag.objects.filter(name="test") + options = {"SUMMARY true) SELECT 1; --": True} + msg = "Invalid option name: 'SUMMARY true) SELECT 1; --'" + with self.assertRaisesMessage(ValueError, msg): + qs.explain(**options) + + def test_invalid_option_names(self): + qs = Tag.objects.filter(name="test") + tests = [ + 'opt"ion', + "o'ption", + "op`tion", + "opti on", + "option--", + "optio\tn", + "o\nption", + "option;", + "你 好", + # [] are used by MSSQL. + "option[", + "option]", + ] + for invalid_option in tests: + with self.subTest(invalid_option): + msg = f"Invalid option name: {invalid_option!r}" + with self.assertRaisesMessage(ValueError, msg): + qs.explain(**{invalid_option: True}) + @unittest.skipUnless(connection.vendor == "mysql", "MySQL specific") def test_mysql_text_to_traditional(self): # Ensure these cached properties are initialized to prevent queries for |