Skip to content

Commit f550111

Browse files
committed
strict HTTP header field name validation
Do the validation on the original, not the result from unicode case folding. Background: latin-1 0xDF is traditionally uppercased 0x53+0x53 which puts it back in ASCII
1 parent fd67112 commit f550111

File tree

3 files changed

+17
-3
lines changed

3 files changed

+17
-3
lines changed

gunicorn/http/message.py

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -98,12 +98,16 @@ def parse_headers(self, data, from_trailer=False):
9898
raise InvalidHeader(curr)
9999
name, value = curr.split(":", 1)
100100
if self.cfg.strip_header_spaces:
101-
name = name.rstrip(" \t").upper()
102-
else:
103-
name = name.upper()
101+
name = name.rstrip(" \t")
104102
if not TOKEN_RE.fullmatch(name):
105103
raise InvalidHeaderName(name)
106104

105+
# this is still a dangerous place to do this
106+
# but it is more correct than doing it before the pattern match:
107+
# after we entered Unicode wonderland, 8bits could case-shift into ASCII:
108+
# b"\xDF".decode("latin-1").upper().encode("ascii") == b"SS"
109+
name = name.upper()
110+
107111
value = [value.lstrip(" \t")]
108112

109113
# Consume value continuation lines
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
GET /germans.. HTTP/1.1\r\n
2+
Content-Lengthß: 3\r\n
3+
Content-Length: 3\r\n
4+
\r\n
5+
ÄÄÄ

tests/requests/invalid/nonascii_03.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
from gunicorn.config import Config
2+
from gunicorn.http.errors import InvalidHeaderName
3+
4+
cfg = Config()
5+
request = InvalidHeaderName

0 commit comments

Comments
 (0)