srdusr
aboutsummaryrefslogtreecommitdiffstats
path: root/plugins
AgeCommit message (Collapse)AuthorFilesLines
2026-08-28Fourth plugin: bpscanner, a Backslash Powered Scanner-style detectorsrdusr1-0/+291
- Phase 1 plugin ecosystem complete The last of the four "cheap IPC win" plugins identified in the original research pass. Different mechanism from the other three, deliberately: where paramminer finds parameters that shouldn't exist, bpscanner tests parameters that already do, asking a more general question than a signature-based scanner does - does the backend treat syntactically-significant characters ('"\<>(){}$;|&, covering SQL quoting, HTML/JS, shell metacharacters, and template syntax at once) differently than an equal-length string of inert filler? That question doesn't need to know what the backend is built on, the whole appeal of the real tool this borrows its name and idea from. For each existing query parameter, sends two same-length replacement values wrapped in a stable marker - one filler, one special-character - and checks whether the marker itself came back intact, not just whether the response looks different overall. An endpoint that never reflects the parameter at all naturally produces "both intact: false," which correctly isn't a finding - the marker-reflection design avoids false-positiving on the common case of a parameter that's read but never echoed. Verified live against a deliberately realistic scenario: an origin with one endpoint that strips a few special characters before reflecting a parameter (a naive-sanitizer/WAF-like pattern) and one that reflects verbatim (the control case) - the sanitizing endpoint was correctly tagged with the exact differential (control_marker_intact: true, special_marker_intact: false), the verbatim endpoint correctly left alone. This closes Phase 1: every plugin identified as a "cheap IPC win" - no new protocol capability needed beyond tag_entry itself - is now real, working, and live-verified (authcheck, paramminer, jslibscan, bpscanner).
2026-08-27Third plugin: jslibscan, a Retire.js-style passive JS library scannersrdusr1-0/+285
The first purely passive plugin in the reference set: subscribe, inspect a response body already captured by ordinary proxying, tag - no repeat calls at all, unlike authcheck and paramminer. Deliberately included as the safest possible plugin to try first, since it never sends anything of its own. Checks any JS library version string found in a response against a small, explicitly-illustrative built-in table (jQuery, Lodash, Handlebars, Moment.js, AngularJS - one well-known vulnerable-version threshold each), tagging a match jslibscan:hit with the version found, the fix version, and a plain-language advisory. Not a maintained vulnerability feed the way real Retire.js's database is, and says so in its own package doc; CVE numbers deliberately omitted in favor of describing the vulnerability class, rather than asserting a specific identifier this reference implementation hasn't independently verified. Found and fixed a real regex bug by testing live rather than trusting the code: the first version failed to match jQuery's own actual banner comment ("jQuery v1.8.3") because the separator pattern only allowed a single character between library name and version digits, and that banner has two (space, then "v"). Fixed with a bounded non-greedy gap verified against three real-world version-string shapes at once (banner comment, minified filename, cache-busting query string) before going back into the plugin. Verified live end to end: a real daemon, a real origin serving both an outdated jQuery 1.8.3 banner and a current 3.7.1 one - the outdated file was correctly tagged with the right version and threshold, the current one correctly left alone. The same run incidentally reconfirmed the regex fix was real: an entry captured moments earlier against the buggy binary sat right next to the correctly-tagged one, itself untagged.
2026-08-26Second plugin: paramminer, a Param Miner-style hidden parameter probersrdusr1-0/+318
For every distinct GET endpoint (deduplicated in-memory so revisiting a URL doesn't rerun the whole wordlist each time), sends a fresh baseline resend plus one probe per candidate from a ~40-entry wordlist of parameter names real backends surprisingly often read even when never part of any observed request (debug, admin, redirect, role, token, and similar). A probe whose response differs from baseline by more than a small threshold (body length, or a different status outright) is a likely hit, tagged paramminer:hit with the parameter name and both response sizes as evidence. Deliberately GET-only with a modest wordlist, not exhaustive POST/JSON-aware probing - same "small honest v1" reasoning as authcheck's single-identity simplification. Same discipline as authcheck: speaks the wire protocol directly, no internal/ipc import, proving PLUGINS.md's documented protocol is actually sufficient on its own. Found and documented two real, non-obvious net/http behaviors while building this: Request.Write ignores the RequestURI field entirely (confirmed directly - a deliberately stale RequestURI still produced the correct output, since Write derives the request line from Request.URL instead) and silently adds a default User-Agent header if the cloned request didn't already have one. Neither affects correctness here since baseline and every probe get identical treatment, but both are worth knowing before reusing this resend pattern elsewhere. Verified live end to end: a real daemon, a real Python origin with a genuinely hidden debug parameter that substantially changes the response, and a control endpoint that's stable regardless of any extra parameter - the hidden-parameter endpoint was correctly tagged with exactly the right parameter name, the stable one correctly left alone, confirmed via tag: search and visually in the TUI with the JSON-array tag payload rendering correctly.
2026-08-25Wire-format JSON tag consistency fix, and the first real pluginsrdusr1-0/+265
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.