srdusr
aboutsummaryrefslogtreecommitdiffstats

mitmux

A terminal-based intercepting proxy for manual pentest work - a daily-driver alternative to Burp Suite, Caido, or OWASP ZAP that runs entirely in your terminal, with vi-style modal editing for raw requests.

mitmux is two binaries: mitmuxd, a headless daemon that owns the proxy listener and the SQLite history database, and mitmux, a Bubble Tea TUI that talks to it over a Unix socket. The daemon keeps running (and keeps capturing traffic) independently of the TUI - close the UI, reopen it later, the proxy never stopped.

For the build history, the architectural decisions behind it, and a list of what's deliberately not implemented (and why), see PLAN.md. To write a plugin - any language, connects over the same socket the TUI itself uses - see PLUGINS.md.

Features

  • Intercepting proxy: plaintext HTTP passthrough and TLS interception (per-host leaf certificates signed by a locally generated CA), with HTTP/1.1 and HTTP/2 handled natively and independently on the client and upstream legs - a client that only speaks HTTP/1.1 and an origin that prefers HTTP/2 both work correctly in the same request.
  • WebSocket: ws:///wss:// connections are relayed byte-for-byte unmodified with every frame captured for display, not just the upgrade handshake - see WebSocket below.
  • History: every request/response captured to SQLite. Raw wire bytes are preserved byte-for-byte on HTTP/1.1 legs (what request smuggling and parser-differential analysis actually needs); HTTP/2 legs - which have no single "raw bytes" representation, being multiplexed HPACK-compressed framing - are reconstructed and marked as such, never silently presented as exact.
  • Search: full-text search (FTS5) across headers and bodies, plain text just works (example.com, x-forwarded-for, 192.168.1.1 - no quoting needed), plus structured filters: status:404, status:4xx, status:>=400, source:repeater, flagged:true, column filters like host:example.com, and AND/OR/NOT. Sort the loaded page by any column (o/O).
  • Repeater: edit and resend a raw request. What you type is what goes on the wire - no normalization, no auto-fixed Content-Length, no "helpful" reformatting. That's the point of a Repeater. Multiple tabs: sending an entry to Repeater opens a new tab rather than replacing whatever's already there, so you can iterate on several requests side by side.
  • Intruder: mark positions in a request template with §markers§, supply payloads, and fuzz them with Sniper, Battering ram, Pitchfork, or Cluster bomb - Burp's own four attack modes. Results land in the same history table as everything else, searchable the same way. Payload processing (optional case and encode rules, applied to every payload before it's sent) and grep-match/grep-extract (flag or pull text out of each result's response with a regexp) are both configurable before starting an attack - see Intruder below.
  • Match-and-replace: header or body rewrite rules (add, remove, or modify) for requests and/or responses, applied live as traffic passes through. History still shows what was actually sent/received on each side - match-and-replace transforms the wire, it doesn't rewrite the audit trail.
  • Client certificates: configure a mutual-TLS cert/key per host pattern, presented automatically on matching handshakes - for proxied traffic and Repeater/Intruder resends alike.
  • Flagging: mark an entry to revisit later (★), filterable via flagged:true.
  • Plugins: any external process, any language, can connect over the same socket the TUI uses and tag entries with structured data - a JWT decoder, an authorization-bypass checker, anything. Tags show as a badge in the history list, searchable via tag:name, viewable (T from detail view) with JSON data syntax-highlighted the same way a pretty-printed response is. See PLUGINS.md and plugins/authcheck/plugins/paramminer/plugins/jslibscan/ plugins/bpscanner for real, working ones (an Autorize-style authorization checker, a Param Miner-style hidden parameter prober, a Retire.js-style scanner for known-vulnerable JS library versions, a Backslash Powered Scanner-style generic injection detector).
  • Comparer: mark one entry (c), then c on a different entry to see a colored unified diff of either side's request or response.
  • Decoder: standalone URL/Base64/Hex/HTML encode and decode (d), output updates live as you type or switch transforms.
  • Vi-modal editing: the raw request editors (Repeater, Intruder) are real modal editors - normal mode by default, i/a/o/etc. to insert, hjkl, dd/yy/p, word motions, gg/G. See Vi bindings below. Everywhere else (history table, read-only response views), standard vi navigation (j/k, g/G, ctrl+u/ctrl+d) already works - that's the underlying TUI library's default, not something layered on top.
  • Mouse support: wheel scroll anywhere there's something to scroll, right-click for a context menu on the history/rules/scope lists. Coexists with tmux's own mouse mode the same way any other mouse-aware terminal app does. See Mouse below.

Install / build

Requires Go 1.26.5+ (see go.mod). No other dependencies - the SQLite driver (modernc.org/sqlite) is pure Go, so there's no C toolchain to install and nothing to link against, on any platform.

git clone <this repo>
cd mitmux
make build          # -> bin/mitmuxd, bin/mitmux

make build covers the common case; see the Makefile itself for the rest - make install (via go install, respecting GOBIN/GOPATH as usual), make release (cross-compiles both binaries for every platform in PLATFORMS into dist/), make test (the same build/vet/gofmt/test checks expected before every commit - see PLAN.md). Pushing a v* tag runs the same make release in CI and attaches the resulting archives (one per platform, .tar.gz on Unix/.zip on Windows, SHA256SUMS alongside them) to a GitHub Release, once this repo actually lives on GitHub - see .github/workflows/release.yml. Building without make works identically:

go build -o bin/mitmuxd ./cmd/mitmuxd
go build -o bin/mitmux  ./cmd/mitmux

Platforms

Linux is the primary target and the only one this has actually run on during development - every feature in this document was verified live against a real daemon and real traffic there. macOS, Windows, and FreeBSD all cross-compile cleanly with CGO_ENABLED=0 and pass go vet (make release builds all of them), and the code itself has nothing Linux-specific in it - CA/history storage uses Go's own cross-platform os.UserConfigDir(), not a hardcoded XDG path - but they haven't been run on real hardware, so treat them as "should work, not yet verified" rather than a tested claim. If you try one and hit something, that's useful to know about.

One concrete thing worth knowing on macOS specifically: its Unix domain socket path limit is shorter than Linux's, and the default control-socket location lives under ~/Library/Application Support/mitmux/ - a deeper path than Linux's usual $XDG_RUNTIME_DIR. A long username or home directory path could push past the limit; if mitmuxd fails to bind its control socket, pass a shorter -socket /tmp/mitmux.sock (and the matching -socket to mitmux) to both binaries.

Quick start

  1. Start the daemon. By default it listens on 127.0.0.1:8080 and stores its CA and history database under your OS's standard config directory (Linux: ~/.config/mitmux, macOS: ~/Library/Application Support/mitmux, Windows: %AppData%\mitmux):

sh ./bin/mitmuxd

On first run it generates a root CA and prints where the cert landed, e.g. ~/.config/mitmux/ca.pem.

  1. Trust the CA. To intercept HTTPS without constant certificate warnings, import ca.pem into whatever's making the requests - your browser's certificate store, curl --cacert, a mobile device's trusted-certificate settings, etc. For copy-pasteable, OS-specific steps (Linux: whichever of trust/update-ca-trust/ update-ca-certificates is actually on your system, plus Firefox's own NSS store; macOS: Keychain; Windows: certutil/PowerShell), run:

