Skip to content

Commit 5202d1e

Browse files
committed
Multiple UI/UX improvements
1 parent fb4dda3 commit 5202d1e

10 files changed

Lines changed: 109 additions & 22 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ All notable changes to Http11Probe are documented in this file.
1111
- **Collapsible sub-groups** — group headers in result tables are now clickable to collapse/expand, with a chevron indicator and a "Collapse All / Expand All" toggle button
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
14+
- **Filter box** — text input above result tables to filter by server name, language, or test name; supports multiple comma-separated keywords
1415

1516
### Changed
1617
- **Horizontal column headers** — test name headers are now displayed horizontally instead of rotated at -55°, improving readability

docs/content/compliance/_index.md

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,12 @@ These tests validate that HTTP/1.1 servers correctly implement the protocol requ
1010

1111
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.
1212

13-
{{< callout type="info" >}}
14-
Click a **server name** to view its Dockerfile and source code. Click a **row** to expand all results for that server. Click a **result cell** to see the full HTTP request and response.
15-
{{< /callout >}}
13+
<style>h1.hx\:mt-2{display:none}.probe-hint{background:#ddf4ff;border:1px solid #54aeff;border-radius:6px;padding:10px 14px;font-size:13px;color:#0969da;font-weight:500}html.dark .probe-hint{background:#1c2333;border-color:#1f6feb;color:#58a6ff}</style>
14+
<div style="display:grid;grid-template-columns:repeat(3,1fr);gap:10px;margin-bottom:16px;">
15+
<div class="probe-hint"><strong style="font-size:14px;">Server Name</strong><br>Click to view Dockerfile and source code</div>
16+
<div class="probe-hint"><strong style="font-size:14px;">Table Row</strong><br>Click to expand all results for that server</div>
17+
<div class="probe-hint"><strong style="font-size:14px;">Result Cell</strong><br>Click to see the full HTTP request and response</div>
18+
</div>
1619

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

docs/content/docs/_index.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ sidebar:
99
Reference documentation for every test in Http11Probe, organized by topic. Each page explains the RFC requirement, what the test sends, what response is expected, and why it matters.
1010

1111
{{< cards >}}
12+
{{< card link="http-overview" title="Understanding HTTP" subtitle="What HTTP is, how HTTP/1.1 works at the wire level, its history from 0.9 to 3, and alternatives." icon="globe-alt" >}}
1213
{{< 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" >}}
1314
{{< card link="baseline" title="Baseline" subtitle="Sanity request used to confirm the target is reachable before running negative tests." icon="check-circle" >}}
1415
{{< 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" >}}

docs/content/docs/baseline.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
---
22
title: "BASELINE"
33
description: "BASELINE test documentation"
4-
weight: 2
4+
weight: 3
55
---
66

77
| | |

docs/content/docs/http-overview/_index.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
---
22
title: Understanding HTTP
33
description: "What HTTP is, how HTTP/1.1 works in depth, its history from 0.9 to 3, and alternatives."
4-
weight: 2
4+
weight: 1
55
sidebar:
66
open: false
77
---

docs/content/docs/rfc-basics.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
---
22
title: RFC Basics
33
description: "RFC Basics — Http11Probe documentation"
4-
weight: 1
4+
weight: 2
55
---
66

77
## What is an RFC?

docs/content/malformed-input/_index.md

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,12 @@ These tests send pathological, oversized, or completely invalid payloads to veri
1010

1111
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.
1212

13-
{{< callout type="info" >}}
14-
Click a **server name** to view its Dockerfile and source code. Click a **row** to expand all results for that server. Click a **result cell** to see the full HTTP request and response.
15-
{{< /callout >}}
13+
<style>h1.hx\:mt-2{display:none}.probe-hint{background:#ddf4ff;border:1px solid #54aeff;border-radius:6px;padding:10px 14px;font-size:13px;color:#0969da;font-weight:500}html.dark .probe-hint{background:#1c2333;border-color:#1f6feb;color:#58a6ff}</style>
14+
<div style="display:grid;grid-template-columns:repeat(3,1fr);gap:10px;margin-bottom:16px;">
15+
<div class="probe-hint"><strong style="font-size:14px;">Server Name</strong><br>Click to view Dockerfile and source code</div>
16+
<div class="probe-hint"><strong style="font-size:14px;">Table Row</strong><br>Click to expand all results for that server</div>
17+
<div class="probe-hint"><strong style="font-size:14px;">Result Cell</strong><br>Click to see the full HTTP request and response</div>
18+
</div>
1619

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

docs/content/normalization/_index.md

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,12 @@ Header normalization tests check what happens when a server *accepts* a malforme
1616
Some tests are **unscored** (marked with `*`). These cover behaviors like case normalization that are RFC-compliant and common across servers.
1717
{{< /callout >}}
1818

19-
{{< callout type="info" >}}
20-
Click a **server name** to view its Dockerfile and source code. Click a **row** to expand all results for that server. Click a **result cell** to see the full HTTP request and response.
21-
{{< /callout >}}
19+
<style>h1.hx\:mt-2{display:none}.probe-hint{background:#ddf4ff;border:1px solid #54aeff;border-radius:6px;padding:10px 14px;font-size:13px;color:#0969da;font-weight:500}html.dark .probe-hint{background:#1c2333;border-color:#1f6feb;color:#58a6ff}</style>
20+
<div style="display:grid;grid-template-columns:repeat(3,1fr);gap:10px;margin-bottom:16px;">
21+
<div class="probe-hint"><strong style="font-size:14px;">Server Name</strong><br>Click to view Dockerfile and source code</div>
22+
<div class="probe-hint"><strong style="font-size:14px;">Table Row</strong><br>Click to expand all results for that server</div>
23+
<div class="probe-hint"><strong style="font-size:14px;">Result Cell</strong><br>Click to see the full HTTP request and response</div>
24+
</div>
2225

2326
<div id="lang-filter"></div>
2427
<div id="table-normalization"><p><em>Loading...</em></p></div>

docs/content/smuggling/_index.md

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,12 @@ These tests send requests with ambiguous framing &mdash; conflicting `Content-Le
1414
Some tests are **unscored** (marked with `*`). These send payloads where the RFC permits multiple valid interpretations &mdash; for example, OWS trimming or case-insensitive TE matching. A `2xx` response is RFC-compliant but shown as a warning since stricter rejection is preferred.
1515
{{< /callout >}}
1616

17-
{{< callout type="info" >}}
18-
Click a **server name** to view its Dockerfile and source code. Click a **row** to expand all results for that server. Click a **result cell** to see the full HTTP request and response.
19-
{{< /callout >}}
17+
<style>h1.hx\:mt-2{display:none}.probe-hint{background:#ddf4ff;border:1px solid #54aeff;border-radius:6px;padding:10px 14px;font-size:13px;color:#0969da;font-weight:500}html.dark .probe-hint{background:#1c2333;border-color:#1f6feb;color:#58a6ff}</style>
18+
<div style="display:grid;grid-template-columns:repeat(3,1fr);gap:10px;margin-bottom:16px;">
19+
<div class="probe-hint"><strong style="font-size:14px;">Server Name</strong><br>Click to view Dockerfile and source code</div>
20+
<div class="probe-hint"><strong style="font-size:14px;">Table Row</strong><br>Click to expand all results for that server</div>
21+
<div class="probe-hint"><strong style="font-size:14px;">Result Cell</strong><br>Click to see the full HTTP request and response</div>
22+
</div>
2023

2124
<div id="lang-filter"></div>
2225
<div id="table-smuggling"><p><em>Loading...</em></p></div>

docs/static/probe/render.js

Lines changed: 80 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,15 @@ window.ProbeRender = (function () {
111111
// Touch-friendly buttons
112112
+ '@media(pointer:coarse){.probe-lang-btn,.probe-cat-btn,.probe-toggle-all{padding:8px 16px;font-size:13px;min-height:36px}}'
113113
// Stronger sticky shadow on mobile
114-
+ '@media(max-width:640px){.probe-table .probe-sticky-col{box-shadow:3px 0 6px rgba(0,0,0,0.1)}html.dark .probe-table .probe-sticky-col{box-shadow:3px 0 6px rgba(0,0,0,0.3)}}';
114+
+ '@media(max-width:640px){.probe-table .probe-sticky-col{box-shadow:3px 0 6px rgba(0,0,0,0.1)}html.dark .probe-table .probe-sticky-col{box-shadow:3px 0 6px rgba(0,0,0,0.3)}}'
115+
// Filter input
116+
+ '.probe-filter-wrap{margin-bottom:8px;display:flex;align-items:center;gap:8px}'
117+
+ '.probe-filter-input{width:100%;max-width:340px;padding:6px 10px;font-size:13px;border:1px solid #d0d7de;border-radius:6px;background:#fff;color:#24292f;outline:none;transition:border-color 0.15s,box-shadow 0.15s}'
118+
+ '.probe-filter-input:focus{border-color:#0969da;box-shadow:0 0 0 3px rgba(9,105,218,0.15)}'
119+
+ '.probe-filter-input::placeholder{color:#8b949e}'
120+
+ 'html.dark .probe-filter-input{background:#161b22;color:#c9d1d9;border-color:#30363d}'
121+
+ 'html.dark .probe-filter-input:focus{border-color:#1f6feb;box-shadow:0 0 0 3px rgba(31,111,235,0.2)}'
122+
+ '.probe-filter-count{font-size:12px;color:#656d76;white-space:nowrap}';
115123
var style = document.createElement('style');
116124
style.textContent = css;
117125
document.head.appendChild(style);
@@ -578,7 +586,7 @@ window.ProbeRender = (function () {
578586
var opacity = isUnscored ? 'opacity:0.55;' : '';
579587
var sepCls = i === unscoredStart ? ' probe-unscored-sep' : '';
580588
var url = testUrl(tid);
581-
t += '<th class="' + sepCls + '" style="padding:6px 8px;vertical-align:bottom;white-space:nowrap;' + opacity + '">';
589+
t += '<th data-test-label="' + escapeAttr(shortLabels[i]) + '" class="' + sepCls + '" style="padding:6px 8px;vertical-align:bottom;white-space:nowrap;' + opacity + '">';
582590
if (url) {
583591
t += '<a href="' + url + '" style="font-size:10.5px;font-weight:600;letter-spacing:0.3px;color:inherit;text-decoration:none;" title="' + escapeAttr(first.description) + '">' + shortLabels[i];
584592
} else {
@@ -598,16 +606,16 @@ window.ProbeRender = (function () {
598606
var isUnscored = first.scored === false;
599607
var opacity = isUnscored ? 'opacity:0.55;' : '';
600608
var sepCls = i === unscoredStart ? ' probe-unscored-sep' : '';
601-
t += '<td class="' + sepCls + '" style="text-align:center;padding:3px 4px;' + opacity + '">' + pill(EXPECT_BG, first.expected.replace(/ or close/g, '/\u2715').replace(/\//g, '/\u200B')) + '</td>';
609+
t += '<td data-test-label="' + escapeAttr(shortLabels[i]) + '" class="' + sepCls + '" style="text-align:center;padding:3px 4px;' + opacity + '">' + pill(EXPECT_BG, first.expected.replace(/ or close/g, '/\u2715').replace(/\//g, '/\u200B')) + '</td>';
602610
});
603611
t += '</tr>';
604612

605613
// Server rows
606614
var serverLangs = {};
607615
if (ctx.servers) ctx.servers.forEach(function (sv) { serverLangs[sv.name] = sv.language; });
608616
names.forEach(function (n) {
609-
t += '<tr class="probe-server-row" data-server="' + escapeAttr(n) + '">';
610617
var lang = serverLangs[n];
618+
t += '<tr class="probe-server-row" data-server="' + escapeAttr(n) + '" data-language="' + escapeAttr(lang || '') + '">';
611619
var langSuffix = lang ? ' <span class="probe-lang-suffix" style="font-weight:400;color:#656d76;font-size:10px;">(' + lang + ')</span>' : '';
612620
var srvUrl = serverUrl(n);
613621
var srvName = srvUrl
@@ -620,10 +628,10 @@ window.ProbeRender = (function () {
620628
var opacity = isUnscored ? 'opacity:0.55;' : '';
621629
var sepCls = i === unscoredStart ? ' probe-unscored-sep' : '';
622630
if (!r) {
623-
t += '<td class="' + sepCls + '" style="text-align:center;padding:3px 4px;' + opacity + '">' + pill(SKIP_BG, '\u2014') + '</td>';
631+
t += '<td data-test-label="' + escapeAttr(shortLabels[i]) + '" class="' + sepCls + '" style="text-align:center;padding:3px 4px;' + opacity + '">' + pill(SKIP_BG, '\u2014') + '</td>';
624632
return;
625633
}
626-
t += '<td class="' + sepCls + '" style="text-align:center;padding:3px 4px;' + opacity + '">' + pill(verdictBg(r.verdict), r.got, r.rawResponse, r.behavioralNote, r.rawRequest) + '</td>';
634+
t += '<td data-test-label="' + escapeAttr(shortLabels[i]) + '" class="' + sepCls + '" style="text-align:center;padding:3px 4px;' + opacity + '">' + pill(verdictBg(r.verdict), r.got, r.rawResponse, r.behavioralNote, r.rawRequest) + '</td>';
627635
});
628636
t += '</tr>';
629637
});
@@ -814,7 +822,8 @@ window.ProbeRender = (function () {
814822
allGroups.push({ key: 'other', label: 'Other', testIds: ungrouped });
815823
}
816824

817-
var html = '<button class="probe-toggle-all" data-target="' + targetId + '">Collapse All</button>';
825+
var html = '<div class="probe-filter-wrap"><input class="probe-filter-input" type="text" placeholder="Filter by server or test name (comma-separated)\u2026"><span class="probe-filter-count"></span></div>';
826+
html += '<button class="probe-toggle-all" data-target="' + targetId + '">Collapse All</button>';
818827
allGroups.forEach(function (g) {
819828
var divId = targetId + '-' + g.key;
820829
html += '<h3 class="probe-group-header" data-group="' + divId + '">'
@@ -828,6 +837,70 @@ window.ProbeRender = (function () {
828837
renderTable(divId, categoryKey, ctx, g.testIds, catLabel + ' \u2014 ' + g.label);
829838
});
830839
wireCollapsible(el, targetId);
840+
841+
// Filter handler
842+
var filterInput = el.querySelector('.probe-filter-input');
843+
var filterCount = el.querySelector('.probe-filter-count');
844+
if (filterInput) {
845+
function matchesAny(text, keywords) {
846+
for (var k = 0; k < keywords.length; k++) {
847+
if (text.indexOf(keywords[k]) !== -1) return true;
848+
}
849+
return false;
850+
}
851+
852+
filterInput.addEventListener('input', function () {
853+
var raw = filterInput.value.toLowerCase();
854+
var keywords = raw.split(',').map(function (s) { return s.trim(); }).filter(Boolean);
855+
var rows = el.querySelectorAll('.probe-server-row');
856+
var allCols = el.querySelectorAll('[data-test-label]');
857+
var thCols = el.querySelectorAll('thead [data-test-label]');
858+
859+
if (keywords.length === 0) {
860+
rows.forEach(function (r) { r.style.display = ''; });
861+
allCols.forEach(function (c) { c.style.display = ''; });
862+
if (filterCount) filterCount.textContent = '';
863+
return;
864+
}
865+
866+
// Count matching servers
867+
var serverMatches = 0;
868+
rows.forEach(function (r) {
869+
var name = (r.getAttribute('data-server') || '').toLowerCase();
870+
var lang = (r.getAttribute('data-language') || '').toLowerCase();
871+
if (matchesAny(name, keywords) || matchesAny(lang, keywords)) serverMatches++;
872+
});
873+
874+
// Count matching test columns
875+
var colMatchSet = {};
876+
thCols.forEach(function (th) {
877+
var label = th.getAttribute('data-test-label').toLowerCase();
878+
if (matchesAny(label, keywords)) colMatchSet[th.getAttribute('data-test-label')] = true;
879+
});
880+
var colMatches = Object.keys(colMatchSet).length;
881+
882+
// Apply row filter (skip if no server matches — show all)
883+
rows.forEach(function (r) {
884+
var name = (r.getAttribute('data-server') || '').toLowerCase();
885+
var lang = (r.getAttribute('data-language') || '').toLowerCase();
886+
r.style.display = (serverMatches === 0 || matchesAny(name, keywords) || matchesAny(lang, keywords)) ? '' : 'none';
887+
});
888+
889+
// Apply column filter (skip if no column matches — show all)
890+
allCols.forEach(function (c) {
891+
var label = c.getAttribute('data-test-label');
892+
c.style.display = (colMatches === 0 || colMatchSet[label]) ? '' : 'none';
893+
});
894+
895+
// Update count label
896+
if (filterCount) {
897+
var parts = [];
898+
if (serverMatches > 0) parts.push(serverMatches + ' server' + (serverMatches !== 1 ? 's' : ''));
899+
if (colMatches > 0) parts.push(colMatches + ' test' + (colMatches !== 1 ? 's' : ''));
900+
filterCount.textContent = parts.length > 0 ? parts.join(', ') : 'No matches';
901+
}
902+
});
903+
}
831904
}
832905

833906
// ── Language filter ────────────────────────────────────────────

0 commit comments

Comments
 (0)