Skip to content

Commit 3ea51c7

Browse files
authored
Allow digits and valid token chars in headers (#134)
1 parent 3a722ed commit 3ea51c7

File tree

4 files changed

+34
-23
lines changed

4 files changed

+34
-23
lines changed

multipart/multipart.py

Lines changed: 15 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -136,13 +136,14 @@ class MultipartState(IntEnum):
136136
LOWER_Z = b"z"[0]
137137
NULL = b"\x00"[0]
138138

139-
140-
# Lower-casing a character is different, because of the difference between
141-
# str on Py2, and bytes on Py3. Same with getting the ordinal value of a byte,
142-
# and joining a list of bytes together.
143-
# These functions abstract that.
144-
def lower_char(c: int) -> int:
145-
return c | 0x20
139+
# Mask for ASCII characters that can be http tokens.
140+
# Per RFC7230 - 3.2.6, this is all alpha-numeric characters
141+
# and these: !#$%&'*+-.^_`|~
142+
TOKEN_CHARS_SET = frozenset(
143+
b"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
144+
b"abcdefghijklmnopqrstuvwxyz"
145+
b"0123456789"
146+
b"!#$%&'*+-.^_`|~")
146147

147148

148149
def ord_char(c: int) -> int:
@@ -1175,12 +1176,8 @@ def data_callback(name: str, remaining: bool = False) -> None:
11751176
# Increment our index in the header.
11761177
index += 1
11771178

1178-
# Do nothing if we encounter a hyphen.
1179-
if c == HYPHEN:
1180-
pass
1181-
11821179
# If we've reached a colon, we're done with this header.
1183-
elif c == COLON:
1180+
if c == COLON:
11841181
# A 0-length header is an error.
11851182
if index == 1:
11861183
msg = "Found 0-length header at %d" % (i,)
@@ -1195,16 +1192,12 @@ def data_callback(name: str, remaining: bool = False) -> None:
11951192
# Move to parsing the header value.
11961193
state = MultipartState.HEADER_VALUE_START
11971194

1198-
else:
1199-
# Lower-case this character, and ensure that it is in fact
1200-
# a valid letter. If not, it's an error.
1201-
cl = lower_char(c)
1202-
if cl < LOWER_A or cl > LOWER_Z:
1203-
msg = "Found non-alphanumeric character %r in " "header at %d" % (c, i)
1204-
self.logger.warning(msg)
1205-
e = MultipartParseError(msg)
1206-
e.offset = i
1207-
raise e
1195+
elif c not in TOKEN_CHARS_SET:
1196+
msg = "Found invalid character %r in header at %d" % (c, i)
1197+
self.logger.warning(msg)
1198+
e = MultipartParseError(msg)
1199+
e.offset = i
1200+
raise e
12081201

12091202
elif state == MultipartState.HEADER_VALUE_START:
12101203
# Skip leading spaces.
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
------WebKitFormBoundaryTkr3kCBQlBe1nrhc
2-
Content-999position: form-data; name="field"
2+
Content-<<<position: form-data; name="field"
33

44
This is a test.
55
------WebKitFormBoundaryTkr3kCBQlBe1nrhc--
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
--b8825ae386be4fdc9644d87e392caad3
2+
Content-Disposition: form-data; filename="secret.txt"; name="files"
3+
Content-Type: text/plain; charset=utf-8
4+
X-Funky-Header-1: bar
5+
abcdefghijklmnopqrstuvwxyz01234: foo
6+
ABCDEFGHIJKLMNOPQRSTUVWXYZ56789: bar
7+
other!#$%&'*+-.^_`|~: baz
8+
Content-Length: 6
9+
10+
aaaaaa
11+
--b8825ae386be4fdc9644d87e392caad3--
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
boundary: b8825ae386be4fdc9644d87e392caad3
2+
expected:
3+
- name: files
4+
type: file
5+
file_name: secret.txt
6+
data: !!binary |
7+
YWFhYWFh

0 commit comments

Comments
 (0)