Issue #24657: Prevent CGIRequestHandler from collapsing the URL query

Initial patch from Xiang Zhang. Also fix out-of-date _url_collapse_path() doc
string.
This commit is contained in:
Martin Panter 2015-10-03 05:55:46 +00:00
parent a02e18a43f
commit cb29e8c0e5
3 changed files with 19 additions and 4 deletions

View File

@ -887,13 +887,15 @@ def _url_collapse_path(path):
The utility of this function is limited to is_cgi method and helps The utility of this function is limited to is_cgi method and helps
preventing some security attacks. preventing some security attacks.
Returns: A tuple of (head, tail) where tail is everything after the final / Returns: The reconstituted URL, which will always start with a '/'.
and head is everything before it. Head will always start with a '/' and,
if it contains anything else, never have a trailing '/'.
Raises: IndexError if too many '..' occur within the path. Raises: IndexError if too many '..' occur within the path.
""" """
# Query component should not be involved.
path, _, query = path.partition('?')
path = urllib.parse.unquote(path)
# Similar to os.path.split(os.path.normpath(path)) but specific to URL # Similar to os.path.split(os.path.normpath(path)) but specific to URL
# path semantics rather than local operating system semantics. # path semantics rather than local operating system semantics.
path_parts = path.split('/') path_parts = path.split('/')
@ -914,6 +916,9 @@ def _url_collapse_path(path):
else: else:
tail_part = '' tail_part = ''
if query:
tail_part = '?'.join((tail_part, query))
splitpath = ('/' + '/'.join(head_parts), tail_part) splitpath = ('/' + '/'.join(head_parts), tail_part)
collapsed_path = "/".join(splitpath) collapsed_path = "/".join(splitpath)
@ -995,7 +1000,7 @@ class CGIHTTPRequestHandler(SimpleHTTPRequestHandler):
(and the next character is a '/' or the end of the string). (and the next character is a '/' or the end of the string).
""" """
collapsed_path = _url_collapse_path(urllib.parse.unquote(self.path)) collapsed_path = _url_collapse_path(self.path)
dir_sep = collapsed_path.find('/', 1) dir_sep = collapsed_path.find('/', 1)
head, tail = collapsed_path[:dir_sep], collapsed_path[dir_sep+1:] head, tail = collapsed_path[:dir_sep], collapsed_path[dir_sep+1:]
if head in self.cgi_directories: if head in self.cgi_directories:

View File

@ -565,6 +565,13 @@ class CGIHTTPServerTestCase(BaseTestCase):
(b'a=b?c=d' + self.linesep, 'text/html', 200), (b'a=b?c=d' + self.linesep, 'text/html', 200),
(res.read(), res.getheader('Content-type'), res.status)) (res.read(), res.getheader('Content-type'), res.status))
def test_query_with_continuous_slashes(self):
res = self.request('/cgi-bin/file4.py?k=aa%2F%2Fbb&//q//p//=//a//b//')
self.assertEqual(
(b'k=aa%2F%2Fbb&//q//p//=//a//b//' + self.linesep,
'text/html', 200),
(res.read(), res.getheader('Content-type'), res.status))
class SocketlessRequestHandler(SimpleHTTPRequestHandler): class SocketlessRequestHandler(SimpleHTTPRequestHandler):
def __init__(self): def __init__(self):

View File

@ -93,6 +93,9 @@ Library
- Issue #25232: Fix CGIRequestHandler to split the query from the URL at the - Issue #25232: Fix CGIRequestHandler to split the query from the URL at the
first question mark (?) rather than the last. Patch from Xiang Zhang. first question mark (?) rather than the last. Patch from Xiang Zhang.
- Issue #24657: Prevent CGIRequestHandler from collapsing slashes in the
query part of the URL as if it were a path. Patch from Xiang Zhang.
- Issue #22958: Constructor and update method of weakref.WeakValueDictionary - Issue #22958: Constructor and update method of weakref.WeakValueDictionary
now accept the self and the dict keyword arguments. now accept the self and the dict keyword arguments.