Map a web application's own JavaScript bundle to the API/route "surface" it exposes: HTTP endpoints, route paths, base URLs, notable config keys, and patterns that look like accidentally-bundled secrets.
It is a small, dependency-light static analyzer and a compact reverse-engineering / recon utility from my security-review toolkit. It answers one defensive question: "What is my web app unintentionally revealing to anyone who opens DevTools?"
This tool is for authorized self-audit only. Run it on:
- your own applications (your code, your bundles), or
- targets you are explicitly authorized to test — a signed penetration-test engagement, or assets that are in scope for a bug-bounty program.
It is a defensive awareness tool. Everything a browser downloads to run your app is already readable by every visitor; this tool just makes what you ship easy to review before an attacker does it for you.
What it will never do: it does not attack, exploit, log in, fuzz, or call any endpoint it discovers. It does not transmit, upload, or exfiltrate anything. It only reads the JavaScript you point it at and prints findings to your terminal. Flagged secrets are redacted by default so the report itself is safe to share with a teammate.
If a target is not yours and not in a documented authorization scope, do not point this (or any other recon tool) at it.
Given a JavaScript file (typically a built/minified front-end bundle), it statically reports:
- API endpoints —
fetch(),axios(axios(...),axios.get/post/...,axios.create),XMLHttpRequest.open(), and jQuery$.ajax, each with its HTTP method and URL. Template/computed URLs are kept and marked[dynamic]. - Route / path literals — string literals that look like routes
(e.g.
/api/users,/admin/users,/settings/billing). - Base / absolute URLs —
https://…origins andbaseURLvalues. - Notable config keys —
apiKey,baseURL,clientId,apiVersion,graphqlUrl, etc. - Potential secrets — high-signal patterns (JWTs, AWS/Google keys, Stripe
keys, GitHub/Slack tokens, private-key blocks,
Bearerliterals) and credential-like assignments (apiKey: "…"), flagged for you to review.
Output is a structured JSON report plus a clean human-readable summary.
It uses acorn to parse the bundle into an
AST for accurate method/URL extraction. If acorn is not installed, it
automatically falls back to a regex pass (labeled in the report) so it still
runs with zero dependencies.
Requires Node.js 18+ (developed and tested on Node 24).
git clone <your-fork-url> web-surface-mapper
cd web-surface-mapper
npm install # installs acorn (optional but recommended)
# analyze a local bundle
node bin/cli.js path/to/app.bundle.js
# or, after `npm link` / global install:
web-surface-mapper path/to/app.bundle.js
# or without installing:
npx . path/to/app.bundle.js
# run the built-in sample
npm run demoweb-surface-mapper <path-to-js-file> [options]
web-surface-mapper <https://url-to-bundle.js> [options] (authorized targets only)
--json Print the structured JSON report to stdout
--out <file> Write the JSON report to <file>
--show-secrets Reveal full flagged values locally (default: redacted)
--no-color Disable colored output
-h, --help Show this help
Passing an https:// URL downloads that script once (a plain GET, like
curl) and analyzes it locally — use it only on bundles you are authorized
to fetch and test. Nothing about you or your systems is sent.
The repo ships a synthetic bundle at sample/app.bundle.js
containing fake endpoints, routes, and obviously-fake tokens (reserved
.example domains, placeholders like EXAMPLE_fake_key_do_not_use). Running
npm run demo produces:
Web Surface Mapper — authorized self-audit only
Source: sample/app.bundle.js
Scanned: 2026-10-13T23:07:59.619Z · parser: acorn-ast
API ENDPOINTS (7)
POST /api/orders [http-client] (line 16)
DELETE /api/orders/42 [axios] (line 17)
GET /api/products [http-client] (line 15)
PUT /api/profile [xhr] (line 18)
GET /api/users [fetch] (line 13)
POST /api/users [fetch] (line 14)
GET /api/users/${userId}/posts [dynamic] [fetch] (line 19)
ROUTE / PATH LITERALS (10)
/admin/users (line 21)
/api/orders [api] (line 16)
/api/orders/42 [api] (line 17)
/api/products [api] (line 15)
/api/profile [api] (line 18)
/api/users [api] (line 13)
/api/users/${userId}/posts [api] (line 19)
/dashboard (line 21)
/login (line 21)
/settings/billing (line 21)
BASE / ABSOLUTE URLS (2)
https://api.acme-demo.example/graphql (line 7)
https://api.acme-demo.example/v1 (line 11)
CONFIG KEYS (7)
apiBase = https://api.acme-demo.example/v1 (line 7)
apiKey [sensitive — see potential secrets] (line 7)
apiVersion = 2026-01-15 (line 7)
baseURL = https://api.acme-demo.example/v1 (line 11)
clientId = acme-web-client (line 7)
graphqlUrl = https://api.acme-demo.example/graphql (line 7)
token [sensitive, non-literal value] (line 22)
POTENTIAL SECRETS — REVIEW (5)
Flagged for the owner to review. Nothing was transmitted.
⚠ Sensitive config key with literal value: apiK…_use (34 chars) (line 7)
⚠ JSON Web Token (JWT): eyJh…sw5c (156 chars) (line 8)
⚠ Google API key: AIza…ABCD (39 chars) (line 9)
⚠ Stripe secret key: sk_t…7890 (40 chars) (line 10)
⚠ Bearer token literal: Bear…_use (34 chars) (line 11)
SUMMARY: 7 endpoints · 10 routes · 2 base URLs · 7 config keys · 5 potential secrets
Reminder: run only on applications you own or are explicitly authorized to test.
The same run as JSON (--json, excerpt):
{
"tool": "web-surface-mapper",
"source": "sample/app.bundle.js",
"parser": "acorn-ast",
"summary": { "endpoints": 7, "routes": 10, "baseUrls": 2, "configKeys": 7, "potentialSecrets": 5 },
"endpoints": [
{ "kind": "http-client", "method": "POST", "url": "/api/orders", "dynamic": false, "line": 16 },
{ "kind": "axios", "method": "DELETE", "url": "/api/orders/42", "dynamic": false, "line": 17 }
],
"baseUrls": [
{ "url": "https://api.acme-demo.example/graphql", "line": 7 }
],
"potentialSecrets": [
{ "type": "Sensitive config key with literal value", "preview": "apiK…_use (34 chars)", "line": 7 },
{ "type": "JSON Web Token (JWT)", "preview": "eyJh…sw5c (156 chars)", "line": 8 }
]
}- Pre-ship self-audit. Run it against your production bundle in CI or before a
release to catch a secret that leaked into the client, or an internal
/admin/*route that shouldn't be referenced from the public app. - Recon on an authorized engagement. Quickly enumerate the server-side surface a single-page app talks to — endpoints, methods, and hidden routes — to seed deeper, properly authorized testing.
- Triage a
.jsartifact. Point it at a saved bundle to get a fast map of what it calls and what it carries, without executing untrusted code.
Findings are a starting map, not a verdict: a flagged "secret" may be a public key or a false positive, and a missing endpoint may be built dynamically. Confirm anything important against the source.
- Static analysis only: URLs assembled entirely at runtime from many variables
show up as
[dynamic]or not at all. - Secret detection matches shapes of known credential formats; always verify before treating a match as a live, sensitive secret.
- The regex fallback (no
acorn) can't read afetchoptions object, so some methods default toGET— installacornfor accurate methods.
web-surface-mapper/
├── bin/cli.js # CLI: args, input loading, output
├── src/analyzer.js # static analysis (AST + regex passes)
├── src/report.js # human-readable console report
├── sample/app.bundle.js# synthetic demo bundle (fake data only)
├── package.json
├── LICENSE
└── README.md
MIT © 2026 Tyler Milam (Golden Rock)