h2 currently accepts request header blocks that contain multiple content-length fields with conflicting values. In src/h2/stream.py (line 1367), _initialize_content_length() uses the first content-length it sees and returns immediately, so later conflicting values remain in the emitted headers but are not considered for body length validation.
This can create parser disagreement for applications, proxies, or protocol translators layered on top of h2: h2 may accept a message based on the first value while downstream code may use the last value, combine values, or reject the request. That shape is request-smuggling-prone.
fix:
Track all content-length values in _initialize_content_length(). If more than one value is present and all values are identical, accept that value. If any value differs, raise ProtocolError. Add inbound tests for duplicate matching and duplicate conflicting content-length fields.
happy to open a pr to fix it
h2 currently accepts request header blocks that contain multiple content-length fields with conflicting values. In src/h2/stream.py (line 1367), _initialize_content_length() uses the first content-length it sees and returns immediately, so later conflicting values remain in the emitted headers but are not considered for body length validation.
This can create parser disagreement for applications, proxies, or protocol translators layered on top of h2: h2 may accept a message based on the first value while downstream code may use the last value, combine values, or reject the request. That shape is request-smuggling-prone.
fix:
Track all content-length values in _initialize_content_length(). If more than one value is present and all values are identical, accept that value. If any value differs, raise ProtocolError. Add inbound tests for duplicate matching and duplicate conflicting content-length fields.
happy to open a pr to fix it