Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 6 additions & 3 deletions docs/content/compliance/_index.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,8 @@ Each test sends a request that violates a specific **MUST** or **MUST NOT** requ
'COMP-ABSOLUTE-FORM',
'COMP-METHOD-CASE','COMP-REQUEST-LINE-TAB',
'COMP-VERSION-MISSING-MINOR','COMP-VERSION-LEADING-ZEROS',
'COMP-VERSION-WHITESPACE','COMP-HTTP12-VERSION',
'COMP-VERSION-WHITESPACE','COMP-VERSION-CASE','COMP-HTTP12-VERSION',
'COMP-LONG-URL-OK','COMP-SPACE-IN-TARGET',
'RFC9112-5.1-OBS-FOLD','RFC9110-5.6.2-SP-BEFORE-COLON',
'RFC9112-5-EMPTY-HEADER-NAME','RFC9112-5-INVALID-HEADER-NAME',
'RFC9112-5-HEADER-NO-COLON',
Expand All @@ -57,12 +58,14 @@ Each test sends a request that violates a specific **MUST** or **MUST NOT** requ
'COMP-CHUNKED-EMPTY','COMP-CHUNKED-NO-FINAL',
'COMP-GET-WITH-CL-BODY','COMP-CHUNKED-EXTENSION',
'COMP-CHUNKED-TRAILER-VALID','COMP-CHUNKED-HEX-UPPERCASE',
'COMP-RANGE-POST'
'COMP-RANGE-POST','COMP-RANGE-INVALID',
'COMP-DUPLICATE-CT','COMP-POST-UNSUPPORTED-CT',
'COMP-ACCEPT-NONSENSE'
]},
{ key: 'methods-upgrade', label: 'Methods & Upgrade', testIds: [
'COMP-METHOD-CONNECT',
'COMP-UNKNOWN-TE-501','COMP-EXPECT-UNKNOWN','COMP-METHOD-TRACE',
'COMP-TRACE-WITH-BODY',
'COMP-TRACE-WITH-BODY','COMP-TRACE-SENSITIVE',
'COMP-UPGRADE-POST','COMP-UPGRADE-MISSING-CONN',
'COMP-UPGRADE-UNKNOWN','COMP-UPGRADE-INVALID-VER','COMP-UPGRADE-HTTP10',
'COMP-CONNECTION-CLOSE','COMP-HTTP10-DEFAULT-CLOSE','COMP-HTTP10-NO-HOST'
Expand Down
46 changes: 46 additions & 0 deletions docs/content/docs/body/post-unsupported-ct.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
---
title: "POST-UNSUPPORTED-CT"
description: "POST-UNSUPPORTED-CT test documentation"
weight: 15
---

| | |
|---|---|
| **Test ID** | `COMP-POST-UNSUPPORTED-CT` |
| **Category** | Compliance |
| **Scored** | No |
| **RFC** | [RFC 9110 §15.5.16](https://www.rfc-editor.org/rfc/rfc9110#section-15.5.16) |
| **RFC Level** | MAY |
| **Expected** | `415` or `2xx` |

## What it sends

A POST request with an unrecognized `Content-Type`.

```http
POST / HTTP/1.1\r\n
Host: localhost:8080\r\n
Content-Length: 5\r\n
Content-Type: application/x-nonsense\r\n
\r\n
hello
```

## What the RFC says

> "The 415 (Unsupported Media Type) status code indicates that the origin server is refusing to service the request because the content is in a format not supported by this method on the target resource." — RFC 9110 §15.5.16

The server is not required to reject unsupported content types — it may choose to accept the body regardless of the declared type.

## Why it matters

A server that validates `Content-Type` and returns `415` for unsupported formats provides better API hygiene, helping clients detect misconfigured requests early. A server that ignores unknown content types and processes the body anyway is also valid behavior — many servers treat the body as opaque bytes regardless of the declared type.

## Verdicts

- **Pass** — Server returns `415` (validates content type) or `2xx` (accepts any type)
- **Warn** — Server returns an unexpected status

## Sources

- [RFC 9110 §15.5.16](https://www.rfc-editor.org/rfc/rfc9110#section-15.5.16)
48 changes: 48 additions & 0 deletions docs/content/docs/body/range-invalid.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
---
title: "RANGE-INVALID"
description: "RANGE-INVALID test documentation"
weight: 14
---

| | |
|---|---|
| **Test ID** | `COMP-RANGE-INVALID` |
| **Category** | Compliance |
| **Scored** | No |
| **RFC** | [RFC 9110 §14.2](https://www.rfc-editor.org/rfc/rfc9110#section-14.2) |
| **RFC Level** | MAY |
| **Expected** | `2xx` (ignore) or `416` |

## What it sends

A GET request with a syntactically invalid `Range` header.

```http
GET / HTTP/1.1\r\n
Host: localhost:8080\r\n
Range: bytes=abc-xyz\r\n
\r\n
```

The range value `abc-xyz` does not match the required integer format.

## What the RFC says

> "A server MAY ignore the Range header field." — RFC 9110 §14.2

> "An origin server MUST ignore a Range header field that contains a range unit it does not understand. A proxy MAY discard a Range header field that contains a range unit it does not understand." — RFC 9110 §14.2

> "A server that supports range requests MAY ignore or reject a Range header field that consists of more than two overlapping ranges, or a set of many small ranges that are not listed in ascending order, since both are indications of either a broken client or a deliberate denial-of-service attack." — RFC 9110 §14.2

## Why it matters

A server that receives an unparseable Range value should either ignore it (serve the full resource with `200`) or reject it with `416 Range Not Satisfiable`. Returning `206 Partial Content` with bogus range values could expose unexpected data or cause client-side parsing errors.

## Verdicts

- **Pass** — Server returns `2xx` (ignoring the invalid range) or `416`
- **Warn** — Server returns an unexpected status

## Sources

- [RFC 9110 §14.2](https://www.rfc-editor.org/rfc/rfc9110#section-14.2)
45 changes: 45 additions & 0 deletions docs/content/docs/headers/accept-nonsense.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
---
title: "ACCEPT-NONSENSE"
description: "ACCEPT-NONSENSE test documentation"
weight: 21
---

| | |
|---|---|
| **Test ID** | `COMP-ACCEPT-NONSENSE` |
| **Category** | Compliance |
| **Scored** | No |
| **RFC** | [RFC 9110 §12.5.1](https://www.rfc-editor.org/rfc/rfc9110#section-12.5.1) |
| **RFC Level** | SHOULD |
| **Expected** | `406` preferred, `2xx` acceptable |

## What it sends

A GET request with an `Accept` header requesting a non-existent media type.

```http
GET / HTTP/1.1\r\n
Host: localhost:8080\r\n
Accept: application/x-nonsense\r\n
\r\n
```

## What the RFC says

> "A request without any Accept header field implies that the user agent will accept any media type in response." — RFC 9110 §12.5.1

> "If the header field is present in a request and none of the available representations for the response have a media type that is listed as acceptable, the origin server can either honor the header field by sending a 406 (Not Acceptable) response or disregard the header field by treating the response as if it is not subject to content negotiation for that request." — RFC 9110 §12.5.1

## Why it matters

Content negotiation allows servers to serve different representations of a resource based on client capabilities. A server that returns `406 Not Acceptable` for unrecognized media types actively enforces content negotiation. A server that ignores the `Accept` header and serves a default representation is also compliant — the RFC explicitly allows both behaviors.

## Verdicts

- **Pass** — Server returns `406 Not Acceptable` (enforces content negotiation)
- **Warn** — Server returns `2xx` (ignores Accept, serves default representation)
- **Fail** — Server returns an unexpected error status

## Sources

- [RFC 9110 §12.5.1](https://www.rfc-editor.org/rfc/rfc9110#section-12.5.1)
54 changes: 54 additions & 0 deletions docs/content/docs/headers/date-format.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
---
title: "DATE-FORMAT"
description: "DATE-FORMAT test documentation"
weight: 20
---

| | |
|---|---|
| **Test ID** | `COMP-DATE-FORMAT` |
| **Category** | Compliance |
| **Scored** | No |
| **RFC** | [RFC 9110 §5.6.7](https://www.rfc-editor.org/rfc/rfc9110#section-5.6.7) |
| **RFC Level** | SHOULD |
| **Expected** | IMF-fixdate format |

## What it does

Sends a standard GET request and checks whether the `Date` response header uses the preferred IMF-fixdate format.

```http
GET / HTTP/1.1\r\n
Host: localhost:8080\r\n
\r\n
```

The test inspects the `Date` header value in the response.

## What the RFC says

> "An HTTP-date value represents time as an instance of Coordinated Universal Time (UTC). The first two formats [IMF-fixdate and rfc850-date] indicate UTC by the three-letter abbreviation for Greenwich Mean Time, 'GMT'... **A recipient that parses a timestamp value in an HTTP field MUST accept all three HTTP-date formats.**" -- RFC 9110 §5.6.7

> "HTTP-date = IMF-fixdate / obs-date" -- RFC 9110 §5.6.7

> "A sender MUST generate timestamps in the IMF-fixdate format." -- RFC 9110 §5.6.7 (quoted from RFC 7231 §7.1.1.1, carried forward)

The preferred format is **IMF-fixdate**:

```
Sun, 06 Nov 1994 08:49:37 GMT
```

## Why it matters

While all three date formats are valid for *recipients* to accept, **senders** (including origin servers) should generate the IMF-fixdate format. Servers using obsolete formats (RFC 850 or asctime) are technically non-conforming senders, though recipients must still parse them.

## Verdicts

- **Pass** -- Date header present and uses IMF-fixdate format
- **Warn** -- Date header missing or uses a non-standard format

## Sources

- [RFC 9110 §5.6.7](https://www.rfc-editor.org/rfc/rfc9110#section-5.6.7)
- [RFC 9110 §6.6.1](https://www.rfc-editor.org/rfc/rfc9110#section-6.6.1)
50 changes: 50 additions & 0 deletions docs/content/docs/headers/duplicate-ct.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
---
title: "DUPLICATE-CT"
description: "DUPLICATE-CT test documentation"
weight: 20
---

| | |
|---|---|
| **Test ID** | `COMP-DUPLICATE-CT` |
| **Category** | Compliance |
| **Scored** | Yes |
| **RFC** | [RFC 9110 §5.3](https://www.rfc-editor.org/rfc/rfc9110#section-5.3) |
| **RFC Level** | SHOULD |
| **Expected** | `400` preferred, `2xx` acceptable |

## What it sends

A POST request with two `Content-Type` headers that have conflicting values.

```http
POST / HTTP/1.1\r\n
Host: localhost:8080\r\n
Content-Length: 5\r\n
Content-Type: text/plain\r\n
Content-Type: text/html\r\n
\r\n
hello
```

## What the RFC says

> "A sender MUST NOT generate multiple header fields with the same field name in a message unless either the entire field value for that header field is defined as a comma-separated list or the header field is a well-known exception." — RFC 9110 §5.3

> "A recipient MAY combine multiple header fields with the same field name into one 'field-name: field-value' pair... by appending each subsequent field value to the combined field value in order, separated by a comma." — RFC 9110 §5.3

`Content-Type` is not a list-based header — it has a single value. Duplicate `Content-Type` headers with different values create ambiguity about which value the server uses.

## Why it matters

When a proxy and origin server disagree on which `Content-Type` to use, it can lead to content-type confusion attacks. An attacker could craft a request that a proxy interprets as `text/plain` while the origin processes as `text/html`, enabling XSS or other injection attacks.

## Verdicts

- **Pass** — Server rejects with `400` or closes the connection
- **Warn** — Server accepts with `2xx` (silently picks one value)
- **Fail** — Server returns an unexpected error status

## Sources

- [RFC 9110 §5.3](https://www.rfc-editor.org/rfc/rfc9110#section-5.3)
48 changes: 48 additions & 0 deletions docs/content/docs/request-line/long-url-ok.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
---
title: "LONG-URL-OK"
description: "LONG-URL-OK test documentation"
weight: 31
---

| | |
|---|---|
| **Test ID** | `COMP-LONG-URL-OK` |
| **Category** | Compliance |
| **Scored** | Yes |
| **RFC** | [RFC 9112 §3](https://www.rfc-editor.org/rfc/rfc9112#section-3) |
| **RFC Level** | SHOULD |
| **Expected** | Any status except `414` |

## What it sends

A GET request with a ~7900-character path (well under 8000 octets total for the request-line).

```http
GET /aaaa...aaa HTTP/1.1\r\n
Host: localhost:8080\r\n
\r\n
```

The path contains 7900 repetitions of `a`.

## What the RFC says

> "A server that receives a request-target longer than any URI it wishes to parse MUST respond with a 414 (URI Too Long) status code." — RFC 9112 §3

> "It is RECOMMENDED that all HTTP senders and recipients support, at a minimum, request-line lengths of 8000 octets." — RFC 9112 §3

## Why it matters

Servers that reject URLs well within the 8000-octet recommendation may break legitimate applications that use long query strings or path parameters. This test verifies the server can handle a request-line just under the recommended minimum.

This is the inverse of `MAL-LONG-URL`, which tests rejection of extremely long URLs (~100KB). Together they verify a server has reasonable upper and lower bounds.

## Verdicts

- **Pass** — Server returns any status other than `414`
- **Fail** — Server returns `414 URI Too Long`
- **Warn** — Server closes the connection without a response

## Sources

- [RFC 9112 §3](https://www.rfc-editor.org/rfc/rfc9112#section-3)
47 changes: 47 additions & 0 deletions docs/content/docs/request-line/space-in-target.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
---
title: "SPACE-IN-TARGET"
description: "SPACE-IN-TARGET test documentation"
weight: 32
---

| | |
|---|---|
| **Test ID** | `COMP-SPACE-IN-TARGET` |
| **Category** | Compliance |
| **Scored** | Yes |
| **RFC** | [RFC 9112 §3.2](https://www.rfc-editor.org/rfc/rfc9112#section-3.2) |
| **RFC Level** | MUST |
| **Expected** | `400` or connection close |

## What it sends

A GET request with an unencoded space inside the request-target.

```http
GET /pa th HTTP/1.1\r\n
Host: localhost:8080\r\n
\r\n
```

The request-target `/pa th` contains a bare space character (0x20) which is not a valid URI character.

## What the RFC says

> "Recipients of an invalid request-line SHOULD respond with either a 400 (Bad Request) error or a 301 (Moved Permanently) redirect with the request-target properly encoded." — RFC 9112 §3.2

> "request-target = origin-form / absolute-form / authority-form / asterisk-form" — RFC 9112 §3.2

The space character is the delimiter between the method, request-target, and HTTP-version in the request-line. An unencoded space in the target makes the request-line ambiguous — the parser sees `GET /pa th HTTP/1.1` as having four tokens instead of three.

## Why it matters

A server that accepts a bare space in the request-target must be performing heuristic parsing to guess where the target ends. This ambiguity is a classic source of request smuggling and cache poisoning vulnerabilities, where different parsers in a chain disagree on the boundaries of the request-line.

## Verdicts

- **Pass** — Server rejects with `400` or closes the connection
- **Fail** — Server accepts the request

## Sources

- [RFC 9112 §3.2](https://www.rfc-editor.org/rfc/rfc9112#section-3.2)
Loading
Loading