CL-PLUS-SIGN
| Test ID | RFC9112-6.1-CL-PLUS-SIGN |
| Category | Compliance |
| RFC | RFC 9110 Section 8.6, RFC 9112 Section 6.3 |
| Requirement | MUST |
| Expected | 400 or close |
What it sends
A request with a plus sign in the Content-Length value: Content-Length: +5.
POST / HTTP/1.1\r\n
Host: localhost:8080\r\n
Content-Length: +5\r\n
\r\n
helloWhat the RFC says
“Content-Length = 1*DIGIT” – RFC 9110 Section 8.6
The + character is not in the DIGIT set (%x30-39), so +5 does not match 1*DIGIT.
“If a message is received without Transfer-Encoding and with an invalid Content-Length header field, then the message framing is invalid and the recipient MUST treat it as an unrecoverable error.” – RFC 9112 Section 6.3
“If the unrecoverable error is in a request message, the server MUST respond with a 400 (Bad Request) status code and then close the connection.” – RFC 9112 Section 6.3
Why it matters
Many programming languages’ integer parsers accept leading + signs (e.g., parseInt("+42") returns 42 in JavaScript). A server that blindly passes Content-Length through such a parser may accept this value while another server in the chain rejects it — creating a framing disagreement.
Deep Analysis
Relevant ABNF Grammar
Content-Length = 1*DIGIT
DIGIT = %x30-39 ; 0-9The + character (0x2B) is not in the DIGIT range (%x30-39). The ABNF is unambiguous: Content-Length must begin with a digit, not a sign character.
RFC Evidence
RFC 9110 Section 8.6 defines the grammar:
“Content-Length = 1*DIGIT” – RFC 9110 Section 8.6
RFC 9112 Section 6.3 classifies invalid Content-Length as an unrecoverable error:
“If a message is received without Transfer-Encoding and with an invalid Content-Length header field, then the message framing is invalid and the recipient MUST treat it as an unrecoverable error, unless the field value can be successfully parsed as a comma-separated list, all values in the list are valid, and all values in the list are the same.” – RFC 9112 Section 6.3
RFC 9112 Section 6.3 mandates the server response:
“If the unrecoverable error is in a request message, the server MUST respond with a 400 (Bad Request) status code and then close the connection.” – RFC 9112 Section 6.3
Chain of Reasoning
- The test sends
Content-Length: +5. The+character (ASCII 0x2B) is not a DIGIT (%x30-39). - The value
+5does not match1*DIGITbecause the first character is not a digit. This makes the Content-Length invalid. - The subtle danger is that many programming language standard libraries accept leading
+in integer parsing: JavaScript’sparseInt("+5")returns5, Python’sint("+5")returns5, and C#’sint.Parse("+5")returns5. - If the server’s parser accepts
+5as5but a downstream proxy’s parser rejects it (or vice versa), the two disagree on the message body length. This framing disagreement is the fundamental precondition for request smuggling. - The RFC deliberately chose
1*DIGITrather than a more permissive integer syntax precisely to prevent this class of parsing divergence.
Scoring Justification
Scored (MUST). The + character violates the 1*DIGIT grammar, making this an invalid Content-Length. RFC 9112 Section 6.3 mandates 400 followed by connection close. Both 400 and connection close are acceptable test outcomes. A server that parses +5 as 5 and processes the request normally is non-compliant and vulnerable to smuggling via framing disagreement.