@@ -80,11 +80,18 @@ def d(value: bytes | bytearray) -> str:
8080
8181_value_re = re .compile (rb"[\x09\x20-\x7e\x80-\xff]*" )
8282
83- # Content-Length = 1*DIGIT, applied after decoding the header value.
83+ # Regexes for validating status codes and numeric header values.
84+ # Set a lower limit than default_max_str_digits; 1 EB is plenty.
8485
86+ # Content-Length = 1*DIGIT
8587# See https://datatracker.ietf.org/doc/html/rfc9110#section-8.6
8688
87- _content_length_re = re .compile (r"[0-9]+" )
89+ _digits_re = re .compile (rb"[0-9]{1,18}" )
90+
91+ # chunk-size = 1*HEXDIG
92+ # See https://datatracker.ietf.org/doc/html/rfc9112#section-7.1
93+
94+ _hexdigits_re = re .compile (rb"[0-9a-fA-F]{1,15}" )
8895
8996
9097@dataclasses .dataclass
@@ -304,12 +311,11 @@ def parse(
304311 raise ValueError (
305312 f"unsupported protocol; expected HTTP/1.1: { d (status_line )} "
306313 )
307- try :
308- status_code = int (raw_status_code )
309- except ValueError : # invalid literal for int() with base 10
314+ if not _digits_re .fullmatch (raw_status_code ):
310315 raise ValueError (
311316 f"invalid status code; expected integer; got { d (raw_status_code )} "
312- ) from None
317+ )
318+ status_code = int (raw_status_code )
313319 if not 100 <= status_code < 600 :
314320 raise ValueError (
315321 f"invalid status code; expected 100–599; got { d (raw_status_code )} "
@@ -468,10 +474,8 @@ def read_body(
468474 while True :
469475 chunk_size_line = yield from parse_line (read_line , SecurityError )
470476 raw_chunk_size = chunk_size_line .split (b";" , 1 )[0 ]
471- # Set a lower limit than default_max_str_digits; 1 EB is plenty.
472- if len (raw_chunk_size ) > 15 :
473- str_chunk_size = raw_chunk_size .decode (errors = "backslashreplace" )
474- raise SecurityError (f"chunk too large: 0x{ str_chunk_size } bytes" )
477+ if not _hexdigits_re .fullmatch (raw_chunk_size ):
478+ raise SecurityError (f"invalid chunk size: { d (raw_chunk_size )} " )
475479 chunk_size = int (raw_chunk_size , 16 )
476480 if chunk_size == 0 :
477481 break
@@ -487,11 +491,8 @@ def read_body(
487491 return body
488492
489493 elif (raw_content_length := headers .get ("Content-Length" )) is not None :
490- if not _content_length_re .fullmatch (raw_content_length ):
491- raise ValueError (f"invalid content length: { raw_content_length } " )
492- # Set a lower limit than default_max_str_digits; 1 EiB is plenty.
493- if len (raw_content_length ) > 18 :
494- raise SecurityError (f"body too large: { raw_content_length } bytes" )
494+ if not _digits_re .fullmatch (raw_content_length .encode ("iso-8859-1" )):
495+ raise SecurityError (f"invalid content length: { raw_content_length } " )
495496 content_length = int (raw_content_length )
496497 if content_length > MAX_BODY_SIZE :
497498 raise SecurityError (f"body too large: { content_length } bytes" )
0 commit comments