sh ./bin/mitmuxd -install-ca

This only prints commands - it never runs anything against your trust store itself. Installing a root CA is a system-wide trust change, so you run the printed command yourself.

For a phone, tablet, or any other device where copying a file over and importing it manually is awkward - the actual common case for mobile testing - point the device's browser at http://mitmux.cert/ once it's configured to proxy through mitmux (step 3). mitmuxd recognizes that hostname specifically and serves its own CA certificate as a download, the same trick mitmproxy's mitm.it uses: no DNS lookup, no real domain, works the moment traffic is flowing through the proxy at all - the browser's install-certificate prompt handles the rest. Plain http://, not https://: fetching it over TLS would need the device to already trust mitmux's CA to intercept that very connection, which is the exact problem this page solves.

  1. Point a client at the proxy. e.g.:

sh curl -x http://127.0.0.1:8080 --cacert ~/.config/mitmux/ca.pem https://example.com/

Or configure your browser's proxy settings to 127.0.0.1:8080 - directly, or via a proxy-switcher extension like FoxyProxy: add a new proxy profile pointing at 127.0.0.1:8080 (HTTP, and reused for HTTPS - mitmux handles both on the same listener), no mitmux-specific setup needed since it's a standard forward proxy speaking the same protocol Burp/ ZAP/Caido all do. Same story for a phone or tablet: set its Wi-Fi proxy to your machine's LAN address and the daemon's port.

