September 3, 2026
Systematic JavaScript Reconnaissance
JavaScript surely knows too much.

By Taoqui
5 min read
Personally, I like JavaScript.
You can build a frontend, backend, APIs, automation, and so much more using a single language. It has become one of the most widely used languages on the web, and honestly, that is one of the things I find fascinating about it.
But there is another side to that.
Because JavaScript is used so widely, especially on modern web applications, it often ends up revealing way more information than it should. Not necessarily because someone intentionally put a secret in the code.
- Sometimes it's an API endpoint.
- Sometimes it's an internal route.
- Sometimes it's a forgotten configuration.
- Sometimes it's a source map containing the original source code.
- And sometimes, yes, it's an actual secret sitting there waiting to be noticed.
That's what makes JavaScript interesting from a reconnaissance perspective. This is the complete workflow I use to systematically analyze JavaScript files during authorized security testing and bug bounty reconnaissance
Important: Only perform these techniques against applications you own or are explicitly authorized to test.
1. Understand What We're Actually Looking For
The goal isn't simply: "Find an API key." That's too narrow.
When analyzing JavaScript during security assessments, I want to answer four core questions:
- What endpoints does the application communicate with?
2. What sensitive information is exposed?
3. What functionality isn't obvious from the UI?
4. Are there old or forgotten files containing information that shouldn't be public?
This turns our high-level reconnaissance pipeline into a systematic flow:
Target β Subdomains β Live Hosts β Crawl β Extract JS β Analyze β Validate
2. Find the JavaScript Files
If you already have a list of discovered target URLs, start by extracting all JavaScript endpoints:
cat urls.txt | grep -Ei '\.js($|\?)' | sort -u > js.txtcat urls.txt | grep -Ei '\.js($|\?)' | sort -u > js.txtNow js.txt contains the JavaScript URLs we want to investigate.
If you're crawling a live application, tools like Katana can help discover dynamic JavaScript resources that load at runtime:
# Crawl target web application with JS execution enabled
katana -u https://target.com -jc -o urls.txt
# Isolate JavaScript files
grep -Ei '\.js($|\?)' urls.txt | sort -u > js.txt# Crawl target web application with JS execution enabled
katana -u https://target.com -jc -o urls.txt
# Isolate JavaScript files
grep -Ei '\.js($|\?)' urls.txt | sort -u > js.txtAt this point, we've gone from a massive list of generic URLs to something far more focused: the application's client-side attack surface.
3. Download and Inspect the Files
Create a local directory for the target files:
mkdir -p js
cat js.txt | xargs -P 10 -I {} wget -q -P js/ {}mkdir -p js
cat js.txt | xargs -P 10 -I {} wget -q -P js/ {}The important rule here is: Do not immediately start searching for "secret."
First, understand the file layout. Look at:
- Filenames
- Application modules
- API paths
- Authentication logic
- Configuration objects
- Third-party services
- Comments
- Environment variables
- Source map references
A minified production bundle may look terrible at first glance:
(()=>{const e="api";function t(){/* ... */}();(()=>{const e="api";function t(){/* ... */}();Don't let that discourage you. Beautifying or de-minifying the JavaScript can make the application's logic vastly easier to understand:
# Beautify minified JavaScript files in place
npx js-beautify -r js/*.js# Beautify minified JavaScript files in place
npx js-beautify -r js/*.js4. Search for Sensitive Keywords
Now we can start looking for obvious indicators using pattern matching:
grep -RniE \
'api[_-]?key|apikey|secret|token|password|passwd|authorization|bearer|client[_-]?secret|access[_-]?key|private[_-]?key' \
js/grep -RniE \
'api[_-]?key|apikey|secret|token|password|passwd|authorization|bearer|client[_-]?secret|access[_-]?key|private[_-]?key' \
js/This can reveal interesting strings such as:
apiKeyclientSecretaccessTokenAuthorizationpasswordinternalToken
The Context Rule
A keyword match does NOT automatically mean you found a vulnerability.
For example:
const apiKey = "PUBLIC_CLIENT_IDENTIFIER";const apiKey = "PUBLIC_CLIENT_IDENTIFIER";That might be intentionally public (e.g., public analytics or mapping client IDs).
On the other hand:
const secret = "sk_live_99a8b7c6d5e4f321...";const secret = "sk_live_99a8b7c6d5e4f321...";Could potentially be much more serious. Context matters.
5. Don't Forget Cloud and Service-Specific Patterns
Different services have recognizable, high-entropy credential formats. You can search for known patterns directly:
grep -RniE \
'AKIA[0-9A-Z]{16}|AIza[0-9A-Za-z_-]{35}|gh[pousr]_[A-Za-z0-9_]{20,}' \
js/grep -RniE \
'AKIA[0-9A-Z]{16}|AIza[0-9A-Za-z_-]{35}|gh[pousr]_[A-Za-z0-9_]{20,}' \
js/Again, finding a matching string is only the beginning. You still need to determine:
- What is it?
- Is it actually sensitive?
- Is it active?
- What permissions does it have?
- Was exposing it actually unintended?
6. The Part People Often Miss: API Endpoints
One of the most valuable things hidden inside JavaScript isn't a secret β it's an endpoint.
For example, the frontend might contain logic like:
fetch("/api/v2/users/profile")fetch("/api/v2/users/profile")or:
axios.get("/internal/reports")axios.get("/internal/reports")or:
fetch("/api/admin/export")fetch("/api/admin/export")These endpoints may never appear in the application's visible UI navigation.
Search your local files for unlinked routes:
grep -RniE \
'/api/|/v1/|/v2/|/graphql|/admin|/internal|/debug|/upload|/download|/export|/auth|/oauth' \
js/grep -RniE \
'/api/|/v1/|/v2/|/graphql|/admin|/internal|/debug|/upload|/download|/export|/auth|/oauth' \
js/Now our JavaScript files have become an endpoint discovery source. This is where JS recon becomes much more interesting.
7. Look for Parameters Too
Finding an endpoint is useful. Finding how the frontend uses it is even better.
Look for parameter keys across fetch calls or function handlers:
iduserIdaccountIdfileredirecturlcallbacktokenrolepagequerysearch
For example, finding this inside a bundle:
fetch(`/api/users/${userId}/profile`)fetch(`/api/users/${userId}/profile`)This tells us much more than simply knowing /api/users/ exists. We now understand something crucial about the application's request structure, enabling targeted testing for broken object-level authorization (IDOR).
8. Source Maps: The Forgotten Goldmine
Now comes one of my favorite checks. Search for source map declarations:
grep -RniE '\.map|sourceMappingURL' js/grep -RniE '\.map|sourceMappingURL' js/You might find references like:
app.jsapp.js.map
A source map can expose information from the original source code that isn't obvious in the production bundle. Depending on how the application was built, this can include:
- Original uncompiled directory structure & filenames
- Full source code and developer comments
- API routes and internal microservice modules
- Development logic and debugging flags
- Configuration objects
So whenever I see sourceMappingURL, I investigate it. If .map files are publicly reachable on the target host, you can unpack the developer's original repository tree:
npx restore-source-tree -i js/app.js.map -o ./reconstructed_sourcenpx restore-source-tree -i js/app.js.map -o ./reconstructed_source9. Current JavaScript Isn't Always Enough
Here's another important part of the workflow: Don't only look at today's JavaScript.
Applications change over time:
- Developers rename endpoints.
- Features get removed from the frontend UI.
- Credentials get rotated (or forgotten).
- Old functionality disappears from modern pages.
However, older JavaScript files can frequently remain publicly hosted on CDNs or web servers long after references to them are deleted from the main HTML index.
Current JS βΆ Historical JS (Wayback/Gau) βΆ Diff Analysis βΆ Deprecated Endpoints & Stale ConfigsCurrent JS βΆ Historical JS (Wayback/Gau) βΆ Diff Analysis βΆ Deprecated Endpoints & Stale ConfigsComparing historical JavaScript files with current versions allows you to uncover:
- New endpoints added over time
- Removed/deprecated endpoints that backends still execute
- Old configuration stubs
- Forgotten or leaked legacy credentials
This turns JavaScript analysis from a static snapshot into a timeline.
10. Validate Before Calling Something a Finding
This is probably the most important step in the entire process.
Finding this:
api_key = "something"api_key = "something"does not automatically mean: "Critical vulnerability."
Before reporting anything, ask yourself:
- Is the value actually sensitive? Some keys are designed to be client-side public (e.g., public analytics keys).
- Is it still active? Old credentials or test environment tokens may no longer be live.
- What permissions does it have? A read-only public identifier is vastly different from a credential with privileged write access.
- Is the exposure actually caused by the target? Third-party vendor libraries often contain their own public identifiers.
- Can you demonstrate security impact safely? Don't perform destructive actions just to prove a point. The goal is evidence, not damage.
The Complete JS Recon Pipeline
Putting everything together into a unified workflow:
ββββββββββββββββββββββββ
β TARGET β
ββββββββββββ¬ββββββββββββ
β
βΌ
ββββββββββββββββββββββββ
β Subdomain Enumerationβ
ββββββββββββ¬ββββββββββββ
β
βΌ
ββββββββββββββββββββββββ
β Live Host Discovery β
ββββββββββββ¬ββββββββββββ
β
βΌ
ββββββββββββββββββββββββ
β Crawling β
ββββββββββββ¬ββββββββββββ
β
βΌ
ββββββββββββββββββββββββ
β Extract JS & Clean β
ββββββββββββ¬ββββββββββββ
β
βΌ
ββββββββββββββββββββββββ
β Download & Beautify β
ββββββββββββ¬ββββββββββββ
β
βββββββββββββββββ΄ββββββββββββββββ
βΌ βΌ
βββββββββββββββββββββββ βββββββββββββββββββββββ
β Secret Search β β Endpoint Search β
βββββββββββββββββββββββ€ βββββββββββββββββββββββ€
β β’ API Keys β β β’ API Routes β
β β’ Cloud Credentials β β β’ Parameters β
β β’ Auth Tokens β β β’ Hidden APIs β
ββββββββββββ¬βββββββββββ ββββββββββββ¬βββββββββββ
β β
βββββββββββββββββ¬ββββββββββββββββ
β
βΌ
ββββββββββββββββββββββββ
β Source Maps & Diffs β
ββββββββββββ¬ββββββββββββ
β
βΌ
ββββββββββββββββββββββββ
β Validate & Document β
ββββββββββββββββββββββββ ββββββββββββββββββββββββ
β TARGET β
ββββββββββββ¬ββββββββββββ
β
βΌ
ββββββββββββββββββββββββ
β Subdomain Enumerationβ
ββββββββββββ¬ββββββββββββ
β
βΌ
ββββββββββββββββββββββββ
β Live Host Discovery β
ββββββββββββ¬ββββββββββββ
β
βΌ
ββββββββββββββββββββββββ
β Crawling β
ββββββββββββ¬ββββββββββββ
β
βΌ
ββββββββββββββββββββββββ
β Extract JS & Clean β
ββββββββββββ¬ββββββββββββ
β
βΌ
ββββββββββββββββββββββββ
β Download & Beautify β
ββββββββββββ¬ββββββββββββ
β
βββββββββββββββββ΄ββββββββββββββββ
βΌ βΌ
βββββββββββββββββββββββ βββββββββββββββββββββββ
β Secret Search β β Endpoint Search β
βββββββββββββββββββββββ€ βββββββββββββββββββββββ€
β β’ API Keys β β β’ API Routes β
β β’ Cloud Credentials β β β’ Parameters β
β β’ Auth Tokens β β β’ Hidden APIs β
ββββββββββββ¬βββββββββββ ββββββββββββ¬βββββββββββ
β β
βββββββββββββββββ¬ββββββββββββββββ
β
βΌ
ββββββββββββββββββββββββ
β Source Maps & Diffs β
ββββββββββββ¬ββββββββββββ
β
βΌ
ββββββββββββββββββββββββ
β Validate & Document β
ββββββββββββββββββββββββ13. The Bigger Lesson
The real value of JavaScript reconnaissance isn't just finding secrets. It's understanding the application from the client's perspective.
The browser has to know:
- Where the APIs are
- How requests are constructed
- What parameters are expected
- What services are used
- What features exist
- How authentication works
- What data the frontend can access
And if the browser can see it, a security researcher can analyze it. That's why I consider JavaScript one of the most valuable sources of information during web reconnaissance.
The best recon isn't: "I ran 20 tools simultaneously."
It's: "I turned publicly accessible client code into an accurate, actionable map of the application's attack surface."
And JavaScript is often the best map you can get.