mirror of
				https://github.com/python/cpython.git
				synced 2025-10-31 05:31:20 +00:00 
			
		
		
		
	[3.9] bpo-42967: only use '&' as a query string separator (GH-24297) (#24528)
(cherry picked from commit fcbe0cb04d)
* [3.9] bpo-42967: only use '&' as a query string separator (GH-24297)
bpo-42967: [security] Address a web cache-poisoning issue reported in urllib.parse.parse_qsl().
urllib.parse will only us "&" as query string separator by default instead of both ";" and "&" as allowed in earlier versions. An optional argument seperator with default value "&" is added to specify the separator.
Co-authored-by: Éric Araujo <merwok@netwok.org>
Co-authored-by: Ken Jin <28750310+Fidget-Spinner@users.noreply.github.com>
Co-authored-by: Adam Goldschmidt <adamgold7@gmail.com>
			
			
This commit is contained in:
		
							parent
							
								
									242f6c9ffe
								
							
						
					
					
						commit
						c9f07813ab
					
				
					 11 changed files with 176 additions and 46 deletions
				
			
		
							
								
								
									
										23
									
								
								Lib/cgi.py
									
										
									
									
									
								
							
							
						
						
									
										23
									
								
								Lib/cgi.py
									
										
									
									
									
								
							|  | @ -115,7 +115,8 @@ def closelog(): | |||
| # 0 ==> unlimited input | ||||
| maxlen = 0 | ||||
| 
 | ||||
| def parse(fp=None, environ=os.environ, keep_blank_values=0, strict_parsing=0): | ||||
| def parse(fp=None, environ=os.environ, keep_blank_values=0, | ||||
|           strict_parsing=0, separator='&'): | ||||
|     """Parse a query in the environment or from a file (default stdin) | ||||
| 
 | ||||
|         Arguments, all optional: | ||||
|  | @ -134,6 +135,9 @@ def parse(fp=None, environ=os.environ, keep_blank_values=0, strict_parsing=0): | |||
|         strict_parsing: flag indicating what to do with parsing errors. | ||||
|             If false (the default), errors are silently ignored. | ||||
|             If true, errors raise a ValueError exception. | ||||
| 
 | ||||
|         separator: str. The symbol to use for separating the query arguments. | ||||
|             Defaults to &. | ||||
|     """ | ||||
|     if fp is None: | ||||
|         fp = sys.stdin | ||||
|  | @ -154,7 +158,7 @@ def parse(fp=None, environ=os.environ, keep_blank_values=0, strict_parsing=0): | |||
|     if environ['REQUEST_METHOD'] == 'POST': | ||||
|         ctype, pdict = parse_header(environ['CONTENT_TYPE']) | ||||
|         if ctype == 'multipart/form-data': | ||||
|             return parse_multipart(fp, pdict) | ||||
|             return parse_multipart(fp, pdict, separator=separator) | ||||
|         elif ctype == 'application/x-www-form-urlencoded': | ||||
|             clength = int(environ['CONTENT_LENGTH']) | ||||
|             if maxlen and clength > maxlen: | ||||
|  | @ -178,10 +182,10 @@ def parse(fp=None, environ=os.environ, keep_blank_values=0, strict_parsing=0): | |||
|             qs = "" | ||||
|         environ['QUERY_STRING'] = qs    # XXX Shouldn't, really | ||||
|     return urllib.parse.parse_qs(qs, keep_blank_values, strict_parsing, | ||||
|                                  encoding=encoding) | ||||
|                                  encoding=encoding, separator=separator) | ||||
| 
 | ||||
| 
 | ||||
| def parse_multipart(fp, pdict, encoding="utf-8", errors="replace"): | ||||
| def parse_multipart(fp, pdict, encoding="utf-8", errors="replace", separator='&'): | ||||
|     """Parse multipart input. | ||||
| 
 | ||||
|     Arguments: | ||||
|  | @ -205,7 +209,7 @@ def parse_multipart(fp, pdict, encoding="utf-8", errors="replace"): | |||
|     except KeyError: | ||||
|         pass | ||||
|     fs = FieldStorage(fp, headers=headers, encoding=encoding, errors=errors, | ||||
|         environ={'REQUEST_METHOD': 'POST'}) | ||||
|         environ={'REQUEST_METHOD': 'POST'}, separator=separator) | ||||
|     return {k: fs.getlist(k) for k in fs} | ||||
| 
 | ||||
| def _parseparam(s): | ||||
|  | @ -315,7 +319,7 @@ class FieldStorage: | |||
|     def __init__(self, fp=None, headers=None, outerboundary=b'', | ||||
|                  environ=os.environ, keep_blank_values=0, strict_parsing=0, | ||||
|                  limit=None, encoding='utf-8', errors='replace', | ||||
|                  max_num_fields=None): | ||||
|                  max_num_fields=None, separator='&'): | ||||
|         """Constructor.  Read multipart/* until last part. | ||||
| 
 | ||||
|         Arguments, all optional: | ||||
|  | @ -363,6 +367,7 @@ def __init__(self, fp=None, headers=None, outerboundary=b'', | |||
|         self.keep_blank_values = keep_blank_values | ||||
|         self.strict_parsing = strict_parsing | ||||
|         self.max_num_fields = max_num_fields | ||||
|         self.separator = separator | ||||
|         if 'REQUEST_METHOD' in environ: | ||||
|             method = environ['REQUEST_METHOD'].upper() | ||||
|         self.qs_on_post = None | ||||
|  | @ -589,7 +594,7 @@ def read_urlencoded(self): | |||
|         query = urllib.parse.parse_qsl( | ||||
|             qs, self.keep_blank_values, self.strict_parsing, | ||||
|             encoding=self.encoding, errors=self.errors, | ||||
|             max_num_fields=self.max_num_fields) | ||||
|             max_num_fields=self.max_num_fields, separator=self.separator) | ||||
|         self.list = [MiniFieldStorage(key, value) for key, value in query] | ||||
|         self.skip_lines() | ||||
| 
 | ||||
|  | @ -605,7 +610,7 @@ def read_multi(self, environ, keep_blank_values, strict_parsing): | |||
|             query = urllib.parse.parse_qsl( | ||||
|                 self.qs_on_post, self.keep_blank_values, self.strict_parsing, | ||||
|                 encoding=self.encoding, errors=self.errors, | ||||
|                 max_num_fields=self.max_num_fields) | ||||
|                 max_num_fields=self.max_num_fields, separator=self.separator) | ||||
|             self.list.extend(MiniFieldStorage(key, value) for key, value in query) | ||||
| 
 | ||||
|         klass = self.FieldStorageClass or self.__class__ | ||||
|  | @ -649,7 +654,7 @@ def read_multi(self, environ, keep_blank_values, strict_parsing): | |||
|                 else self.limit - self.bytes_read | ||||
|             part = klass(self.fp, headers, ib, environ, keep_blank_values, | ||||
|                          strict_parsing, limit, | ||||
|                          self.encoding, self.errors, max_num_fields) | ||||
|                          self.encoding, self.errors, max_num_fields, self.separator) | ||||
| 
 | ||||
|             if max_num_fields is not None: | ||||
|                 max_num_fields -= 1 | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Senthil Kumaran
						Senthil Kumaran