Skip to content

Commit fe92d87

Browse files
committed
Go through and re evaluate some tests
1 parent 53be743 commit fe92d87

18 files changed

Lines changed: 314 additions & 41 deletions

File tree

docs/content/docs/_index.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ Reference documentation for every test in Http11Probe, organized by topic. Each
1010

1111
{{< cards >}}
1212
{{< card link="rfc-basics" title="RFC Basics" subtitle="What RFCs are, how to read requirement levels (MUST/SHOULD/MAY), and which RFCs define HTTP/1.1." icon="book-open" >}}
13+
{{< card link="baseline" title="Baseline" subtitle="Sanity request used to confirm the target is reachable before running negative tests." icon="check-circle" >}}
1314
{{< card link="line-endings" title="Line Endings" subtitle="CRLF requirements, bare LF handling, and bare CR rejection per RFC 9112 Section 2.2." icon="code" >}}
1415
{{< card link="request-line" title="Request Line" subtitle="Request-line format, multiple spaces, missing target, fragments, HTTP version validation." icon="terminal" >}}
1516
{{< card link="headers" title="Header Syntax" subtitle="Obs-fold, space before colon, empty names, invalid characters, missing colon." icon="document-text" >}}

docs/content/docs/baseline.md

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
---
2+
title: "BASELINE"
3+
description: "BASELINE test documentation"
4+
weight: 2
5+
---
6+
7+
| | |
8+
|---|---|
9+
| **Test ID** | `COMP-BASELINE` |
10+
| **Category** | Compliance |
11+
| **Expected** | `2xx` |
12+
13+
## What it sends
14+
15+
A well-formed minimal HTTP/1.1 GET request.
16+
17+
```http
18+
GET / HTTP/1.1\r\n
19+
Host: localhost:8080\r\n
20+
\r\n
21+
```
22+
23+
## Why it matters
24+
25+
This is the sanity check for reachability and parser baseline. If this fails, later negative tests are not meaningful.

