diff options
| author | srdusr <[email protected]> | 2026-08-25 15:58:00 +0200 |
|---|---|---|
| committer | srdusr <[email protected]> | 2026-08-25 15:58:00 +0200 |
| commit | 9e94bcbc939afd38b45f9ef42e1b1666fafd8d45 (patch) | |
| tree | 88a36226ee332c74e2dba98a9568a3a09ad074a8 /PLAN.md | |
| parent | dde73a349a68f942a5bd89b27ac980d7973148e0 (diff) | |
| download | mitmux-9e94bcbc939afd38b45f9ef42e1b1666fafd8d45.tar.gz mitmux-9e94bcbc939afd38b45f9ef42e1b1666fafd8d45.zip | |
Wire-format JSON tag consistency fix, and the first real plugin
Writing PLUGINS.md as an authoritative external spec surfaced a real,
pre-existing bug: store.Summary/Entry/EntryTag/WSMessage, rules.Rule,
scope.Rule, and clientcert.Cert had no JSON struct tags at all, so Go's
default marshaling serialized them PascalCase ("ID", "StartedAt")
while the rest of the protocol (EntryDetail's own fields, every
Request/Response wrapper field) uses snake_case. Confirmed live against
a real daemon before touching anything: a raw socket "list" request
came back with "ID"/"StartedAt"/"StatusCode", exactly the mismatch
suspected. Nothing outside this repo's own Go code consumes this wire
format yet, so this was a free, purely additive fix rather than
something to work around - every affected struct now tags snake_case
consistently.
plugins/authcheck is the first real plugin: an Autorize-style
authorization checker. For every proxied request carrying an
Authorization or Cookie header, resends it with that header stripped
and compares status classes - a resend that still succeeds where the
original did too is a likely missing-function-level-access-control
bug, tagged authcheck:bypass with structured detail. Deliberately
speaks the wire protocol directly (its own local request/response/
summary/entryDetail structs mirroring the real ones field-for-field,
not imported from internal/ipc) rather than taking the shortcut a Go
plugin could - proof the documented protocol is actually sufficient on
its own, since that's all a non-Go plugin author has to work with.
Verified live end to end: a real daemon, a real Python origin with one
endpoint that looks like it enforces auth but doesn't (vulnerable by
design) and one that actually does (the control case) - the broken
endpoint was correctly tagged, the secure one correctly left alone, no
false positive, confirmed both via the stored tag data directly and
visually in the TUI (tmux, real keystrokes): the Tags column badge, T's
tag list, and the tag detail view's JSON-colorized data (ANSI-verified,
not eyeballed) all showing the plugin's actual findings.
Diffstat (limited to 'PLAN.md')
| -rw-r--r-- | PLAN.md | 71 |
1 files changed, 67 insertions, 4 deletions
@@ -864,7 +864,70 @@ because it came from a plugin rather than raw traffic. core-engine change to how requests get dispatched, not something a plugin protocol should be shaped around. -Next: Phase 1's plugins themselves (Autorize- and Param-Miner- -equivalents first, since they validate the simple subscribe-act-tag -path before anything gets built on top of it), then the live-RPC -protocol addition for JWT Editor/SAML Raider. +### Wire-format consistency fix (found while building the first plugin) + +Writing PLUGINS.md as an authoritative external spec surfaced a real, +pre-existing inconsistency: `store.Summary`/`Entry`/`EntryTag`/ +`WSMessage`, `rules.Rule`, `scope.Rule`, and `clientcert.Cert` had no +JSON struct tags at all, so Go's default marshaling serialized them as +PascalCase (`"ID"`, `"StartedAt"`) while the rest of the protocol +(`EntryDetail`'s own fields, every `Request`/`Response` wrapper field) +uses snake_case. Confirmed live against a real daemon before touching +anything: a raw socket `list` request came back with `"ID"`, +`"StartedAt"`, `"StatusCode"` - exactly the mismatch suspected. Nothing +outside this repo's own Go code consumes this wire format yet, so +adding tags now (rather than documenting the wart) was a free, purely +additive fix - every affected struct now tags snake_case throughout, +consistent with the rest of the protocol, verified with a full +build/vet/test pass afterward. + +### First plugin shipped: `plugins/authcheck` + +An Autorize-style authorization checker, and the reference +implementation `PLUGINS.md` points to. Deliberately speaks the wire +protocol directly - its own local `request`/`response`/`summary`/ +`entryDetail` structs mirroring the real ones field-for-field, not +imported - rather than reaching into `internal/ipc`, even though nothing +stops a Go plugin from doing that. The point isn't style: it's proof +that the documented protocol is actually sufficient on its own, since +that's the only thing a non-Go plugin author has to work with, and the +best available check against silently depending on some Go-internal +convenience that never made it into the docs. + +For every proxied (not `source: "repeater"` - see below) request +carrying an `Authorization` or `Cookie` header, resends it via `repeat` +with that header stripped and compares status classes: if the original +succeeded (2xx/3xx) and the anonymous resend *also* succeeded, that's a +likely missing-function-level-access-control bug, tagged +`authcheck:bypass` with the two status codes and which header was +stripped as the tag's JSON data. Matches Burp's own Autorize default of +only surfacing likely findings, not logging every check performed. +Explicitly guards against reprocessing its own resends (`Repeat` +records `source: "repeater"`, filtered out on the subscribe feed) - +without that, the plugin would try to authcheck its own control-group +requests, which is at best wasted work and at worst misattributes a +finding to the wrong entry. + +Simplified relative to real Autorize: Autorize's other mode swaps in a +SECOND, lower-privileged identity's session rather than going fully +anonymous, which catches cross-account IDORs an anonymous-only check +can't (an endpoint might correctly reject "no credentials" while still +leaking another user's data to a validly-authenticated-but-wrong-user +request). That needs a second credential as input this reference +version doesn't take - a natural, small extension (a `-low-priv-cookie` +flag, one more resend variant, one more tag) once wanted. + +Verified live end to end against a real daemon, a real Python origin +server with one endpoint that looks like it enforces auth but doesn't +(vulnerable by design) and one that actually does (the control case): +the broken endpoint was correctly tagged, the properly-secured one was +correctly left untouched - no false positive - confirmed both via the +stored tag data (`go run` against the daemon directly) and visually in +the TUI (tmux, real keystrokes): the `Tags` column badge, `T`'s tag +list, and the tag detail view's JSON-colorized data, ANSI-verified, all +showing the plugin's actual findings. + +Next: the remaining Phase 1 plugins (Param Miner, Backslash Powered +Scanner, Retire.js - no new protocol capability needed, same pattern +`authcheck` already validates), then the live-RPC protocol addition for +JWT Editor/SAML Raider. |