Or skip manual configuration entirely: mitmux -launch-browser=chrome (or firefox, or auto to use whichever's installed) opens a fresh, throwaway browser profile already pointed at the proxy, landing straight on http://mitmux.cert/ so installing the CA in that one profile is a single click. The profile is brand new every time - no cookies, extensions, or cached certificate-trust decisions carried over from your regular browsing - and never reused, matching the "don't disturb your everyday session" spirit of a pentest tool. mitmux doesn't ship its own browser - building and maintaining one is a different project entirely, and a terminal proxy tool has no business trying; this launches your existing one instead.

  1. Open the TUI (in another terminal - the daemon keeps running independently):

sh ./bin/mitmux

Both binaries take flags for non-default setups - -listen, -socket, -ca-dir, -db, -upstream-proxy on mitmuxd; -socket, -launch-browser on mitmux. Both also take -version (prints version/commit/date and exits - dev for a plain go build; make build/make release fill it in from git describe) and -h for the full list.

-listen takes a comma-separated list to bind more than one address (-listen "127.0.0.1:8080,127.0.0.1:8081") - one logical proxy on several ports/interfaces, sharing the same history, CA and rules.

-upstream-proxy host:port chains every outbound connection through another HTTP CONNECT proxy (Burp, a corporate proxy, anything that speaks CONNECT) instead of dialing origins directly. Chaining into another intercepting proxy needs that proxy's own CA trusted too - it terminates and re-signs the connection with its own CA, which mitmux's outbound TLS client has no reason to trust otherwise; you'll see a clear certificate-verification error in history rather than a silent failure. -upstream-proxy socks5://[user:pass@]host:port chains through a SOCKS5 proxy instead - Tor, ssh -D, or any other SOCKS5 relay - with optional username/password auth.

Usage

Press ? from any screen in the TUI for the full, current keybinding reference - it's generated from the same source as this document, so it never drifts out of date the way a static list can. The summary below is enough to get going.

History (the default view)

Key Action
↑/↓ or j/k navigate (also g/G top/bottom, ctrl+u/ctrl+d half-page)
enter view request/response detail
r open in Repeater
i open in Intruder
f toggle flag
c mark for comparison - press c on another entry to diff
x delete the selected entry (asks y/n to confirm)
X clear ALL history, not just the current search filter (asks y/n to confirm)
E export the current view (respects an active search filter) - .har or .csv
I import a HAR file's entries into history
d Decoder
/ search
o cycle sort column (captured/status/size/time taken/method/host/path)
O reverse the current sort column's direction
m match-and-replace rules
s target scope (what gets recorded)
t client (mutual-TLS) certificates
q quit

Also mouse-driven - wheel to scroll, right-click a row for a context menu of the same actions. See Mouse below.

o cycles which column sorts the currently loaded page (captured order - the default, newest-first or search-relevance order - then status, size, time taken, method, host, path); O reverses whichever column is active. Client-side, on top of whatever List/Search already returned: loading more or re-searching keeps the same sort applied. Status-code color-coding (2xx green through 5xx red, the same convention Burp and Caido use) isn't in the table itself - bubbles/table, the terminal table widget this UI is built on, has no way to color one cell without corrupting the whole row's layout (confirmed, not guessed: its column- width fitting counts every character of a color code as visible text). It's in the detail view's title instead, where that constraint doesn't apply.

Detail view

tab switches request/response, p toggles pretty-printed, syntax-highlighted JSON on the response (keys/strings/numbers/booleans colored, matching most editors - display-only, never touches the stored or resent bytes), c mark/compare (same as the history list), r/i jump straight to Repeater/Intruder seeded from this entry, e exports the entry (request and response, raw bytes, plain text - type a path and press enter), w views captured WebSocket messages if this entry's connection was upgraded (see WebSocket below), esc back.

WebSocket

A ws:// or wss:// request that gets a matching 101 Switching Protocols back stops being one-shot request/response - mitmux relays every frame byte-for-byte unmodified in both directions (this is capture, not tampering) while decoding each one's payload for display. Press w from an upgraded entry's detail view to see them: direction, opcode (text/binary/close/ping/pong), size, and a preview; enter on a row shows that frame's full decoded payload. One row per frame, not per reassembled logical message - a message fragmented across several frames (rare in real-world WebSocket traffic: JSON events, chat messages, game state are almost always single-frame) shows up as several rows rather than being stitched back together.

Comparer

Reachable by pressing c on two different history entries (from either the list or detail view). Shows a colored unified diff - diff -u style, +/- lines - of the two entries' requests or responses, tab to switch between them. CRLF is normalized before diffing so an exact HTTP/1.1 capture doesn't show every line as changed purely from the invisible \r.

Decoder

Reachable with d from the history list - a standalone tool, not seeded from any entry. i to type or paste text; the output pane updates live as you type. tab/shift+tab cycles through URL, Base64, and Hex encode/decode and HTML entity encode/decode. Base64 decode tries the standard, URL-safe, padded, and unpadded variants in turn rather than requiring you to know which one you're looking at. Single-transform only - not chained/pipelined the way Burp's Decoder supports.

Export

Two independent export paths, both a modal path-prompt (enter writes and confirms, esc cancels). Format is picked by the extension you type, the same convention any "save as" dialog uses - no separate format-selection control:

  • e from Detail view exports the single selected entry.
  • .txt (default) - request and response raw bytes, plain text, exactly what Detail view already shows. Each side is annotated when it isn't wire-exact (truncated or reconstructed), matching Detail view's own labels.
  • .sh / .curl - the request as a runnable curl command line (Burp/DevTools' own "copy as curl"), for handing to someone else or re-running standalone without mitmux. Every value is shell-quoted (a captured or edited request can contain arbitrary bytes).
  • E from the history list exports the current view - the visible, filtered set if a search is active, everything otherwise.
  • .har (default) - one HAR 1.2 file, for importing into Chrome/Firefox DevTools, Burp, Postman, or anything else that reads HAR. A binary body (an image, say) is base64- encoded rather than corrupted as text. An entry that fails to fetch or parse is skipped rather than aborting the whole export; the status line reports how many, if any.
  • .csv - a summary table (id, method, host, path, status, sizes, timing, flag, source) for a report or spreadsheet - lighter and faster than HAR since it needs no per-entry fetch from the daemon. Any field that could be interpreted as a spreadsheet formula (starts with =, +, -, @, tab, or CR - method/host/path/error all ultimately trace back to a request line or Host header, exactly the kind of content this tool exists to inspect from hostile traffic) is neutralized with a leading quote before writing, the standard CSV-injection mitigation.

Import

I from the history list reads a HAR file and inserts its entries into history, tagged source:import so they're easy to find or filter out later (source:import in search). Works with a HAR from mitmux itself or from anywhere else that produces one - Chrome/Firefox DevTools, Burp, Postman. An entry that fails to convert is skipped rather than aborting the whole import; the status line reports how many, if any. Imported entries are always shown as "reconstructed" (never "exact") - they're rebuilt from HAR's structured fields, not the literal bytes that were actually on the wire for the original request, same situation an HTTP/2 capture is already in.

Search syntax

Plain text searches headers and bodies on both sides of the exchange. A handful of characters that are FTS5 syntax rather than literal text (., -, /, @, and more) are handled transparently - you don't need to quote a domain name or an IP address for it to work.

  • example.com, x-forwarded-for, 192.168.1.1 - literal text, just works
  • host:example.com, AND, OR, NOT - FTS5 syntax for column filters and boolean queries
  • status:404 - exact status code
  • status:4xx (also 2xx/3xx/5xx) - status range shorthand
  • status:>=400, status:!=200 - status comparison operators
  • source:proxy / source:repeater / source:intruder - where the request came from
  • flagged:true / flagged:false
  • Combine freely: admin status:200 source:repeater

Repeater / Intruder - vi bindings

The request editors start in normal mode, not insert mode - like real vi. Press i (or a/I/A/o/O) to start typing, esc to go back to normal mode. The mode indicator (-- NORMAL -- / -- INSERT --) is always visible while one of these editors is focused.

Normal-mode key Action
h j k l left / down / up / right
0 / $ line start / end
w / b word forward / back
x delete character
i a I A insert: before cursor / after cursor / line start / line end
o / O open a line below / above and insert
dd / yy delete / yank the current line
p / P paste below / above
dw d$ d0 delete word / to end of line / to start of line
gg / G top / bottom of the buffer
esc (in insert mode) back to normal mode - never leaves the view

This is a genuine, if intentionally scoped, modal editor: it translates these commands into the underlying text widget's own editing primitives rather than reimplementing cursor and line manipulation. Not implemented: registers beyond a single yank slot, visual mode, ex commands, macros, and counts (3dd, 5j). There's no undo, because the underlying text widget doesn't have one either.

ctrl+r sends (Repeater) or starts the attack (Intruder). tab switches panes. In Intruder's template pane specifically, ctrl+g inserts a § marker at the cursor if typing the character directly isn't convenient on your keyboard/terminal.

Repeater supports multiple concurrent tabs - each open request/response pair is independent. ]/[ switch to the next/previous tab, ctrl+w closes the active one. All three only fire in normal mode, so they don't interfere with typing ([/] show up in JSON bodies constantly, and ctrl+w is the editor's own delete-word-backward while composing).

Intruder

Beyond marking §positions§ and supplying payloads, three more things are configurable before ctrl+r starts the attack - all normal-mode-only shortcuts, available from any pane:

  • a cycles the attack mode: Sniper, Battering ram, Pitchfork, Cluster bomb - Burp's own four, same semantics. Sniper fuzzes one marked position at a time through a single shared payload set, every other position held at its base value. Battering ram sends the same payload, from that same single set, into every marked position at once. Pitchfork and Cluster bomb are inherently per-position - that's their whole point - so they need one payload set per marked position instead of one shared set: put them in the same Payloads pane, separated by a line containing exactly ---, in position order. Pitchfork walks all sets in lockstep, one request per index, stopping at the shortest set's length. Cluster bomb tries every combination (the last position cycles fastest), so its request count is the product of every set's length - capped at 1000 requests like every other mode, checked before anything is sent.
  • c / e cycle payload processing: an optional case rule (off/upper/lower) and an optional encode rule (off/URL/Base64/Hex/ HTML), shown in the status line above the results table. Applied to every payload, case first then encode, right before it's substituted into the request - case-folding an already-encoded value would corrupt it (e.g. uppercasing Base64 padding), so case always runs on the original text first.
  • m / v edit grep-match / grep-extract, each a Go regexp evaluated against every result's actual response bytes (same enter confirms / esc cancels pattern as the history list's / search - an invalid regexp is rejected with an error rather than silently accepted). Grep-match flags a result (a Match column) if the pattern is found anywhere in the response; grep-extract captures the first submatch - or the whole match, if the pattern has no capturing group - into an Extract column. Both are optional and independent; leave either blank to skip that check.

