burp2model
burp2model turns Burp traffic into an evidence-backed model of a web app. It is a model builder, not a scanner.
- Pure Python, no runtime dependencies. It never replays your capture.
- Every node and edge cites the requests it came from (
ev_N). - Secret values are masked before anything is written.
- It names what the capture could not tell you, and never calls anything a vulnerability.
Install #
Python 3.9 or newer.
pip install burp2model burp2model --version
From source: pip install -e ".[dev]", then pytest -q (288 tests).
Quickstart #
In Burp, open Proxy → HTTP history, select all, right-click, Save items. Keep base64 encoding on. Burp Community works. Only capture traffic you are authorized to record.
# build: writes burp2model-out/shop/ burp2model history.xml --webapp shop # ask: no AI, every line cited burp2model query shop "code vs runtime" burp2model query shop help # two accounts: one build per role, then compare burp2model user.xml -w shop --role user burp2model admin.xml -w shop --role admin burp2model cross-role shop --low user --high admin
No Burp? burp2model crawl URL --yes -w shop crawls a running app and builds the same model. It sends live requests, so use it only on apps you are authorized to test. --read-only blocks anything but GET, HEAD and OPTIONS.
Output of query shop "code vs runtime" on the bundled sample:
APIs: 10 endpoints
BOTH 6
STATIC_ONLY 1
RUNTIME_ONLY 3
static-only * /api/admin/audit ev_3STATIC_ONLY means the code names the path but the capture never requested it. The * means the code gave no HTTP method, so none is guessed.
Outputs #
Every build writes burp2model-out/WEBAPP/. The default --webapp name is app.
| File | What it is |
|---|---|
report.html | Offline dashboard with a map, a query console and an evidence drawer. Demo |
model.json | The full graph, unknowns and the evidence table. Example |
context.json | A compact package of the model. Example |
graph.db | One SQLite file with the graph and redacted exchanges. burp2model q WEBAPP queries it. |
graph.json · graph.graphml | The graph for D3, Cytoscape, Gephi and yEd. |
build.svg | The build animation. |
inputs/<role>.jsonl | Redacted per-role records. Role builds merge from these. |
By default a build also runs light recon on the primary public host and saves osint.json. It fails soft. Use --no-osint or BURP2MODEL_OFFLINE=1 to stay offline.
The six layers #
third_party, role, auth, cookieendpoint, parameter, operation (GraphQL)script, plus the workers it spawns and the source map it names.route: pages, redirects and other GET resourceshost: schemes, ports, server headers- Every edge is OBSERVED (seen in traffic) or INFERRED (read from code). Recon edges are
EXTERNAL. - Layer 3: a script that starts a worker gets a
SPAWNSedge (inferred). A script that names a source map gets asourcemapattribute. If the map is not in the capture, the model raises the unknownSOURCE_MAP_NOT_CAPTURED. - Reconciliation compares endpoints named in code with endpoints seen at runtime: BOTH, STATIC_ONLY or RUNTIME_ONLY.
- Scope: the busiest host sets the first-party domain (by Public Suffix List). Everything else is a third party. Override with
--scope. - Unknowns include
AUTHENTICATED_STATE_NOT_OBSERVED,ROLE_NOT_TAGGED,API_PURPOSE_UNKNOWN,AUTHORIZATION_UNKNOWNandCAPTURE_ITEMS_SKIPPED.
An absence in the model is an absence in the capture, not a statement about the target.
Query intents #
burp2model query WEBAPP "INTENT" answers from the model with no language model. Every line is cited and ends with Model call: none.
| Intent | Answers |
|---|---|
list-apis | Every endpoint with state, statuses and evidence. |
reconcile | Code vs runtime counts. code vs runtime also works. |
list-routes | Every route. |
route-apis /x | The APIs a route calls, directly or through its scripts. |
provenance /api/orders | Where an endpoint was seen: evidence, statuses, roles, callers. |
third-parties | Off-scope hosts. |
auth-surface | Credentials, roles and cookie flags. |
unknowns | What the capture could not answer. |
entity-evidence NAME | The evidence ids behind any node, by label. |
Also available: parameters, secrets (masked), privileged, errors and shape. Any question that matches no intent is refused, not guessed.
For filters over graph.db, use burp2model q WEBAPP 'req.method:POST AND resp.code.gte:400'. q WEBAPP help lists the language.
Redaction #
Before an exchange leaves the parser, every value is masked and every name and shape is kept. password=hunter2 becomes password=[REDACTED].
- Headers such as
Authorization,CookieandSet-Cookie. - Sensitive parameter names, plus JWTs, API keys, private keys, card numbers and emails wherever they appear.
- Value-like URL segments:
/reset/alice@corp.com/Zx9kQ2pLm7Rtbecomes/reset/{email}/{token}.
Bodies are never written to disk. A masked value is kept only as a keyed HMAC fingerprint (kind, length, entropy, count). The key lives at ~/.config/burp2model/fingerprint.key. Tests check that no planted secret reaches any output file. The HTML report escapes every string from the target and blocks network requests with a strict CSP.
Limits #
- Endpoint extraction from code is pattern-based, not a JavaScript parser. URLs built at runtime can be missed.
- Path-token masking uses shape and entropy. A short token can survive, and an odd slug can be masked.
- Default scope uses the busiest host. A capture dominated by a CDN can pick the wrong site. Use
--scope. - "Privileged-looking" in
cross-roleis a path-name heuristic. - Page-to-API edges depend on the Referer header. Strict policies give fewer
CALLSedges. - The model covers only what was captured. Nothing it reports is a finding.
FAQ #
Is this a scanner?
No. It reads a capture you already made. It never sends requests to build a model from a Burp export. Only crawl, osint and the default build recon touch the network.
Is it safe to share the outputs?
They hold no secret values or bodies. They do hold hostnames, paths and parameter names, so treat them as engagement material. Never share the fingerprint key.
My API subdomain shows as a third party.
It is under a different registrable domain. Pass --scope with both domains.
cross-role says a role is missing.
Build each capture with --role into the same --out and --webapp. A build without --role starts over.
Can I open graph.db directly?
Yes. It is plain SQLite. burp2model q WEBAPP schema lists the tables.