Skip to content

Commit acfa55c

Browse files
authored
Merge pull request #21 from MDA2AV/fix/adjust-bare-lf
Adjust BARE LF tests to warn 2xx
2 parents fe9680d + 0df4d56 commit acfa55c

3 files changed

Lines changed: 28 additions & 14 deletions

File tree

docs/content/docs/line-endings/bare-lf-header.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ weight: 2
1010
| **Category** | Compliance |
1111
| **RFC** | [RFC 9112 Section 2.2](https://www.rfc-editor.org/rfc/rfc9112#section-2.2) |
1212
| **Requirement** | MAY |
13-
| **Expected** | `400` or close |
13+
| **Expected** | `400` or close (pass), `2xx` (warn) |
1414

1515
## What it sends
1616

@@ -93,12 +93,12 @@ This directly supports why bare LF in headers is a security concern: if the fron
9393

9494
### Scored / Unscored justification
9595

96-
This test is **scored (Pass/Fail)**. Although the RFC requirement level is MAY, Http11Probe enforces strict rejection:
96+
This test is **scored (Pass/Warn)**:
9797

9898
- **Pass** for `400` or connection close --- strict rejection prevents header boundary disagreements between hops.
99-
- **Fail** for `2xx` --- the server accepted bare LF in a header, which introduces a smuggling vector in multi-hop architectures.
99+
- **Warn** for `2xx` --- the server accepted bare LF in a header. This is permitted by the RFC (MAY) but introduces a smuggling vector in multi-hop architectures.
100100

101-
The scoring reflects Http11Probe's security-first philosophy: when the RFC gives discretion (MAY), the tool rewards the stricter posture. Header-level bare LF is arguably more dangerous than request-line bare LF because header boundaries directly control how Content-Length, Transfer-Encoding, and Host are parsed --- all of which are smuggling-critical fields.
101+
The strict posture is rewarded because header-level bare LF is particularly sensitive --- header boundaries directly control how Content-Length, Transfer-Encoding, and Host are parsed, all of which are smuggling-critical fields.
102102

103103
## Sources
104104

docs/content/docs/line-endings/bare-lf-request-line.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ weight: 1
1010
| **Category** | Compliance |
1111
| **RFC** | [RFC 9112 Section 2.2](https://www.rfc-editor.org/rfc/rfc9112#section-2.2) |
1212
| **Requirement** | MAY |
13-
| **Expected** | `400` or close |
13+
| **Expected** | `400` or close (pass), `2xx` (warn) |
1414

1515
## What it sends
1616

@@ -94,12 +94,12 @@ This explains the security motivation for rejecting bare LF: when a front-end pr
9494

9595
### Scored / Unscored justification
9696

97-
This test is **scored (Pass/Fail)**. Although the RFC requirement level is MAY, Http11Probe enforces strict rejection:
97+
This test is **scored (Pass/Warn)**:
9898

9999
- **Pass** for `400` or connection close --- strict rejection eliminates parser-differential attacks.
100-
- **Fail** for `2xx` --- the server accepted bare LF as a line terminator, which introduces a smuggling vector in multi-hop architectures.
100+
- **Warn** for `2xx` --- the server accepted bare LF as a line terminator. This is permitted by the RFC (MAY) but introduces a smuggling vector in multi-hop architectures.
101101

102-
The scoring reflects Http11Probe's security-first philosophy: when the RFC gives discretion (MAY), the tool rewards the stricter posture because bare LF acceptance is a well-known source of parser disagreements that enable request smuggling.
102+
The strict posture is rewarded because bare LF acceptance is a well-known source of parser disagreements that enable request smuggling.
103103

104104
## Sources
105105

src/Http11Probe/TestCases/Suites/ComplianceSuite.cs

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -22,28 +22,42 @@ public static IEnumerable<TestCase> GetTestCases()
2222
yield return new TestCase
2323
{
2424
Id = "RFC9112-2.2-BARE-LF-REQUEST-LINE",
25-
Description = "Bare LF in request line must be rejected",
25+
Description = "Bare LF in request line should be rejected, but MAY be accepted",
2626
Category = TestCategory.Compliance,
2727
RfcReference = "RFC 9112 §2.2",
2828
PayloadFactory = ctx => MakeRequest($"GET / HTTP/1.1\nHost: {ctx.HostHeader}\r\n\r\n"),
2929
Expected = new ExpectedBehavior
3030
{
31-
ExpectedStatus = StatusCodeRange.Exact(400),
32-
AllowConnectionClose = true
31+
Description = "400 or close (pass), 2xx (warn)",
32+
CustomValidator = (response, state) =>
33+
{
34+
if (response is null)
35+
return state == ConnectionState.ClosedByServer ? TestVerdict.Pass : TestVerdict.Fail;
36+
if (response.StatusCode == 400) return TestVerdict.Pass;
37+
if (response.StatusCode is >= 200 and < 300) return TestVerdict.Warn;
38+
return TestVerdict.Fail;
39+
}
3340
}
3441
};
3542

3643
yield return new TestCase
3744
{
3845
Id = "RFC9112-2.2-BARE-LF-HEADER",
39-
Description = "Bare LF in header must be rejected",
46+
Description = "Bare LF in header should be rejected, but MAY be accepted",
4047
Category = TestCategory.Compliance,
4148
RfcReference = "RFC 9112 §2.2",
4249
PayloadFactory = ctx => MakeRequest($"GET / HTTP/1.1\r\nHost: {ctx.HostHeader}\nX-Test: value\r\n\r\n"),
4350
Expected = new ExpectedBehavior
4451
{
45-
ExpectedStatus = StatusCodeRange.Exact(400),
46-
AllowConnectionClose = true
52+
Description = "400 or close (pass), 2xx (warn)",
53+
CustomValidator = (response, state) =>
54+
{
55+
if (response is null)
56+
return state == ConnectionState.ClosedByServer ? TestVerdict.Pass : TestVerdict.Fail;
57+
if (response.StatusCode == 400) return TestVerdict.Pass;
58+
if (response.StatusCode is >= 200 and < 300) return TestVerdict.Warn;
59+
return TestVerdict.Fail;
60+
}
4761
}
4862
};
4963

0 commit comments

Comments
 (0)