Both settings apply for the attack you're about to start - changing them mid-run doesn't retroactively re-evaluate requests already sent, matching Burp's own behavior.

Match-and-replace rules

Press m from the history view. Rules match request or response headers or bodies (a add, enter/e edit, d delete, space toggle enabled, Part field to choose header vs body). Matching is literal-substring by default, or regex if the rule's Regex toggle is on. Header rules operate on the raw header block as text, not per-value substitution, so a rule can add or remove a header entirely, not just rewrite an existing one. Body rules run directly against the raw body bytes; a body larger than the capture cap is left unmodified rather than partially rewritten, and Content-Length is recomputed automatically when a body rule changes a body's length.

Scope

Press s from the history view to manage what gets recorded to history - not what gets proxied. Out-of-scope traffic still reaches its destination and the response still reaches the client completely normally; it's just not stored, so unrelated CDN/analytics/tracker noise doesn't pollute history and search on a real engagement. No rules (or none enabled) means everything is recorded, same as before scope existed.

a adds a rule (a pattern, and tab to toggle regex matching - same Match-text-or-regex model as match-and-replace rules), space toggles one on/off, d deletes the selected one. A non-regex pattern matches by case-insensitive substring against the host - example.com matches example.com, www.example.com, and api.example.com alike, covering "this domain and its subdomains" without a separate wildcard syntax.

