srdusr
aboutsummaryrefslogtreecommitdiffstats
path: root/PLAN.md
diff options
context:
space:
mode:
authorsrdusr <[email protected]>2026-07-31 16:07:00 +0200
committersrdusr <[email protected]>2026-07-31 16:07:00 +0200
commit9b630b2cbf9206855534668ebaaaee255cabedd4 (patch)
treeb4c45860ae3e52eb8909cb868649c07c6c93e1a6 /PLAN.md
parent384573a2dc5e3b8e2a7bdfe2ce949f2c52ba2c52 (diff)
downloadmitmux-9b630b2cbf9206855534668ebaaaee255cabedd4.tar.gz
mitmux-9b630b2cbf9206855534668ebaaaee255cabedd4.zip
History sorting, status color-coding, and JSON syntax highlighting
Filtering already existed (FTS5 search plus status:/source:/flagged: and column filters). Sorting and highlighting didn't, at all. Sorting: o/O cycle and reverse the sort column (status, size, time taken, method, host, path) applied client-side on top of whatever order List/Search already returned. refreshTable() reorders m.entries itself, not just what's rendered - every "act on the selected row" key handler indexes m.table.Cursor() straight into m.entries with no indirection, so keeping the two in identical order sidesteps an entire class of "highlighted row and actual target silently disagree" bugs rather than updating every one of those call sites. JSON syntax highlighting (cmd/mitmux/jsoncolor.go): walks the token stream via json.Decoder.Token() with an explicit stack, not recursive calls, so depth is bounded by memory rather than Go's call stack for adversarial nesting. Every string re-escaped via json.Marshal before writing, which is also why the colorized output is deliberately never run through sanitizeControl afterward (unlike every other raw-text view here): JSON's own encoding rules already forbid a literal control character in a string, so re-marshaling neutralizes one as a side effect of producing valid JSON - running sanitizeControl on top would instead corrupt the ANSI codes this adds. Status-code color-coding (2xx green through 5xx red) does not live in the history/Intruder tables, despite an initial attempt to put it there. Confirmed live: bubbles/table v1.0.0 (the newest available) fits cell text to its column width via go-runewidth's Truncate, which has no ANSI awareness - it counts every character of a color escape sequence as real display width. Coloring the Status cell silently deleted the status text from the row; the width-fitting truncation cut into the escape sequence itself. styledStatus is used once instead, in detailView's title, a plain string rendered whole with no width constraint. Verified live in tmux against a running daemon: ascending/descending sort by status across six real entries: pretty-printed JSON confirmed correctly colored and indented via raw ANSI capture, not just eyeballed; the detail title's status color confirmed red for a 500 entry the same way; the request tab (never JSON) confirmed unaffected.
Diffstat (limited to 'PLAN.md')
-rw-r--r--PLAN.md73
1 files changed, 73 insertions, 0 deletions
diff --git a/PLAN.md b/PLAN.md
index 2b94b81..e95622c 100644
--- a/PLAN.md
+++ b/PLAN.md
@@ -674,3 +674,76 @@ after use - never part of the build) confirmed the captured messages
in `ws_messages` via `ipc.Client.ListWSMessages`, matching what the
test client actually sent and received, correctly attributed to
direction and opcode.
+
+## Sorting and highlighting
+
+Filtering already existed (FTS5 search plus `status:`/`source:`/
+`flagged:` and column filters - see Search above); sorting and
+highlighting didn't, at all, until now.
+
+Sorting is client-side, applied on top of whatever order List/Search
+already returned (newest-first, or FTS5 relevance) - `o` cycles the
+sort column (the default "captured" - no override - then status,
+size, time taken, method, host, path), `O` reverses it.
+`refreshTable()` doesn't just re-render sorted rows, it reorders
+`m.entries` itself to match: every "act on the selected row" key
+handler (enter/r/i/f/c/x, and the mouse equivalents) reads
+`m.table.Cursor()` and indexes straight into `m.entries` at that
+position, with no indirection layer. If the table's rendered order and
+`m.entries`'s order ever diverged, the highlighted row and the entry
+an action actually targets would silently disagree. Keeping them
+identical sidesteps that whole bug class rather than updating every
+one of those call sites to go through a lookup.
+
+Highlighting has two parts, and they needed genuinely different
+solutions because they render through different paths:
+
+- **JSON syntax highlighting** (`cmd/mitmux/jsoncolor.go`): walks the
+ token stream via `json.Decoder.Token()` with an explicit stack
+ rather than recursive calls, so depth is bounded by available memory
+ rather than Go's call stack for deeply nested attacker-controlled
+ JSON. Every string value and object key gets re-escaped via
+ `json.Marshal` before being written - which is also the entire
+ safety argument for *not* running the colorized output through
+ `sanitizeControl` afterward the way every other raw-text view in
+ this codebase does: JSON's own encoding rules forbid a literal
+ control character (ESC included) in a string, so re-marshaling one
+ neutralizes it as a side effect of just producing valid JSON text.
+ Running the already-colored output through `sanitizeControl`
+ afterward would instead corrupt the ANSI codes this function adds -
+ it treats ESC like any other control byte, correctly, for content
+ that hasn't been through this treatment. `prettyResponse` sanitizes
+ the header/status block (still server-controlled, still raw text)
+ separately from the colorized body for exactly this reason. This
+ path renders into a `viewport`, a plain scrolling text pane with no
+ fixed-width cell model, so ANSI content survives untouched.
+- **Status-code color-coding** (2xx green through 5xx red, Burp/
+ Caido's own convention) does *not* live in the history or Intruder-
+ results tables, despite an initial attempt to put it there. Confirmed
+ live, not guessed: `bubbles/table` v1.0.0 - the newest version
+ available, there is no newer one to upgrade to - fits cell text to
+ its column width via `go-runewidth`'s `Truncate`, which has zero ANSI
+ awareness; it counts every visible character of a
+ `"\x1b[38;5;42m"` escape sequence as real display width. Coloring the
+ Status cell didn't misalign the table, it silently deleted the status
+ text from the row entirely - the width-fitting truncation cut into
+ the escape sequence itself. `styledStatus` exists but is deliberately
+ unused inside any `table.Row`; it's used once, in `detailView`'s
+ title, which is a plain string lipgloss-renders whole with no width
+ constraint - nesting one nested `Render()` call inside another works
+ correctly there (confirmed live via raw escape-code inspection), at
+ the cost of one trailing space losing the outer title's bold/color
+ after the inner reset code, placed at the very end of the string
+ specifically to keep that cost minimal.
+
+Verified live in tmux against a running daemon with six real captured
+entries spanning 200/302/404/500 responses: `o` sorted ascending by
+status (200, 200, 200, 302, 404, 500), `O` reversed it; the detail
+view's title rendered the status code in the correct color per class
+(confirmed red for the 500 entry via raw escape-code capture, not just
+visually); pretty-printing a real JSON response produced correctly
+colored, correctly indented output - keys blue, string values green,
+numbers orange, booleans/null magenta, punctuation gray, confirmed
+against the raw captured ANSI codes, not just eyeballed - and the
+request tab (never JSON, never pretty-printed) rendered as plain
+unstyled text, unaffected.