@@ -59,8 +59,8 @@ def test_basic(self):
5959
6060 @skipUnlessDBFeature ("validates_explain_options" )
6161 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 } )
6464
6565 def test_unknown_format (self ):
6666 msg = "DOES NOT EXIST is not a recognized format."
@@ -94,6 +94,35 @@ def test_postgres_options(self):
9494 option = "{} {}" .format (name .upper (), "true" if value else "false" )
9595 self .assertIn (option , captured_queries [0 ]["sql" ])
9696
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+
97126 @unittest .skipUnless (connection .vendor == "mysql" , "MySQL specific" )
98127 def test_mysql_text_to_traditional (self ):
99128 # Ensure these cached properties are initialized to prevent queries for
0 commit comments