Skip to content
Open
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Improved unit tests from gps.
  • Loading branch information
nascheme committed Jun 16, 2022
commit a00656c572c121e131808c18c16ae8f05f2b0be4
27 changes: 21 additions & 6 deletions Lib/test/test_httpservers.py
Original file line number Diff line number Diff line change
Expand Up @@ -421,23 +421,38 @@ def test_undecodable_filename(self):
def test_get_dir_redirect_location_domain_injection_bug(self):
Comment thread
nascheme marked this conversation as resolved.
"""Ensure //evil.co/..%2f../../X does not put //evil.co/ in Location.

//domain/ in a Location header is a redirect to a new domain name.
//netloc/ in a Location header is a redirect to a new host.
https://github.com/python/cpython/issues/87389

This checks that a path resolving to a directory on our server cannot
resolve into a redirect to another server telling it that the
directory in question exists on the Referrer server.
resolve into a redirect to another server.
"""
os.mkdir(os.path.join(self.tempdir, 'existing_directory'))
attack_url = f'//python.org/..%2f..%2f..%2f..%2f..%2f../%0a%0d/../{self.tempdir_name}/existing_directory'
url = f'/python.org/..%2f..%2f..%2f..%2f..%2f../%0a%0d/../{self.tempdir_name}/existing_directory'
expected_location = f'{url}/' # /python.org.../ single slash single prefix, trailing slash
# Canonicalizes to /tmp/tempdir_name/existing_directory which does
# exist and is a dir, triggering the 301 redirect logic.
response = self.request(url)
self.check_status_and_reason(response, HTTPStatus.MOVED_PERMANENTLY)
location = response.getheader('Location')
self.assertEqual(location, expected_location, msg='non-attack failed!')

# //python.org... multi-slash prefix, no trailing slash
attack_url = f'/{url}'
response = self.request(attack_url)
self.check_status_and_reason(response, HTTPStatus.MOVED_PERMANENTLY)
location = response.getheader('Location')
self.assertFalse(location.startswith('//'), msg=location)
self.assertEqual(location, attack_url[1:] + '/',
msg='Expected Location: to start with a single / and '
self.assertEqual(location, expected_location,
msg='Expected Location header to start with a single / and '
'end with a / as this is a directory redirect.')

# ///python.org... triple-slash prefix, no trailing slash
attack3_url = f'//{url}'
response = self.request(attack3_url)
self.check_status_and_reason(response, HTTPStatus.MOVED_PERMANENTLY)
self.assertEqual(response.getheader('Location'), expected_location)

def test_get(self):
#constructs the path relative to the root directory of the HTTPServer
response = self.request(self.base_url + '/test')
Expand Down