Repeater and Intruder always record regardless of scope - a request you deliberately resend or fuzz is something you clearly want to see the result of, not noise scope exists to cut.

Client (mutual-TLS) certificates

Press t from the history view to manage which client certificate mitmux presents when an upstream server's TLS handshake requests one - a target requiring mutual TLS otherwise fails the handshake before any request/response ever happens. a adds one: a name, a host pattern (same substring-or-regex model as scope and match-and-replace rules), and paths to a PEM certificate file and its matching PEM private key. The files are read once, at save time, and their content - not the paths - is what's stored and later presented, so a cert keeps working even if the original file moves or is deleted afterward. space toggles one on/off, d deletes it. Applies to proxied HTTPS traffic and to Repeater/Intruder resends against https:// targets alike; a host matching no configured certificate just handshakes without one, same as if this feature didn't exist.

Mouse

This is a real terminal application (any terminal, not just tmux - the name is a naming convention, not a runtime dependency) with genuine mouse support, not just a keyboard-only TUI:

  • Wheel scrolls whatever's focused - the history/rules/scope tables, a Detail/Comparer/Decoder-output pane, or a Repeater/ Intruder text editor.
  • Right-click a row in the history, rules, or scope list opens a context menu of the same actions the keyboard shortcuts already do (view/repeater/intruder/flag/delete for history, edit/enable-disable/ delete for rules and scope). Click an item, or navigate with j/k/arrows and enter; esc or right-clicking again dismisses it without doing anything.

