Skip to content

[2.7] bpo-38216, bpo-36274: Allow subclasses to separately override validation and encoding behavior (GH-16448) #16476

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Oct 8, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
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
33 changes: 22 additions & 11 deletions Lib/httplib.py
Original file line number Diff line number Diff line change
Expand Up @@ -933,19 +933,15 @@ def putrequest(self, method, url, skip_host=0, skip_accept_encoding=0):
else:
raise CannotSendRequest()

# Save the method we use, we need it later in the response phase
# Save the method for use later in the response phase
self._method = method
if not url:
url = '/'
# Prevent CVE-2019-9740.
match = _contains_disallowed_url_pchar_re.search(url)
if match:
raise InvalidURL("URL can't contain control characters. %r "
"(found at least %r)"
% (url, match.group()))
hdr = '%s %s %s' % (method, url, self._http_vsn_str)

self._output(hdr)
url = url or '/'
self._validate_path(url)

request = '%s %s %s' % (method, url, self._http_vsn_str)

self._output(self._encode_request(request))

if self._http_vsn == 11:
# Issue some standard headers for better HTTP/1.1 compliance
Expand Down Expand Up @@ -1018,6 +1014,21 @@ def putrequest(self, method, url, skip_host=0, skip_accept_encoding=0):
# For HTTP/1.0, the server will assume "not chunked"
pass

def _encode_request(self, request):
# On Python 2, request is already encoded (default)
return request

def _validate_path(self, url):
"""Validate a url for putrequest."""
# Prevent CVE-2019-9740.
match = _contains_disallowed_url_pchar_re.search(url)
if match:
msg = (
"URL can't contain control characters. {url!r} "
"(found at least {matched!r})"
).format(matched=match.group(), url=url)
raise InvalidURL(msg)

def putheader(self, header, *values):
"""Send a request header line to the server.

Expand Down
14 changes: 14 additions & 0 deletions Lib/test/test_httplib.py
Original file line number Diff line number Diff line change
Expand Up @@ -702,6 +702,20 @@ def test_proxy_tunnel_without_status_line(self):
with self.assertRaisesRegexp(socket.error, "Invalid response"):
conn._tunnel()

def test_putrequest_override_validation(self):
"""
It should be possible to override the default validation
behavior in putrequest (bpo-38216).
"""
class UnsafeHTTPConnection(httplib.HTTPConnection):
def _validate_path(self, url):
pass

conn = UnsafeHTTPConnection('example.com')
conn.sock = FakeSocket('')
conn.putrequest('GET', '/\x00')


class OfflineTest(TestCase):
def test_responses(self):
self.assertEqual(httplib.responses[httplib.NOT_FOUND], "Not Found")
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Allow the rare code that wants to send invalid http requests from the
`http.client` library a way to do so. The fixes for bpo-30458 led to
breakage for some projects that were relying on this ability to test their
own behavior in the face of bad requests.