@@ -41,8 +41,8 @@ def test_basic(self):
4141
4242 @skipUnlessDBFeature ('validates_explain_options' )
4343 def test_unknown_options (self ):
44- with self .assertRaisesMessage (ValueError , ' Unknown options: test, test2' ):
45- Tag .objects .all ().explain (test = 1 , test2 = 1 )
44+ with self .assertRaisesMessage (ValueError , " Unknown options: TEST, TEST2" ):
45+ Tag .objects .all ().explain (** { "TEST" : 1 , "TEST2" : 1 } )
4646
4747 def test_unknown_format (self ):
4848 msg = 'DOES NOT EXIST is not a recognized format.'
@@ -71,6 +71,35 @@ def test_postgres_options(self):
7171 option = '{} {}' .format (name .upper (), 'true' if value else 'false' )
7272 self .assertIn (option , captured_queries [0 ]['sql' ])
7373
74+ def test_option_sql_injection (self ):
75+ qs = Tag .objects .filter (name = "test" )
76+ options = {"SUMMARY true) SELECT 1; --" : True }
77+ msg = "Invalid option name: 'SUMMARY true) SELECT 1; --'"
78+ with self .assertRaisesMessage (ValueError , msg ):
79+ qs .explain (** options )
80+
81+ def test_invalid_option_names (self ):
82+ qs = Tag .objects .filter (name = "test" )
83+ tests = [
84+ 'opt"ion' ,
85+ "o'ption" ,
86+ "op`tion" ,
87+ "opti on" ,
88+ "option--" ,
89+ "optio\t n" ,
90+ "o\n ption" ,
91+ "option;" ,
92+ "你 好" ,
93+ # [] are used by MSSQL.
94+ "option[" ,
95+ "option]" ,
96+ ]
97+ for invalid_option in tests :
98+ with self .subTest (invalid_option ):
99+ msg = "Invalid option name: '%s'" % invalid_option
100+ with self .assertRaisesMessage (ValueError , msg ):
101+ qs .explain (** {invalid_option : True })
102+
74103 @unittest .skipUnless (connection .vendor == 'mysql' , 'MySQL specific' )
75104 def test_mysql_text_to_traditional (self ):
76105 # Initialize the cached property, if needed, to prevent a query for
0 commit comments