JSFinder is a fast, safe, and modular domain attack-surface discovery CLI tool engineered for authorized bug-bounty hunters, red teams, and penetration testers. It identifies subdomains, live hosts, JavaScript files, source maps, hidden API endpoints, and other static web resources while enforcing strict scope boundaries and robust per-host rate limiting.
- Strict Scope Enforcement: Centralized scope validation engine ensures zero requests are made to out-of-scope hosts, including intercepting and aborting redirect chains before contacting foreign origins (e.g. preventing SSRF/phishing traps or accidental scanning of un-scoped third parties).
- Passive-First Subdomain Discovery: Integrates Certificate Transparency (crt.sh) logs, HTML page crawling, and JavaScript code mining, with optional active DNS wordlist enumeration (
--active-subdomains). - HTTP/HTTPS Probing: Safely probes discovered hosts to record status codes, final URLs after redirects, server headers, page titles, content types, and response times.
- Resource Extraction & Chunk Identification: Discovers
.js,.js.map,.css,.json,.xml,.txt,.yaml, and identifies framework chunks (Webpack, Next.js, Vite, Nuxt). - Deep JavaScript Static Analysis: Extracts absolute URLs, relative paths, API routes (
/api/...,/v1/...,/graphql), URL query parameters, referenced subdomains, and source map directives (sourceMappingURL=), tracking full provenance for each finding. - Source Map Detection: Identifies source maps via directives and conventional
.mapprobes, safely checking status and size without exposing raw source code in the terminal unless requested (--download-sourcemaps). - Per-Host Token Bucket Rate Limiter: Independent rate limiter buckets per hostname (e.g.
example.com,api.example.com,cdn.example.com), respectingRetry-After, handling HTTP429responses with bounded exponential backoff, and enforcing global concurrency limits. - Automatic Multi-Target Directory Output: Automatically creates
jsresult/<domain>/containing full structured JSON and individual CSV reports (endpoints.csv,resources.csv,hosts.csv,subdomains.csv,source_maps.csv). Different target domains are organized neatly side-by-side in the samejsresult/parent directory. - Comprehensive Output Formats: Supports structured JSON, tabular CSV, dedicated multi-file directory reports (
--output-dir), and formatted terminal dashboards.
JSFinder is structured into clean, decoupled Python modules:
jsfinder/
├── cli.py # CLI argument parsing, banner, logging configuration
├── crawler.py # Asynchronous crawl coordinator, depth & page manager
├── subdomains.py # Passive CT log discovery, DNS resolver, HTTP host prober
├── http.py # Safe async HTTP client with scope & redirect interceptors
├── scope.py # Centralized ScopeManager and ScopeRule matcher
├── ratelimit.py # Per-host token bucket rate limiter & 429 backoff engine
├── parser.py # HTML parser, URL normalizer, resource classifier
├── js_analyzer.py # JavaScript AST/regex scanner, endpoint & parameter extractor
├── models.py # Dataclass models for findings, probes, and results
├── output.py # Terminal reporting, JSON export, and CSV writer
└── tests/ # Full test suite (scope, rate limits, parser, mock server)
- Python 3.11 or higher
pippackage managergit
git clone https://github.com/tejassroot/jsfinder.git
cd jsfinder
pip install -r requirements.txtLink jsfinder directly to /usr/local/bin so it can be executed from any terminal directory:
chmod +x jsfinder.py
sudo ln -sf "$(pwd)/jsfinder.py" /usr/local/bin/jsfinderAlternatively, install via pip:
pip install .jsfinder --help
# or: python jsfinder.py --helpScan a single target URL or domain (using either the global jsfinder command or python jsfinder.py):
jsfinder -u https://example.com
# or
python jsfinder.py -u https://example.comAutomatic File Saving: Scan results are automatically saved into
jsresult/<domain>/containingresults.json,endpoints.csv,resources.csv,subdomains.csv,hosts.csv, andsource_maps.csv. Scanning multiple domains organizes them neatly side-by-side inside the samejsresult/parent directory!
Discover subdomains passively using Certificate Transparency (crt.sh) and inline scripts:
python jsfinder.py -u https://example.com --subdomainsEnable active DNS enumeration using a wordlist:
python jsfinder.py -u https://example.com --subdomains --active-subdomainsOptionally provide a custom wordlist:
python jsfinder.py -u https://example.com --active-subdomains --wordlist subdomains.txtMaintain conservative traffic profiles to comply with engagement Rules of Engagement (RoE):
python jsfinder.py -u https://example.com \
--rate 2 \
--concurrency 3 \
--delay 0.5 \
--jitter--rate 2: Limits requests to 2 requests/sec per individual hostname.--concurrency 3: Ensures at most 3 simultaneous active connections across all hosts.--delay 0.5: Minimum 500ms delay between consecutive requests to the same host.--jitter: Introduces randomized micro-delays to prevent mechanical patterns.
By default, JSFinder scopes scans to the target host and its subdomains (example.com and *.example.com).
To specify an explicit scope file:
python jsfinder.py -u https://example.com --scope scope.txtExample scope.txt:
# Exact domain match
example.com
# Wildcard subdomains
*.example.com
*.api.example.com
# Internal IP subnets
10.0.0.0/8
192.168.1.0/24
Security Guarantee: Domains such as
evil-example.com,example.com.attacker.com, or external CDNs will be strictly blocked unless explicitly listed in your scope.
By default, every scan automatically creates a structured output folder under jsresult/<domain>/. When you scan multiple domains, they are all neatly organized side-by-side inside the same jsresult/ parent directory:
jsfinder -u https://pescheck.io/
# Automatically creates and saves to: jsresult/pescheck.io/
jsfinder -u https://example.com/
# Automatically creates and saves to: jsresult/example.com/Each domain directory contains clean, deduplicated, single-column CSV files (ideal for scripting and pipelines) while preserving full metadata in results.json:
results.json— Complete scan metadata, timestamps, headers, IPs, and provenanceendpoints.csv— Clean list of all discovered endpoint and API URLsresources.csv— Clean list of all discovered JavaScript (.js) file URLssubdomains.csv— Clean list of all discovered subdomainshosts.csv— Clean list of all probed live host URLssource_maps.csv— Clean list of all discovered source map URLsurls.txt— Clean deduplicated list of all discovered URLs (one per line, ready for piping)sourcemaps/— Downloaded.mapfiles (when--download-sourcemapsis enabled)
Print only clean discovered URLs to stdout (one per line, ideal for piping into httpx, nuclei, gf, or curl):
jsfinder -u https://example.com --urls-onlyPipe directly into httpx or other reconnaissance tools:
jsfinder -u https://example.com --urls-only | httpx -status-code -titleOverride the default jsresult/<domain>/ directory with a custom path:
jsfinder -u https://example.com --output-dir ./audit_resultsIf you only want terminal output without writing files to disk, pass --no-save:
jsfinder -u https://example.com --no-save# Export clean discovered URLs to a plain text file
jsfinder -u https://example.com --txt urls.txt
# Or using -o with a .txt extension:
jsfinder -u https://example.com -o urls.txt
# Export single JSON file
jsfinder -u https://example.com --json results.json
# Export single endpoints CSV file
jsfinder -u https://example.com --csv endpoints.csvSafely download discovered .js.map files to disk under the output directory:
jsfinder -u https://example.com --download-sourcemaps| Flag | Argument | Description | Default |
|---|---|---|---|
-u, --url |
URL |
Target URL or domain to scan (Required) | — |
--urls-only |
— | Print only discovered URLs to stdout (ideal for piping) | False |
--scope |
FILE |
Path to scope file with domains, wildcards, or CIDRs | Default target scope |
--subdomains |
— | Enable passive subdomain discovery (crt.sh, HTML/JS extraction) | False |
--active-subdomains |
— | Enable active DNS subdomain enumeration | False |
--wordlist |
FILE |
Custom wordlist for active DNS enumeration | Built-in top words |
--rate |
FLOAT |
Maximum requests per second per host | 2.0 |
--concurrency |
INT |
Maximum global concurrent HTTP requests | 3 |
--delay |
FLOAT |
Minimum delay in seconds between requests to same host | 0.5 |
--jitter |
— | Add small randomized jitter to inter-request delays | False |
--timeout |
FLOAT |
HTTP request timeout in seconds | 10.0 |
--user-agent |
STR |
Custom User-Agent header string | Chrome/128 JSFinder |
--insecure |
— | Disable TLS/SSL certificate verification | False |
--max-depth |
INT |
Maximum HTML crawl recursion depth | 2 |
--max-pages |
INT |
Maximum number of HTML pages to crawl | 50 |
--download-sourcemaps |
— | Save discovered source map files to output directory | False |
--output-dir |
DIR |
Save full multi-table reports and downloaded artifacts | jsresult/<domain> |
--no-save |
— | Disable automatic saving of results to jsresult/ |
False |
-o, --output |
FILE |
Generic output file path (.txt, .json, or .csv) |
— |
--txt |
FILE |
Save clean discovered URLs to plain text file (one per line) | — |
--json |
FILE |
Save structured findings to JSON file | — |
--csv |
FILE |
Save discovered endpoints to CSV file | — |
-v, --verbose |
— | Enable detailed debug logging | False |
-q, --quiet |
— | Suppress banner and progress logs | False |
JSON output includes discovery provenance:
{
"target": "https://example.com",
"scan_time": "2026-08-27T13:30:00Z",
"scope": [
"example.com",
"*.example.com"
],
"subdomains": [
{
"hostname": "api.example.com",
"source": "crt.sh",
"ips": ["93.184.216.34"],
"is_live": true
}
],
"hosts": [
{
"url": "https://example.com",
"status": 200,
"final_url": "https://example.com",
"content_type": "text/html",
"content_length": 1420,
"response_time": 0.124,
"server": "cloudflare",
"title": "Example Domain Portal"
}
],
"javascript": [
"https://example.com/assets/app.8b4ef2.js"
],
"source_maps": [
{
"url": "https://example.com/assets/app.8b4ef2.js.map",
"referenced_js": "https://example.com/assets/app.8b4ef2.js",
"status": 200,
"content_type": "application/json",
"size": 245120,
"detected_via": "directive"
}
],
"resources": [
{
"url": "https://example.com/assets/app.8b4ef2.js",
"resource_type": "javascript",
"source_url": "https://example.com",
"tag": "script",
"framework_chunk": true
}
],
"endpoints": [
{
"endpoint": "/api/v1/users",
"source": "https://example.com/assets/app.8b4ef2.js",
"endpoint_type": "api",
"parameters": ["page", "limit", "sort"]
}
],
"parameters": [
"limit",
"page",
"sort"
]
}Run the test suite with pytest:
pytest -vThe test suite covers:
- Scope validation: Exact matches, wildcard subdomains, IP/CIDR, and security boundary rejection of tricky suffixes (
evil-example.com,example.com.attacker.com). - URL normalization: Protocol-relative URLs, dot-segment resolution, trailing slashes, fragment stripping, and non-HTTP scheme discard.
- Rate limiting: Per-host token buckets, minimum inter-request delays, concurrency ceilings, and 429
Retry-Afterbackoff. - HTML Resource parsing: Script, link, image, iframe extraction, extension classification, and framework chunk detection.
- JavaScript analysis: Absolute URLs, relative paths, REST/GraphQL endpoints, query parameters, referenced subdomains, and source map directives.
- Deduplication: Resource, endpoint, and parameter deduplication across crawl cycles.
- Redirect scope enforcement: Following in-scope redirects while aborting out-of-scope hops.
- Integration test: Complete scan against a local mock HTTP server serving HTML, JS chunks, source maps, redirects, and 429 throttling.
JSFinder is designed exclusively for authorized penetration testing, security assessments, and bug-bounty engagements. The tool adheres to non-destructive methodologies:
- No credential attacks or brute forcing
- No active exploits or destructive payloads
- No denial-of-service behavior (conservative default rate limits)
- Strict scope adherence to prevent unintentional requests to out-of-scope hosts