Skip to content

Commit d9cb45b

Browse files
authored
Merge pull request #39 from MDA2AV/fix/kestrel
Fix kestrel - HEAD and OPTIONS headers allowed
2 parents 5202d1e + c47a6fb commit d9cb45b

5 files changed

Lines changed: 44 additions & 2 deletions

File tree

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ All notable changes to Http11Probe are documented in this file.
1212
- **Row-click detail popup** — clicking a server row opens a modal showing that server's results for the current table in a vertical layout (Test, Expected, Got, Description) with section and table name in the header
1313
- **Truncation notice** — tooltip and modal now show a `[Truncated]` notice at the top when raw request/response data exceeds the 8,192-byte display limit
1414
- **Filter box** — text input above result tables to filter by server name, language, or test name; supports multiple comma-separated keywords
15+
- **`--verbose` CLI flag** — prints the raw server response below each test result when enabled (`--verbose` or `-v`)
1516

1617
### Changed
1718
- **Horizontal column headers** — test name headers are now displayed horizontally instead of rotated at -55°, improving readability
@@ -31,6 +32,7 @@ All notable changes to Http11Probe are documented in this file.
3132
- **Stronger sticky shadow on mobile** — increased shadow intensity for the pinned server name column
3233
- **Scrollable tooltips** — hover tooltips are now interactive and scrollable for large payloads (removed `pointer-events:none`, increased `max-height` to `60vh`)
3334
- **Larger click modal** — expanded from `max-width:700px` to `90vw` and `max-height` from `80vh` to `85vh` to better accommodate large request/response data
35+
- **Kestrel HEAD/OPTIONS support** — added explicit HEAD and OPTIONS endpoint handlers to ASP.NET Minimal server so smuggling tests evaluate correctly instead of returning 405
3436
- Raw request capture now includes truncation metadata when payload exceeds 8,192 bytes (`TestRunner.cs`)
3537
- Raw response capture now includes truncation metadata when response exceeds 8,192 bytes (`ResponseParser.cs`)
3638

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ dotnet run --project src/Http11Probe.Cli -- --host localhost --port 8080
4848
| `--test` | Run only specific test IDs, case-insensitive (repeatable) | all |
4949
| `--timeout` | Connect and read timeout in seconds per test | `5` |
5050
| `--output` | Write JSON results to file ||
51+
| `--verbose`, `-v` | Print the raw server response for each test | off |
5152

5253
### Examples
5354

src/Http11Probe.Cli/Program.cs

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,14 +19,18 @@
1919

2020
var outputOption = new Option<string?>("--output") { Description = "Write JSON results to this file path" };
2121

22+
var verboseOption = new Option<bool>("--verbose", "-v") { Description = "Print the raw server response for each test" };
23+
verboseOption.DefaultValueFactory = _ => false;
24+
2225
var rootCommand = new RootCommand("Http11Probe — HTTP/1.1 server compliance & hardening tester")
2326
{
2427
hostOption,
2528
portOption,
2629
categoryOption,
2730
testOption,
2831
timeoutOption,
29-
outputOption
32+
outputOption,
33+
verboseOption
3034
};
3135

3236
rootCommand.SetAction(async (parseResult, cancellationToken) =>
@@ -37,6 +41,7 @@
3741
var timeout = parseResult.GetValue(timeoutOption);
3842
var testIds = parseResult.GetValue(testOption);
3943
var outputPath = parseResult.GetValue(outputOption);
44+
var verbose = parseResult.GetValue(verboseOption);
4045

4146
Console.WriteLine($" Http11Probe targeting {host}:{port}");
4247
Console.WriteLine();
@@ -62,7 +67,12 @@
6267
var runner = new TestRunner(options);
6368

6469
ConsoleReporter.PrintHeader();
65-
var report = await runner.RunAsync(testCases, ConsoleReporter.PrintRow);
70+
var report = await runner.RunAsync(testCases, result =>
71+
{
72+
ConsoleReporter.PrintRow(result);
73+
if (verbose)
74+
ConsoleReporter.PrintRawResponse(result);
75+
});
6676
ConsoleReporter.PrintSummary(report);
6777

6878
if (outputPath is not null)

src/Http11Probe.Cli/Reporting/ConsoleReporter.cs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,27 @@ public static void PrintRow(TestResult result)
6262
Console.WriteLine();
6363
}
6464

65+
public static void PrintRawResponse(TestResult result)
66+
{
67+
if (result.Verdict == TestVerdict.Skip)
68+
return;
69+
70+
var raw = result.Response?.RawResponse;
71+
if (raw is null)
72+
{
73+
Console.ForegroundColor = ConsoleColor.DarkGray;
74+
Console.WriteLine(" (no response)");
75+
Console.ResetColor();
76+
return;
77+
}
78+
79+
Console.ForegroundColor = ConsoleColor.DarkGray;
80+
foreach (var line in raw.Split('\n'))
81+
Console.WriteLine($" {line.TrimEnd('\r')}");
82+
Console.ResetColor();
83+
Console.WriteLine();
84+
}
85+
6586
public static void PrintSummary(TestRunReport report)
6687
{
6788
Console.WriteLine(" " + new string('─', 80));

src/Servers/AspNetMinimal/Program.cs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,14 @@
66

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

9+
app.MapMethods("/", ["HEAD"], () => Results.Ok());
10+
11+
app.MapMethods("/", ["OPTIONS"], (HttpContext ctx) =>
12+
{
13+
ctx.Response.Headers["Allow"] = "GET, HEAD, POST, OPTIONS";
14+
return Results.Ok();
15+
});
16+
917
app.MapPost("/", async (HttpContext ctx) =>
1018
{
1119
using var reader = new StreamReader(ctx.Request.Body);

0 commit comments

Comments
 (0)