One honest limitation, not an oversight: the menu always acts on the row that's currently selected, not necessarily the exact row your cursor happens to be over when you right-click. The underlying table widget doesn't expose its own scroll position, so there's no reliable way to map a click's screen coordinates back to a specific row without reaching into that library's private internals - which this deliberately doesn't do, rather than risk silently selecting the wrong one. Scroll or navigate to the row you want first; wheel scroll and keyboard navigation both work exactly as you'd expect.

If you're running inside tmux, its own mouse mode needs to be on too (set -g mouse on in .tmux.conf) for mouse events to reach mitmux at all - tmux forwards them to whichever pane is focused once that's set, the same way it does for nvim or any other mouse-aware terminal app, nothing mitmux-specific to configure. If you ever want to select and copy on-screen text with the mouse instead (which an app with mouse mode on normally intercepts), most terminal emulators let you hold Shift while click-dragging to fall back to the terminal's own native selection.

Architecture

mitmuxd owns the proxy listener and the SQLite database; mitmux is a thin client that only ever talks to the daemon over a Unix socket (internal/ipc). This split is deliberate: a web UI, a CLI scanner, or any other client could be bolted on later without touching the proxy engine, and the TUI restarting (or crashing) never interrupts capture.

Capture fidelity is the other core design constraint: for HTTP/1.1 traffic, request and response bytes stored in history are exactly what was read off the wire - captured via a net.Conn wrapper that records bytes as net/http's own (memory-safe) parser consumes them, rather than re-serializing a parsed representation. For HTTP/2, which has no meaningful single "raw bytes" form, the stored representation is a reconstruction, and every stored entry says which kind it is (request_exact/response_exact in the database, "exact" vs. "reconstructed" in the UI). Repeater and Intruder both write raw bytes straight to the wire for the same reason - the whole point of a Repeater is that a deliberately malformed request reaches the target unmodified.

Package layout:

cmd/mitmuxd/    daemon entrypoint
cmd/mitmux/     TUI entrypoint
internal/ca/    root CA + per-host leaf certificate generation
internal/proxy/ proxy engine: HTTP/CONNECT handling, capture, Repeater, Intruder
internal/rules/ match-and-replace engine
internal/scope/ target scope (what gets recorded)
internal/store/ SQLite storage, FTS5 search
internal/ipc/   daemon <-> client protocol (JSON over a Unix socket)

Development

go build ./...
go vet ./...
gofmt -l .          # should print nothing
go test ./...

There's no mock traffic layer - the test suite covers pure logic (marker parsing, search-query parsing) that's worth locking down with real tests rather than trusting by inspection. Everything that touches the network, the daemon, or the TUI has been verified by actually running it against real traffic during development; see commit messages for what was checked and how.

Known limitations

Deliberate scope decisions, not oversights - see PLAN.md for the reasoning behind each:

  • Intruder: sequential sending only (no concurrent workers), capped at 1000 requests per attack across all four modes
  • mitmuxd -install-ca prints per-OS trust-store install steps; it never runs them for you (see Quick start above for why)
  • WebSocket messages are captured one row per frame, not reassembled from fragments (rare in real-world traffic) - see WebSocket below
  • No active or passive vulnerability scanning, no plugin system - this is a manual-testing tool, not a scanner

License

GPL-3.0. See LICENSE.

Copyright (C) 2026 srdusr