@@ -59,8 +59,8 @@ def test_basic(self):
59
59
60
60
@skipUnlessDBFeature ("validates_explain_options" )
61
61
def test_unknown_options (self ):
62
- with self .assertRaisesMessage (ValueError , "Unknown options: test, test2 " ):
63
- Tag .objects .all ().explain (test = 1 , test2 = 1 )
62
+ with self .assertRaisesMessage (ValueError , "Unknown options: TEST, TEST2 " ):
63
+ Tag .objects .all ().explain (** { "TEST" : 1 , "TEST2" : 1 } )
64
64
65
65
def test_unknown_format (self ):
66
66
msg = "DOES NOT EXIST is not a recognized format."
@@ -94,6 +94,35 @@ def test_postgres_options(self):
94
94
option = "{} {}" .format (name .upper (), "true" if value else "false" )
95
95
self .assertIn (option , captured_queries [0 ]["sql" ])
96
96
97
+ def test_option_sql_injection (self ):
98
+ qs = Tag .objects .filter (name = "test" )
99
+ options = {"SUMMARY true) SELECT 1; --" : True }
100
+ msg = "Invalid option name: 'SUMMARY true) SELECT 1; --'"
101
+ with self .assertRaisesMessage (ValueError , msg ):
102
+ qs .explain (** options )
103
+
104
+ def test_invalid_option_names (self ):
105
+ qs = Tag .objects .filter (name = "test" )
106
+ tests = [
107
+ 'opt"ion' ,
108
+ "o'ption" ,
109
+ "op`tion" ,
110
+ "opti on" ,
111
+ "option--" ,
112
+ "optio\t n" ,
113
+ "o\n ption" ,
114
+ "option;" ,
115
+ "你 好" ,
116
+ # [] are used by MSSQL.
117
+ "option[" ,
118
+ "option]" ,
119
+ ]
120
+ for invalid_option in tests :
121
+ with self .subTest (invalid_option ):
122
+ msg = f"Invalid option name: { invalid_option !r} "
123
+ with self .assertRaisesMessage (ValueError , msg ):
124
+ qs .explain (** {invalid_option : True })
125
+
97
126
@unittest .skipUnless (connection .vendor == "mysql" , "MySQL specific" )
98
127
def test_mysql_text_to_traditional (self ):
99
128
# Ensure these cached properties are initialized to prevent queries for
0 commit comments