Skip to content

fix: reject a message whose Transfer-Encoding value cannot be parsed - #1267

Open
pjfanning wants to merge 1 commit into
apache:mainfrom
pjfanning:reject-unparseable-transfer-encoding
Open

fix: reject a message whose Transfer-Encoding value cannot be parsed#1267
pjfanning wants to merge 1 commit into
apache:mainfrom
pjfanning:reject-unparseable-transfer-encoding

Conversation

@pjfanning

Copy link
Copy Markdown
Member

Motivation

When a modelled header value fails to parse, ModeledHeaderValueParser calls onIllegalHeader — which by default only logs — and degrades the header to a RawHeader:

case HeaderParser.Failure(error) =>
  onIllegalHeader(error.withSummaryPrepended(s"Illegal '$headerName' header")...)
  RawHeader(headerName, trimmedHeaderValue)

For Transfer-Encoding that is unsafe. The degraded header never reaches the case h: Transfer-Encoding arm in parseHeaderLines, so isChunked stays false and — with a Content-Length also present — the message is framed by Content-Length. An upstream that does parse the value (stripping quotes, tolerating trailing junk) frames the same message as chunked. The two then disagree about where the message ends, which is a request-smuggling primitive.

Pekko HTTP already rejects the clear cases: chunked together with a Content-Length, an unsupported coding, and multiple entries. Only the unparseable value was silently tolerated.

Modification

Fail the message when a Transfer-Encoding header arrives as a RawHeader. That can only happen when the modelled parse failed: transfer-encoding is in alwaysParsedHeaders, so it is modelled even when modeled-header-parsing is off, and a well-formed value always reaches the modelled arm. The change is therefore scoped to this one header and does not alter the general illegal-header contract.

Result

A message whose Transfer-Encoding cannot be understood is rejected, rather than framed by a different rule than the sender used.

Tests

  • sbt "http-core/testOnly org.apache.pekko.http.impl.engine.parsing.*" — pass (246 tests). A new test sends Transfer-Encoding: "chunked" alongside a Content-Length and expects a 400. Verified it fails without the change, where the message is accepted and framed by Content-Length.
  • sbt http-core/mimaReportBinaryIssues — pass.
  • Native scalafmt clean.

References

None - rejects a message whose Transfer-Encoding value cannot be parsed

🤖 Generated with Claude Code

Motivation:
When a modelled header value fails to parse, `ModeledHeaderValueParser`
calls `onIllegalHeader` - which by default only logs - and degrades the
header to a `RawHeader`. For `Transfer-Encoding` that is unsafe: the
degraded header never reaches the `case h: Transfer-Encoding` arm in
`parseHeaderLines`, so `isChunked` stays false and, with a
Content-Length also present, the message is framed by Content-Length.
An upstream that does parse the value - stripping quotes, tolerating
trailing junk - frames the same message as chunked. The two disagree
about where the message ends, which is a request smuggling primitive.

Pekko HTTP already rejects the clear cases: `chunked` together with a
Content-Length, an unsupported coding, and multiple entries. Only the
unparseable value was silently tolerated.

Modification:
Fail the message when a `Transfer-Encoding` header arrives as a
`RawHeader`. That can only happen when the modelled parse failed:
`transfer-encoding` is in `alwaysParsedHeaders`, so it is modelled even
when `modeled-header-parsing` is off, and a well-formed value always
reaches the modelled arm.

Result:
A message whose Transfer-Encoding cannot be understood is rejected
rather than framed by a different rule than the sender used.

Tests:
- sbt "http-core/testOnly org.apache.pekko.http.impl.engine.parsing.*" - pass (246 tests); a new test sends `Transfer-Encoding: "chunked"` alongside a Content-Length and expects a 400. Verified it fails without the change, where the message is accepted and framed by Content-Length.
- sbt http-core/mimaReportBinaryIssues - pass

References:
None - rejects a message whose Transfer-Encoding value cannot be parsed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant