diff options
| author | srdusr <[email protected]> | 2026-07-31 16:07:00 +0200 |
|---|---|---|
| committer | srdusr <[email protected]> | 2026-07-31 16:07:00 +0200 |
| commit | 9b630b2cbf9206855534668ebaaaee255cabedd4 (patch) | |
| tree | b4c45860ae3e52eb8909cb868649c07c6c93e1a6 /PLAN.md | |
| parent | 384573a2dc5e3b8e2a7bdfe2ce949f2c52ba2c52 (diff) | |
| download | mitmux-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.md | 73 |
1 files changed, 73 insertions, 0 deletions
@@ -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. |