docs/content/docs/body/chunked-extension.md

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ weight: 10
1010
| **Category** | Compliance |
1111
| **RFC** | [RFC 9112 Section 7.1.1](https://www.rfc-editor.org/rfc/rfc9112#section-7.1.1) |
1212
| **Requirement** | MUST ignore unrecognized extensions |
13-
| **Expected** | `2xx` or `400` |
13+
| **Expected** | `2xx` = Pass, `400` = Warn |
1414

1515
## What it sends
1616

@@ -75,9 +75,11 @@ chunk-ext-val = token / quoted-string
7575
4. However, the RFC also says servers "ought to limit the total length of chunk extensions" and may generate a 4xx response if limits are exceeded. This introduces a legitimate reason for a `400` response.
7676
5. The extension in this test (`ext=value`) is short (9 bytes), so a length-limit rejection would be unreasonable. But the RFC permits it in principle.
7777

78-
### Scored / Unscored justification
78+
### Scoring justification
7979

80-
**Unscored.** The MUST keyword applies to *ignoring unrecognized* extensions, which implies the server should parse and skip them. However, the RFC also explicitly permits servers to reject requests with excessive chunk extensions via a 4xx response. Because the boundary between "acceptable" and "excessive" is left to the server's discretion, there is room for a compliant server to reject even short extensions. The test uses SHOULD accept (`2xx` = Pass, `400` = Warn) to acknowledge that `2xx` is the preferred behavior while `400` is not a clear violation.
80+
This test is **scored** because the payload uses a short, syntactically valid chunk extension. For this input, RFC 9112 §7.1.1 says recipients MUST ignore unrecognized extensions and continue processing.
81+
`2xx` is Pass.
82+
`400` is Warn (strict behavior seen in the wild, but not the preferred RFC behavior for this specific payload).
8183

8284
### Edge cases
8385

docs/content/docs/request-line/absolute-form.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ weight: 9
1010
| **Category** | Compliance |
1111
| **RFC** | [RFC 9112 Section 3.2.2](https://www.rfc-editor.org/rfc/rfc9112#section-3.2.2) |
1212
| **Requirement** | MUST accept (server) |
13-
| **Expected** | `400` or `2xx` |
13+
| **Expected** | `2xx` = Pass, `400` = Warn (unscored) |
1414

1515
## What it sends
1616

@@ -35,8 +35,8 @@ Although the RFC says servers MUST accept absolute-form, in practice most non-pr
3535

3636
## Why it matters
3737

38-
**Pass:** Server rejects with `400` (common origin-server behavior).
39-
**Warn:** Server accepts with `2xx` (RFC-compliant, accepts absolute-form).
38+
**Pass:** Server accepts with `2xx` (RFC-compliant).
39+
**Warn:** Server rejects with `400` (common in practice, but non-compliant with MUST accept).
4040

4141
## Deep Analysis
4242

@@ -74,7 +74,7 @@ The `absolute-form` production requires a complete `absolute-URI` as defined in
7474

7575
### Scoring Justification
7676

77-
**Unscored.** Although the RFC uses "MUST accept," this requirement primarily targets proxy servers. An origin server that rejects absolute-form (returning `400`) is technically non-compliant but is not creating a security vulnerability -- it is simply refusing a request format it was not designed to handle. Both `400` and `2xx` are treated as acceptable outcomes.
77+
**Unscored.** RFC 9112 uses a server-side MUST to accept absolute-form. In practice, many origin stacks still reject it. To preserve interoperability visibility without hard-failing broad classes of servers, this test is unscored: `2xx` is Pass and `400` is Warn.
7878

7979
### Edge Cases
8080

docs/content/docs/smuggling/_index.md

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,17 +87,28 @@ For these, `400` is the strict/safe response and `2xx` is RFC-compliant. Http11P
8787
{{< card link="chunk-spill" title="CHUNK-SPILL" subtitle="Chunk declares size 5 but sends 7 bytes." >}}
8888
{{< card link="chunk-lf-term" title="CHUNK-LF-TERM" subtitle="Bare LF as chunk data terminator." >}}
8989
{{< card link="chunk-ext-ctrl" title="CHUNK-EXT-CTRL" subtitle="NUL byte in chunk extension." >}}
90+
{{< card link="chunk-ext-cr" title="CHUNK-EXT-CR" subtitle="Bare CR inside chunk extension metadata." >}}
9091
{{< card link="chunk-lf-trailer" title="CHUNK-LF-TRAILER" subtitle="Bare LF in trailer section termination." >}}
9192
{{< card link="te-identity" title="TE-IDENTITY" subtitle="Transfer-Encoding: identity (deprecated) with CL." >}}
93+
{{< card link="te-vtab" title="TE-VTAB" subtitle="Vertical tab before chunked token." >}}
94+
{{< card link="te-formfeed" title="TE-FORMFEED" subtitle="Form-feed before chunked token." >}}
95+
{{< card link="te-null" title="TE-NULL" subtitle="NUL byte appended to chunked token." >}}
9296
{{< card link="chunk-negative" title="CHUNK-NEGATIVE" subtitle="Negative chunk size (-1)." >}}
97+
{{< card link="chunk-bare-cr-term" title="CHUNK-BARE-CR-TERM" subtitle="Bare CR as chunk size line terminator." >}}
98+
{{< card link="cl-underscore" title="CL-UNDERSCORE" subtitle="Content-Length with underscore digit separator (1_0)." >}}
99+
{{< card link="cl-negative-zero" title="CL-NEGATIVE-ZERO" subtitle="Content-Length: -0 — not valid 1*DIGIT." >}}
100+
{{< card link="cl-double-zero" title="CL-DOUBLE-ZERO" subtitle="Content-Length: 00 — leading zero ambiguity." >}}
101+
{{< card link="cl-leading-zeros-octal" title="CL-LEADING-ZEROS-OCTAL" subtitle="Content-Length: 0200 — octal vs decimal disagreement." >}}
102+
{{< card link="te-obs-fold" title="TE-OBS-FOLD" subtitle="Transfer-Encoding with obs-fold line wrapping." >}}
103+
{{< card link="te-trailing-comma" title="TE-TRAILING-COMMA" subtitle="Transfer-Encoding: chunked, — trailing comma." >}}
104+
{{< card link="multiple-host-comma" title="MULTIPLE-HOST-COMMA" subtitle="Host with comma-separated values." >}}
93105
{{< /cards >}}
94106

95107
### Unscored
96108

97109
{{< cards >}}
98110
{{< card link="cl-trailing-space" title="CL-TRAILING-SPACE" subtitle="Trailing space in CL value. OWS trimming is valid." >}}
99111
{{< card link="cl-extra-leading-sp" title="CL-EXTRA-LEADING-SP" subtitle="Extra space after CL colon. OWS is valid." >}}
100-
{{< card link="header-injection" title="HEADER-INJECTION" subtitle="CRLF injection in header value." >}}
101112
{{< card link="te-double-chunked" title="TE-DOUBLE-CHUNKED" subtitle="Duplicate 'chunked' TE with CL." >}}
102113
{{< card link="te-case-mismatch" title="TE-CASE-MISMATCH" subtitle="'Chunked' vs 'chunked'. Case is valid per RFC." >}}
103114
{{< card link="transfer-encoding-underscore" title="TRANSFER_ENCODING" subtitle="Underscore instead of hyphen in header name." >}}
@@ -107,6 +118,10 @@ For these, `400` is the strict/safe response and `2xx` is RFC-compliant. Http11P
107118
{{< card link="trailer-cl" title="TRAILER-CL" subtitle="Content-Length in chunked trailers (prohibited)." >}}
108119
{{< card link="trailer-te" title="TRAILER-TE" subtitle="Transfer-Encoding in chunked trailers (prohibited)." >}}
109120
{{< card link="trailer-host" title="TRAILER-HOST" subtitle="Host header in chunked trailers (must not route)." >}}
121+
{{< card link="trailer-auth" title="TRAILER-AUTH" subtitle="Authorization in chunked trailers (prohibited)." >}}
122+
{{< card link="trailer-content-type" title="TRAILER-CONTENT-TYPE" subtitle="Content-Type in chunked trailers (prohibited)." >}}
110123
{{< card link="head-cl-body" title="HEAD-CL-BODY" subtitle="HEAD with Content-Length and body." >}}
111124
{{< card link="options-cl-body" title="OPTIONS-CL-BODY" subtitle="OPTIONS with Content-Length and body." >}}
125+
{{< card link="te-tab-before-value" title="TE-TAB-BEFORE-VALUE" subtitle="Tab as OWS before Transfer-Encoding value." >}}
126+
{{< card link="absolute-uri-host-mismatch" title="ABSOLUTE-URI-HOST-MISMATCH" subtitle="Absolute-form URI with different Host header." >}}
112127
{{< /cards >}}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
---
2+
title: "CHUNK-EXT-CR"
3+
description: "CHUNK-EXT-CR test documentation"
4+
weight: 51
5+
---
6+
7+
| | |
8+
|---|---|
9+
| **Test ID** | `SMUG-CHUNK-EXT-CR` |
10+
| **Category** | Smuggling |
11+
| **RFC** | [RFC 9112 §7.1.1](https://www.rfc-editor.org/rfc/rfc9112#section-7.1.1), [RFC 9112 §2.2](https://www.rfc-editor.org/rfc/rfc9112#section-2.2) |
12+
| **Requirement** | MUST reject malformed chunk line |
13+
| **Expected** | `400` or close |
14+
15+
## What it sends
16+
17+
A chunk-size line where a bare CR appears inside the extension area, not as a valid `CRLF` terminator.
18+
19+
```http
20+
POST / HTTP/1.1\r\n
21+
Host: localhost:8080\r\n
22+
Transfer-Encoding: chunked\r\n
23+
\r\n
24+
5;a\rX\r\n
25+
hello\r\n
26+
0\r\n
27+
\r\n
28+
```
29+
30+
## Why it matters
31+
32+
Differential handling of bare CR in framing metadata can produce parser disagreement across hops and create desync risk.
33+
34+
## Sources
35+
36+
- [RFC 9112 §2.2](https://www.rfc-editor.org/rfc/rfc9112#section-2.2)
37+
- [RFC 9112 §7.1.1](https://www.rfc-editor.org/rfc/rfc9112#section-7.1.1)
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
---
2+
title: "TE-FORMFEED"
3+
description: "TE-FORMFEED test documentation"
4+
weight: 53
5+
---
6+
7+
| | |
8+
|---|---|
9+
| **Test ID** | `SMUG-TE-FORMFEED` |
10+
| **Category** | Smuggling |
11+
| **RFC** | [RFC 9110 §5.5](https://www.rfc-editor.org/rfc/rfc9110#section-5.5), [RFC 9112 §6.1](https://www.rfc-editor.org/rfc/rfc9112#section-6.1) |
12+
| **Requirement** | MUST reject invalid transfer-coding token |
13+
| **Expected** | `400` or close |
14+
15+
## What it sends
16+
17+
`Transfer-Encoding: <FF>chunked` with `Content-Length` present.
18+
19+
```http
20+
POST / HTTP/1.1\r\n
21+
Host: localhost:8080\r\n
22+
Transfer-Encoding: \x0cchunked\r\n
23+
Content-Length: 5\r\n
24+
\r\n
25+
hello
26+
```
27+
28+
## Why it matters
29+
30+
Form-feed control characters in TE values are an obfuscation vector that can trigger parser disagreement in proxy chains.
31+
32+
## Sources
33+
34+
- [RFC 9110 §5.5](https://www.rfc-editor.org/rfc/rfc9110#section-5.5)
35+
- [RFC 9112 §6.1](https://www.rfc-editor.org/rfc/rfc9112#section-6.1)

docs/content/docs/smuggling/te-identity.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ weight: 30
1010
| **Category** | Smuggling |
1111
| **RFC** | [RFC 9112 §6.1](https://www.rfc-editor.org/rfc/rfc9112#section-6.1) |
1212
| **Requirement** | MUST reject |
13-
| **Expected** | `400` or close |
13+
| **Expected** | `400`/`501` or close |
1414

1515
## What it sends
1616

@@ -76,7 +76,7 @@ The token `identity` is syntactically valid per the ABNF (it consists entirely o
7676

7777
This test is **scored** (MUST reject). Although the SHOULD in RFC 9112 section 6.1 for unrecognized transfer codings is not a MUST, the combined presence of Transfer-Encoding and Content-Length triggers the MUST-level requirement in section 6.1 to close the connection. The server cannot safely process `Transfer-Encoding: identity` because it is not a recognized coding, and the dual-header scenario mandates connection closure at minimum.
7878

79-
- **Pass (400 or close):** The server correctly rejects the unknown transfer coding or closes the connection per the dual-header rule.
79+
- **Pass (400/501 or close):** The server rejects the unknown transfer coding or closes the connection per the dual-header rule.
8080
- **Fail (2xx):** The server accepted a request with an unrecognized transfer coding and conflicting Content-Length, violating the connection-closure requirement.
8181

8282
### Smuggling Attack Scenarios
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
---
2+
title: "TE-NULL"
3+
description: "TE-NULL test documentation"
4+
weight: 54
5+
---
6+
7+
| | |
8+
|---|---|
9+
| **Test ID** | `SMUG-TE-NULL` |
10+
| **Category** | Smuggling |
11+
| **RFC** | [RFC 9110 §5.5](https://www.rfc-editor.org/rfc/rfc9110#section-5.5), [RFC 9112 §6.1](https://www.rfc-editor.org/rfc/rfc9112#section-6.1) |
12+
| **Requirement** | MUST reject malformed field value |
13+
| **Expected** | `400` or close |
14+
15+
## What it sends
16+
17+
`Transfer-Encoding: chunked<NUL>` with `Content-Length` present.
18+
19+
```http
20+
POST / HTTP/1.1\r\n
21+
Host: localhost:8080\r\n
22+
Transfer-Encoding: chunked\x00\r\n
23+
Content-Length: 5\r\n
24+
\r\n
25+
hello
26+
```
27+
28+
## Why it matters
29+
30+
NUL handling differences (truncate vs reject) are a classic parser differential that can destabilize message framing.
31+
32+
## Sources
33+
34+
- [RFC 9110 §5.5](https://www.rfc-editor.org/rfc/rfc9110#section-5.5)
35+
- [RFC 9112 §6.1](https://www.rfc-editor.org/rfc/rfc9112#section-6.1)

docs/content/docs/smuggling/te-obs-fold.md

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@ weight: 50
99
| **Test ID** | `SMUG-TE-OBS-FOLD` |
1010
| **Category** | Smuggling |
1111
| **RFC** | [RFC 9112 §5.2](https://www.rfc-editor.org/rfc/rfc9112#section-5.2) |
12-
| **Requirement** | MUST |
13-
| **Expected** | `400` |
12+
| **Requirement** | MUST reject or unfold obs-fold |
13+
| **Expected** | `400`, or `2xx` with connection close |
1414

1515
## What it sends
1616

@@ -37,7 +37,7 @@ When obs-fold is used on the Transfer-Encoding header with Content-Length also p
3737

3838
## Why it matters
3939

40-
This is a high-confidence smuggling vector. The obs-fold mechanism was deprecated precisely because of parser disagreements. When applied to Transfer-Encoding — the header that determines message framing — it creates a situation where one parser uses chunked encoding and another uses Content-Length, enabling request smuggling. The RFC requires rejection (MUST), and no `AllowConnectionClose` alternative is acceptable because the server must actively reject the malformed header rather than simply closing the connection.
40+
This is a high-confidence smuggling vector. The obs-fold mechanism was deprecated precisely because of parser disagreements. When applied to Transfer-Encoding, one parser can unfold to `chunked` while another ignores it and falls back to Content-Length.
4141

4242
## Deep Analysis
4343

@@ -71,10 +71,11 @@ The `obs-fold` rule (obsolete line folding) allows a field value to be continued
7171

7272
### Scored / Unscored Justification
7373

74-
This test is **scored** (MUST reject with `400`). RFC 9112 section 5.2 provides a MUST-level requirement for servers receiving obs-fold. While the RFC allows two options (reject or unfold), this test expects strict `400` rejection because the obs-fold is applied to the Transfer-Encoding header -- the header that determines message framing. Allowing an unfolded interpretation when Content-Length is also present would require the server to then handle the CL/TE dual-header scenario, adding further complexity and risk. No `AllowConnectionClose` alternative is acceptable because the server must actively reject the malformed header.
74+
This test is **scored**. RFC 9112 §5.2 gives two compliant server behaviors: reject with `400`, or replace obs-fold with SP and continue. If unfolded, the message still carries both TE and CL, so RFC 9112 §6.1 requires closing the connection after responding.
7575

76-
- **Pass (400):** The server correctly rejects the obs-fold per the MUST requirement.
77-
- **Fail (2xx or close):** The server either silently accepted the folded header or merely closed the connection without the required `400` response.
76+
- **Pass:** `400`.
77+
- **Warn:** `2xx` with connection close (accepted unfold path).
78+
- **Fail:** `2xx` without connection close.
7879

7980
### Smuggling Attack Scenarios
8081

0 commit comments

Comments
 (0)