@@ -34,8 +34,8 @@ def test_basic(self):
3434
3535 @skipUnlessDBFeature ('validates_explain_options' )
3636 def test_unknown_options (self ):
37- with self .assertRaisesMessage (ValueError , ' Unknown options: test, test2' ):
38- Tag .objects .all ().explain (test = 1 , test2 = 1 )
37+ with self .assertRaisesMessage (ValueError , " Unknown options: TEST, TEST2" ):
38+ Tag .objects .all ().explain (** { "TEST" : 1 , "TEST2" : 1 } )
3939
4040 def test_unknown_format (self ):
4141 msg = 'DOES NOT EXIST is not a recognized format.'
@@ -68,6 +68,35 @@ def test_postgres_options(self):
6868 option = '{} {}' .format (name .upper (), 'true' if value else 'false' )
6969 self .assertIn (option , captured_queries [0 ]['sql' ])
7070
71+ def test_option_sql_injection (self ):
72+ qs = Tag .objects .filter (name = "test" )
73+ options = {"SUMMARY true) SELECT 1; --" : True }
74+ msg = "Invalid option name: 'SUMMARY true) SELECT 1; --'"
75+ with self .assertRaisesMessage (ValueError , msg ):
76+ qs .explain (** options )
77+
78+ def test_invalid_option_names (self ):
79+ qs = Tag .objects .filter (name = "test" )
80+ tests = [
81+ 'opt"ion' ,
82+ "o'ption" ,
83+ "op`tion" ,
84+ "opti on" ,
85+ "option--" ,
86+ "optio\t n" ,
87+ "o\n ption" ,
88+ "option;" ,
89+ "你 好" ,
90+ # [] are used by MSSQL.
91+ "option[" ,
92+ "option]" ,
93+ ]
94+ for invalid_option in tests :
95+ with self .subTest (invalid_option ):
96+ msg = f"Invalid option name: { invalid_option !r} "
97+ with self .assertRaisesMessage (ValueError , msg ):
98+ qs .explain (** {invalid_option : True })
99+
71100 @unittest .skipUnless (connection .vendor == 'mysql' , 'MySQL specific' )
72101 def test_mysql_text_to_traditional (self ):
73102 # Ensure these cached properties are initialized to prevent queries for
0 commit comments