LinkFinder: extracting endpoints from JavaScript bundles
LinkFinder · MIT
LinkFinder parses JavaScript files and pulls out anything that looks like an endpoint. Modern front ends ship their entire routing table to the browser, which means the bundle is a fairly complete description of the API — including routes the UI never calls.
Install
git clone https://github.com/GerbenJavado/LinkFinder.git
cd LinkFinder && pip install -r requirements.txt
python linkfinder.py -h Core commands
# one JS file, plain output
python linkfinder.py -i https://example.com/static/app.js -o cli
# every script on a page
python linkfinder.py -i https://example.com -d -o cli
# a whole domain from the Wayback corpus
python linkfinder.py -i "https://example.com/*" -o results.html
# a local bundle
python linkfinder.py -i ./app.bundle.js -o cli -o cli prints to stdout, which is what you want in a pipeline; the default writes an HTML report.
Output
One candidate endpoint per line:
/api/v2/users
/api/v2/admin/impersonate
/internal/healthz Where it sits in Tandera
LinkFinder runs in the analyze phase of recon_web_lite and recon_web_full, against the JavaScript that katana retrieved during crawling. It sits alongside jxscout and retirejs, which read the same bundles for different things — secrets and vulnerable dependencies respectively.
The endpoints it recovers rejoin the URL corpus and become probe targets.
Using it in a pentest
Admin routes are shipped to every user. A front end that hides an admin panel by not rendering the button still contains the route. /api/v2/admin/impersonate in a bundle served to anonymous users is worth testing immediately — if it is not authorised server-side, that is your critical.
Expect noise. The regex matches path-like strings, so you will get CSS fragments, MIME types and version numbers. Filter, then probe:
python linkfinder.py -i https://example.com -d -o cli
| grep -E '^/' | sort -u
| sed 's|^|https://example.com|' | httpx -silent -status-code Source maps are better when they exist. If app.js.map is served, you get original sources rather than minified output. Always check.