Tighten up checks on port ranges and path character sets.

This commit is contained in:
Aldo Cortesi 2013-03-03 21:36:19 +13:00
parent b21a7da142
commit 5a050bb6b2
3 changed files with 37 additions and 13 deletions

View File

@ -1,5 +1,5 @@
import string, urlparse, binascii
import odict
import odict, utils
class HttpError(Exception):
def __init__(self, code, msg):
@ -12,6 +12,22 @@ class HttpError(Exception):
class HttpErrorConnClosed(HttpError): pass
def _is_valid_port(port):
if not 0 <= port <= 65535:
return False
return True
def _is_valid_host(host):
try:
host.decode("idna")
except ValueError:
return False
if "\0" in host:
return None
return True
def parse_url(url):
"""
Returns a (scheme, host, port, path) tuple, or None on error.
@ -42,17 +58,11 @@ def parse_url(url):
path = urlparse.urlunparse(('', '', path, params, query, fragment))
if not path.startswith("/"):
path = "/" + path
try:
host.decode("idna")
except ValueError:
if not _is_valid_host(host):
return None
if "\0" in host:
if not utils.isascii(path):
return None
try:
path.decode("ascii")
except ValueError:
return None
if not 0 <= port <= 65535:
if not _is_valid_port(port):
return None
return scheme, host, port, path
@ -236,6 +246,10 @@ def parse_init_connect(line):
port = int(port)
except ValueError:
return None
if not _is_valid_port(port):
return None
if not _is_valid_host(host):
return None
return host, port, httpversion
@ -260,7 +274,8 @@ def parse_init_http(line):
if not v:
return None
method, url, httpversion = v
if not utils.isascii(url):
return None
if not (url.startswith("/") or url == "*"):
return None
return method, url, httpversion

View File

@ -1,4 +1,12 @@
def isascii(s):
try:
s.decode("ascii")
except ValueError:
return False
return True
def cleanBin(s, fixspacing=False):
"""
Cleans binary data to make it safe to display. If fixspacing is True,

View File

@ -136,6 +136,8 @@ def test_parse_http_protocol():
def test_parse_init_connect():
assert http.parse_init_connect("CONNECT host.com:443 HTTP/1.0")
assert not http.parse_init_connect("CONNECT \0host.com:443 HTTP/1.0")
assert not http.parse_init_connect("CONNECT host.com:444444 HTTP/1.0")
assert not http.parse_init_connect("bogus")
assert not http.parse_init_connect("GET host.com:443 HTTP/1.0")
assert not http.parse_init_connect("CONNECT host.com443 HTTP/1.0")
@ -164,11 +166,10 @@ def test_parse_init_http():
assert m == "GET"
assert u == "/test"
assert httpversion == (1, 1)
assert not http.parse_init_http("invalid")
assert not http.parse_init_http("GET invalid HTTP/1.1")
assert not http.parse_init_http("GET /test foo/1.1")
assert not http.parse_init_http("GET /test\xc0 HTTP/1.1")
class TestReadHeaders:
def _read(self, data, verbatim=False):