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
10 changes: 9 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,9 @@
##
## Get latest from https://github.com/github/gitignore/blob/main/Dotnet.gitignore

# Rider
# Rider / VS
.idea/
*.DotSettings.user

# Build results
[Dd]ebug/
Expand Down Expand Up @@ -63,5 +64,12 @@ docs/resources/
# Probe data (generated by CI, pushed to latest-results branch)
docs/static/probe/data.js

# Python
__pycache__/

# Node
node_modules/
package-lock.json

# Probe results (local testing)
probe-*.json
94 changes: 94 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
# Changelog

All notable changes to Http11Probe are documented in this file.

## [Unreleased]

### Added
- **Server configuration pages** — per-server docs pages showing Dockerfile, source code, and config files for all 36 tested servers (`docs/content/servers/`)
- **Clickable server names** — server names in the probe results table and summary bar chart now link to their configuration page

## [2026-02-12]

### Added
- **Request/response detail tooltips** — hover over a result pill to see the raw response; click to open a modal with both the raw request and response (#27)
- Repository cleanup — removed clutter files (probe-glyph.json, pycache, package-lock.json, DotSettings.user)

### Fixed
- BARE-LF tests (RFC 9112 §2.2) adjusted to warn on 2xx instead of fail, matching RFC SHOULD-level requirement (#21)

### Removed
- Proxy compliance tests removed from the suite (#20)

## [2026-02-11]

### Added
- POST endpoint for Kestrel (ASP.NET Minimal) server (#13)
- POST endpoint for Quarkus server (#14)
- POST endpoint for Spring Boot server (#16)
- POST endpoint for Express server (#17)

### Fixed
- H2O server now allows POST commands (#19)
- Flask server routing and default port (#11)
- SimpleW server POST handling and version update (#5)

## [2026-02-09]

### Added
- SimpleW server contributed by stratdev3 (#2)

### Fixed
- Glyph server — reset request state on each new connection (#3)
- In-development frameworks now filtered from results (#4)
- SimpleW removed from blacklisted servers

## [2026-02-08]

### Added
- **30 new tests** — body/content handling, chunked TE attack vectors, and additional compliance/smuggling tests (46 → 80 → 110+)
- **7 new servers** — Actix, Ntex, Bun, H2O, NetCoreServer, Sisk, Watson
- **6 more servers** — GenHTTP, SimpleW, EmbedIO, Puma, PHP, Deno, and others (total: 36)
- **Deep analysis docs** — verified RFC evidence and ABNF grammar added to all glossary pages
- **Exact HTTP request code blocks** in all glossary pages
- **Category filter** — filter probe results by Compliance, Smuggling, or Malformed Input
- **Language filter** — filter servers by programming language
- **Sub-tables** — result tables split into logical groups within each category
- **Unscored tests** — separate bucket for RFC-compliant reference tests, shown with reduced opacity and asterisk
- **CLI improvements** — `--test` filter, `--help`, docs links in output, selected test display
- **Summary bar chart** — ranked bars replacing summary badges, with pass/warn/fail/unscored segments
- **Scrollbar styling** — themed scrollbars for probe result tables
- **Custom favicon** — shield icon for browser tab
- **Docs logo** — minimal shield outline

### Fixed
- Summary fail count derivation so pass + warn + fail = total
- Unscored double-counting in summary statistics
- Sort order: rank by scored pass + scored warn only
- Puma Dockerfile: install build-essential for nio4r native extension
- Deno Dockerfile: use `latest` tag instead of nonexistent `:2`
- FRAGMENT-IN-TARGET re-scored as strict (implicit grammar prohibition)
- Nancy and Nginx failing to start in CI
- All servers bound to `0.0.0.0` for Docker reachability

### Removed
- Redundant SMUG-HEADER-INJECTION test (covered by other smuggling tests)
- Nancy server removed from probe (no probe.json)

## [2026-02-07]

### Added
- **Initial release** — extracted from Glyph11 into standalone Http11Probe repository
- 12 standalone test servers dockerized with Docker Compose
- Sequential probe workflow — one server at a time on port 8080
- CI probe workflow (`.github/workflows/probe.yml`) with STRICT expectations dictionary
- Hugo + Hextra documentation site with glossary, per-test docs, and probe results pages
- Separate pages for Compliance, Smuggling, Malformed Input categories
- Landing page with platform framing and contributor onboarding
- "Add a Framework" documentation page

### Fixed
- Docker image tags lowercased as required
- Git worktree/orphan branch creation for latest-results
- GlyphServer: replaced manual buffer with PipeReader, fixed closing without response on oversized requests
- Pingora build: added cmake and g++ to build stage
3 changes: 0 additions & 3 deletions Http11Probe.sln.DotSettings.user

This file was deleted.

4 changes: 4 additions & 0 deletions docs/content/compliance/_index.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@ These tests validate that HTTP/1.1 servers correctly implement the protocol requ

Each test sends a request that violates a specific **MUST** or **MUST NOT** requirement from the RFCs. A compliant server should reject these with a `400 Bad Request` (or close the connection). Accepting the request silently means the server is non-compliant and potentially vulnerable to downstream attacks.

{{< callout type="info" >}}
Click a **server name** to view its Dockerfile and source code. Click a **result cell** to see the full HTTP request and response.
{{< /callout >}}

<div id="lang-filter"></div>
<div id="table-compliance"><p><em>Loading...</em></p></div>

Expand Down
4 changes: 4 additions & 0 deletions docs/content/malformed-input/_index.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@ These tests send pathological, oversized, or completely invalid payloads to veri

A well-implemented server should respond with `400 Bad Request`, `414 URI Too Long`, or `431 Request Header Fields Too Large` depending on the violation, or simply close the connection.

{{< callout type="info" >}}
Click a **server name** to view its Dockerfile and source code. Click a **result cell** to see the full HTTP request and response.
{{< /callout >}}

<div id="lang-filter"></div>
<div id="table-malformed"><p><em>Loading...</em></p></div>

Expand Down
8 changes: 4 additions & 4 deletions docs/content/probe-results/_index.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@ HTTP/1.1 compliance comparison across frameworks. Each test sends a specific mal

## Summary

{{< callout type="info" >}}
These results are from CI runs (`ubuntu-latest`). Click a **server name** to view its Dockerfile and source code. Click on the **Compliance**, **Smuggling**, or **Malformed Input** tabs above for detailed results per category, where you can click any **result cell** to see the full HTTP request and response.
{{< /callout >}}

<div id="lang-filter" style="margin-bottom:6px;"></div>
<div id="cat-filter" style="margin-bottom:16px;"></div>
<div id="probe-summary"><p><em>Loading probe data...</em></p></div>
Expand All @@ -24,10 +28,6 @@ HTTP/1.1 compliance comparison across frameworks. Each test sends a specific mal

**Unscored** — tests marked with `*` in the detail tables. These cover RFC language that uses "MAY" or permits multiple valid behaviors, so there is no single correct answer to score against. They are still run and displayed for visibility, but do not count toward the pass/fail score.

{{< callout type="info" >}}
These results are from CI runs (`ubuntu-latest`). Click on the **Compliance**, **Smuggling**, or **Malformed Input** tabs above for detailed results per category.
{{< /callout >}}

<script src="/Http11Probe/probe/data.js"></script>
<script src="/Http11Probe/probe/render.js"></script>
<script>
Expand Down
9 changes: 9 additions & 0 deletions docs/content/servers/_index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
title: "Servers"
toc: false
breadcrumbs: false
sidebar:
open: false
---

Configuration and source code for each server tested by Http11Probe.
58 changes: 58 additions & 0 deletions docs/content/servers/actix.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
---
title: "Actix"
toc: false
breadcrumbs: false
---

**Language:** Rust · [View source on GitHub](https://github.com/MDA2AV/Http11Probe/tree/main/src/Servers/ActixServer)

## Dockerfile

```dockerfile
FROM rust:1-slim AS build
WORKDIR /src

# Cache dependencies with dummy main
COPY src/Servers/ActixServer/Cargo.toml .
RUN mkdir src && echo "fn main() {}" > src/main.rs && cargo build --release && rm -rf src target/release/.fingerprint/actix-server-*

COPY src/Servers/ActixServer/src/ src/
RUN cargo build --release

FROM debian:bookworm-slim
COPY --from=build /src/target/release/actix-server /usr/local/bin/
ENTRYPOINT ["actix-server", "8080"]
```

## Source — `src/main.rs`

```rust
use actix_web::{web, App, HttpServer, HttpRequest, HttpResponse};

async fn handler(req: HttpRequest, body: web::Bytes) -> HttpResponse {
if req.method() == actix_web::http::Method::POST {
HttpResponse::Ok()
.content_type("text/plain")
.body(body)
} else {
HttpResponse::Ok()
.content_type("text/plain")
.body("OK")
}
}

#[actix_web::main]
async fn main() -> std::io::Result<()> {
let port: u16 = std::env::args()
.nth(1)
.and_then(|s| s.parse().ok())
.unwrap_or(8080);

HttpServer::new(|| {
App::new().default_service(web::to(handler))
})
.bind(("0.0.0.0", port))?
.run()
.await
}
```
37 changes: 37 additions & 0 deletions docs/content/servers/apache.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
---
title: "Apache"
toc: false
breadcrumbs: false
---

**Language:** C · [View source on GitHub](https://github.com/MDA2AV/Http11Probe/tree/main/src/Servers/ApacheServer)

## Dockerfile

```dockerfile
FROM httpd:2.4

COPY src/Servers/ApacheServer/httpd-probe.conf /usr/local/apache2/conf/httpd.conf
RUN echo "OK" > /usr/local/apache2/htdocs/index.html
```

## Source — `httpd-probe.conf`

```apacheconf
ServerRoot "/usr/local/apache2"
Listen 8080

LoadModule mpm_event_module modules/mod_mpm_event.so
LoadModule dir_module modules/mod_dir.so
LoadModule unixd_module modules/mod_unixd.so
LoadModule authz_core_module modules/mod_authz_core.so

ErrorLog /proc/self/fd/2
LogLevel warn

DocumentRoot "/usr/local/apache2/htdocs"

<Directory "/usr/local/apache2/htdocs">
Require all granted
</Directory>
```
44 changes: 44 additions & 0 deletions docs/content/servers/aspnet-minimal.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
---
title: "Kestrel"
toc: false
breadcrumbs: false
---

**Language:** C# · [View source on GitHub](https://github.com/MDA2AV/Http11Probe/tree/main/src/Servers/AspNetMinimal)

## Dockerfile

```dockerfile
FROM mcr.microsoft.com/dotnet/sdk:10.0 AS build
WORKDIR /src
COPY Directory.Build.props .
COPY src/Servers/AspNetMinimal/ src/Servers/AspNetMinimal/
RUN dotnet restore src/Servers/AspNetMinimal/AspNetMinimal.csproj
RUN dotnet publish src/Servers/AspNetMinimal/AspNetMinimal.csproj -c Release -o /app --no-restore

FROM mcr.microsoft.com/dotnet/aspnet:10.0
WORKDIR /app
COPY --from=build /app .
ENTRYPOINT ["dotnet", "AspNetMinimal.dll"]
```

## Source — `Program.cs`

```csharp
var builder = WebApplication.CreateBuilder(args);

builder.WebHost.UseUrls("http://+:8080");

var app = builder.Build();

app.MapGet("/", () => "OK");

app.MapPost("/", async (HttpContext ctx) =>
{
using var reader = new StreamReader(ctx.Request.Body);
var body = await reader.ReadToEndAsync();
return Results.Text(body);
});

app.Run();
```
36 changes: 36 additions & 0 deletions docs/content/servers/bun.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
---
title: "Bun"
toc: false
breadcrumbs: false
---

**Language:** JavaScript · [View source on GitHub](https://github.com/MDA2AV/Http11Probe/tree/main/src/Servers/BunServer)

## Dockerfile

```dockerfile
FROM oven/bun:1-slim
WORKDIR /app
COPY src/Servers/BunServer/server.ts .
ENTRYPOINT ["bun", "run", "server.ts", "8080"]
```

## Source — `server.ts`

```typescript
const port = parseInt(Bun.argv[2] || "8080", 10);

Bun.serve({
port,
hostname: "0.0.0.0",
async fetch(req) {
if (req.method === "POST") {
const body = await req.text();
return new Response(body);
}
return new Response("OK");
},
});

console.log(`Bun listening on 127.0.0.1:${port}`);
```
22 changes: 22 additions & 0 deletions docs/content/servers/caddy.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
---
title: "Caddy"
toc: false
breadcrumbs: false
---

**Language:** Go · [View source on GitHub](https://github.com/MDA2AV/Http11Probe/tree/main/src/Servers/CaddyServer)

## Dockerfile

```dockerfile
FROM caddy:2
COPY src/Servers/CaddyServer/Caddyfile /etc/caddy/Caddyfile
```

## Source — `Caddyfile`

```text
:8080 {
respond "OK" 200
}
```
Loading
Loading