From d7d630386c066caa9538e6ad2ed0cbc1ab61a29c Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 17 Aug 2026 00:16:18 +0000 Subject: [PATCH] Add search box to CrowdSec Active bans table Filters by IP/range, scenario, ASN, carrier name, or country in a single free-text field -- asked for so a phone's current IP or its network/carrier name can be searched directly instead of scanning the full ban list by eye. --- services/security-dashboard.sh | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/services/security-dashboard.sh b/services/security-dashboard.sh index 46a1061..8344df4 100644 --- a/services/security-dashboard.sh +++ b/services/security-dashboard.sh @@ -3999,6 +3999,7 @@ INDEX_HTML = """

Active bans

+
@@ -5060,8 +5061,20 @@ function decSortValue(d, key) { } } +// Matches against every column shown in the table (IP/range, scenario, +// carrier name/ASN, country, origin) rather than just the IP, since "search +// by network/carrier" was asked for explicitly -- a phone's ISP/mobile +// carrier name is often more memorable than its current IP. +function decMatchesSearch(d, term) { + if (!term) return true; + const haystack = [d.value, d.scenario, d.as_number, d.as_name, d.country, d.origin] + .filter(Boolean).join(" ").toLowerCase(); + return haystack.includes(term); +} + function renderDecisions() { - let rows = lastDecisions.slice(); + const term = (document.getElementById("dec-search").value || "").trim().toLowerCase(); + let rows = lastDecisions.filter(d => decMatchesSearch(d, term)); if (decSort.key) { rows.sort((a, b) => { const av = decSortValue(a, decSort.key), bv = decSortValue(b, decSort.key); @@ -5087,7 +5100,7 @@ function renderDecisions() { ${d.as_number ? `` : ""} - `).join("") || ""; + `).join("") || ``; } document.querySelectorAll("#dec-table th.sortable").forEach(th => { @@ -5099,6 +5112,8 @@ document.querySelectorAll("#dec-table th.sortable").forEach(th => { }); }); +document.getElementById("dec-search").addEventListener("input", renderDecisions); + async function loadDecisions() { const res = await fetch("/api/decisions"); lastDecisions = await res.json();
IP/Range
No active bans.
${term ? "No bans match that search." : "No active bans."}