From b33c1e5cc7086da46b36122aa5645ccee26be69f Mon Sep 17 00:00:00 2001 From: srdusr <99972264+srdusr@users.noreply.github.com> Date: Mon, 16 Feb 2026 11:09:00 +0200 Subject: Structured search filters: status:, source: Closes another top item from the Burp/ZAP/Caido gap research: status- code and MIME/type filtering alongside free text is used constantly in practice (Caido's HTTPQL, Burp's proxy history filter). Scoped to status and source for now - method: already works today via FTS5's own method column (a plain text match on "POST" is effectively exact for a short alphanumeric token), so it didn't need special handling. status_code isn't a text column FTS5 can index, and doesn't benefit from full-text matching anyway (it's a numeric comparison, not a word search), so extractStructured pulls status:/source: tokens out of the query before it reaches FTS5 and turns them into real parameterized SQL predicates against history's typed columns: status:404 (exact), status:>=400 / status:!=200 (comparison operators), status:4xx (also 2xx/3xx/5xx - the shorthand people actually reach for: "show me the errors"), source:repeater/intruder/proxy. Whatever text remains after extraction still goes through the existing FTS5 path, so "admin status:200" correctly ANDs a real full-text match with a real status predicate in one query. When nothing remains (pure "status:4xx"), Search skips the FTS5 join entirely and queries history directly. store_test.go covers the parsing (exact/operator/range/source, combined with free text, and two "looks like it but isn't" cases - status:banana and the malformed 4-digit status:4004 - to confirm they fall through as literal search text instead of being misparsed). Verified live against real varied traffic (status 200/404/500 requests plus a POST with an "admin" body) - status:4xx matched only the 404; status:>=400 matched both 404 and 500; "admin status:200" correctly matched only the POST and excluded the other unrelated 200; source:proxy matched everything captured so far. All against the actual SQL execution path, not just the pure parsing function. --- cmd/mitmux/main.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'cmd') diff --git a/cmd/mitmux/main.go b/cmd/mitmux/main.go index 360b68d..aa659cd 100644 --- a/cmd/mitmux/main.go +++ b/cmd/mitmux/main.go @@ -194,7 +194,7 @@ func newModel(client *ipc.Client, subCh <-chan store.Summary, socketPath string) si := textinput.New() si.Prompt = "/" - si.Placeholder = "search - plain text, or host:example.com / AND / OR / NOT" + si.Placeholder = "search - plain text, host:x, status:404 / status:4xx / status:>=400, source:repeater" rulesCols := []table.Column{ {Title: "On", Width: 3}, @@ -1022,7 +1022,8 @@ func (m *model) helpView() string { "enter view request/response detail", "r open in Repeater", "i open in Intruder", - "/ search (plain text, host:value, AND/OR/NOT)", + "/ search: plain text, host:value, AND/OR/NOT,", + " status:404 / status:4xx / status:>=400, source:repeater", "esc clear active search filter", "m match-and-replace rules", "q quit", -- cgit v1.2.3