Repository: cc1a2b/JShunter Branch: main Commit: d5cd4aeb5ea4 Files: 30 Total size: 366.9 KB Directory structure: gitextract_f1fvidta/ ├── .gitignore ├── .jshunterignore.example ├── CHANGELOG.md ├── CREDITS.md ├── LICENSE ├── README.md ├── RULES.md ├── cmd/ │ └── jshunter/ │ └── main.go ├── go.mod ├── go.sum ├── internal/ │ └── jshunter/ │ ├── aws_pair.go │ ├── cache.go │ ├── concurrent_verify.go │ ├── crawler.go │ ├── csp.go │ ├── detection.go │ ├── diff.go │ ├── har.go │ ├── html_extract.go │ ├── ignore.go │ ├── jshunter.go │ ├── ndjson.go │ ├── robots.go │ ├── rules_cli.go │ ├── rules_loader.go │ ├── sarif.go │ ├── sourcemap.go │ ├── stats.go │ └── verify.go └── patterns.json ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitignore ================================================ # Compiled binary (rebuilt locally; release artifacts attached on GitHub) /jshunter /jshunter.exe /jshunter_* /dist/ # Go build/test cache *.test *.out # Editor .vscode/ .idea/ *.swp .DS_Store # Operator state .jshunterignore *.sarif *.har ================================================ FILE: .jshunterignore.example ================================================ # JSHunter ignore file. # One entry per line. Blank lines and lines starting with `#` are skipped. # # Supported kinds: # hash: # suppress one specific finding (stable across runs) # rule: # suppress an entire rule or family # source: # suppress all findings from a source matching glob # rule_value:: # # suppress when rule matches AND value matches glob # # Globs use filepath.Match syntax (`*`, `?`, `[abc]`). # Example: suppress an analytics SDK that always carries a public-but-rotating key rule:slack.webhook source:*/cdn.segment.com/* # Example: suppress one specific known FP by its sha256 prefix hash:a1b2c3d4e5f60718 # Example: suppress only test JWTs from a specific bundle rule_value:jwt.token:*test_* ================================================ FILE: CHANGELOG.md ================================================ # Changelog All notable changes to JSHunter are tracked here. Dates are ISO-8601. ## [v0.6 — page-aware crawling, sourcemaps, cache, concurrent verify] — 2026-05-08 The "JS-aware crawler, not just a JS-file scanner" iteration. ### HTML page-awareness (`--inline-html`) `golang.org/x/net/html` tokenizer walks the response, extracts: - Every inline `. body, err := readUntilEndTag(z, "script") if err == nil && strings.TrimSpace(body) != "" { script := InlineScript{ Index: scriptIdx, Body: body, Type: attrs["type"], Nonce: attrs["nonce"], } script.IsLDJSON = strings.EqualFold(script.Type, "application/ld+json") out.InlineScripts = append(out.InlineScripts, script) } scriptIdx++ } case "meta": // CSP via http-equiv (some sites prefer this over header). if strings.EqualFold(tagAttrs(t)["http-equiv"], "Content-Security-Policy") { content := tagAttrs(t)["content"] out.CSPOrigins = append(out.CSPOrigins, ParseCSPOrigins(content)...) } case "link": attrs := tagAttrs(t) rel := strings.ToLower(attrs["rel"]) href := attrs["href"] if href != "" { switch rel { case "preload", "modulepreload", "prefetch": if strings.EqualFold(attrs["as"], "script") || rel == "modulepreload" { out.ExternalJS = append(out.ExternalJS, ExternalJS{ URL: href, Integrity: attrs["integrity"], Type: "module", }) } } } } } } } // readUntilEndTag consumes tokens up to and including the closing tag, // returning the concatenated text content. Used to capture inline script // bodies which the tokenizer reports as a separate Text token. func readUntilEndTag(z *html.Tokenizer, tag string) (string, error) { var buf bytes.Buffer for { tt := z.Next() switch tt { case html.ErrorToken: return buf.String(), z.Err() case html.TextToken: buf.Write(z.Text()) case html.EndTagToken: t := z.Token() if strings.EqualFold(t.Data, tag) { return buf.String(), nil } } } } func tagAttrs(t html.Token) map[string]string { m := make(map[string]string, len(t.Attr)) for _, a := range t.Attr { m[strings.ToLower(a.Key)] = a.Val } return m } func hasAttr(t html.Token, name string) bool { for _, a := range t.Attr { if strings.EqualFold(a.Key, name) { return true } } return false } // looksLikeHTML returns true when the response body is HTML rather than JS. // We use a tiny prefix sniff rather than the full encoding/sniff implementation // because the body is already bounded by --max-bytes. func looksLikeHTML(body []byte, contentType string) bool { if strings.Contains(strings.ToLower(contentType), "html") { return true } head := body if len(head) > 512 { head = head[:512] } low := strings.ToLower(string(head)) low = strings.TrimSpace(low) return strings.HasPrefix(low, " # suppress one specific finding // rule: # suppress an entire rule (or family) // source: # suppress all findings whose source matches // rule_value:: # suppress findings where rule matches and // # value matches the glob (after rule) // // Globs use the standard filepath.Match syntax (`*`, `?`, `[abc]`). type IgnoreEntry struct { Kind string A string B string } type IgnoreList struct { Entries []IgnoreEntry } // LoadIgnoreFile reads and parses an ignore file. A missing file is NOT an // error — operators expect to run with or without one — but a malformed // file is, because silently ignoring bad rules invites "why didn't my // suppression work?" tickets. func LoadIgnoreFile(path string) (*IgnoreList, error) { if path == "" { return nil, nil } f, err := os.Open(path) if err != nil { if os.IsNotExist(err) { return nil, nil } return nil, fmt.Errorf("open ignore file: %w", err) } defer f.Close() return parseIgnoreReader(f) } func parseIgnoreReader(r io.Reader) (*IgnoreList, error) { il := &IgnoreList{} sc := bufio.NewScanner(r) lineNo := 0 for sc.Scan() { lineNo++ line := strings.TrimSpace(sc.Text()) if line == "" || strings.HasPrefix(line, "#") { continue } idx := strings.Index(line, ":") if idx == -1 { return nil, fmt.Errorf("ignore line %d: missing ':' separator", lineNo) } kind := strings.TrimSpace(line[:idx]) rest := strings.TrimSpace(line[idx+1:]) if rest == "" { return nil, fmt.Errorf("ignore line %d: empty value", lineNo) } switch kind { case "hash", "rule", "source": il.Entries = append(il.Entries, IgnoreEntry{Kind: kind, A: rest}) case "rule_value": parts := strings.SplitN(rest, ":", 2) if len(parts) != 2 || parts[0] == "" || parts[1] == "" { return nil, fmt.Errorf("ignore line %d: rule_value needs :", lineNo) } il.Entries = append(il.Entries, IgnoreEntry{Kind: "rule_value", A: parts[0], B: parts[1]}) default: return nil, fmt.Errorf("ignore line %d: unknown kind %q (want hash|rule|source|rule_value)", lineNo, kind) } } if err := sc.Err(); err != nil { return nil, err } return il, nil } // ShouldIgnore returns true if any entry matches this finding. func (il *IgnoreList) ShouldIgnore(f *Finding) bool { if il == nil { return false } for _, e := range il.Entries { switch e.Kind { case "hash": if f.ValueHash == e.A { return true } case "rule": if f.RuleID == e.A || globMatch(e.A, f.RuleID) { return true } case "source": if globMatch(e.A, f.Source) { return true } case "rule_value": if (f.RuleID == e.A || globMatch(e.A, f.RuleID)) && globMatch(e.B, f.Value) { return true } } } return false } func globMatch(pattern, s string) bool { if pattern == "*" { return true } ok, _ := filepath.Match(pattern, s) return ok } ================================================ FILE: internal/jshunter/jshunter.go ================================================ package jshunter import ( "bufio" "context" "crypto/tls" "encoding/csv" "encoding/json" "flag" "fmt" "io" "math" "net" "net/http" "net/url" "os" "regexp" "runtime" "sort" "strings" "sync" "time" "math/rand" "golang.org/x/net/proxy" ) var ( version = "v0.7.5" colors = map[string]string{ "RED": "\033[0;31m", "GREEN": "\033[0;32m", "BLUE": "\033[0;34m", "YELLOW": "\033[0;33m", "CYAN": "\033[0;36m", "PURPLE": "\033[0;35m", "NC": "\033[0m", } // Global deduplication for all outputs globalSeenParams = make(map[string]bool) globalSeenAll = make(map[string]bool) globalSeenMutex sync.Mutex globalFoundAny = false // Track if any findings were made across all files missingMessages = make([]string, 0) // Buffer for MISSING messages missingMutex sync.Mutex ) var ( //regex-cc1a2b regexPatterns = map[string]*regexp.Regexp{ "Google API": regexp.MustCompile(`AIza[0-9A-Za-z-_]{35}`), "Firebase": regexp.MustCompile(`AAAA[A-Za-z0-9_-]{7}:[A-Za-z0-9_-]{140}(?:\s|$|[^A-Za-z0-9_-])`), "Amazon Aws Access Key ID": regexp.MustCompile(`A[SK]IA[0-9A-Z]{16}`), "Amazon Mws Auth Token": regexp.MustCompile(`\bamzn\.mws\.[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\b`), "Amazon Aws Url": regexp.MustCompile(`s3\.amazonaws.com[/]+|[a-zA-Z0-9_-]*\.s3\.amazonaws.com`), "Amazon Aws Url2": regexp.MustCompile(`([a-zA-Z0-9-._]+\.s3\.amazonaws\.com|s3://[a-zA-Z0-9-._]+|s3-[a-z]{2}-[a-z]+-[0-9]+\.amazonaws\.com|s3.amazonaws.com/[a-zA-Z0-9-._]+|s3.console.aws.amazon.com/s3/buckets/[a-zA-Z0-9-._]+)`), "Facebook Access Token": regexp.MustCompile(`EAACEdEose0cBA[0-9A-Za-z]+`), "Authorization Basic": regexp.MustCompile(`(?i)\bauthorization\s*:\s*basic\s+[a-zA-Z0-9=:_\+\/-]{20,100}`), "Authorization Bearer": regexp.MustCompile(`(?i)\bauthorization\s*:\s*bearer\s+[a-zA-Z0-9_\-\.=:_\+\/]{20,100}`), "Authorization Api": regexp.MustCompile(`(?i)\bapi[_-]?key\s*[:=]\s*["']?[a-zA-Z0-9_\-]{20,100}["']?`), "Twilio Api Key": regexp.MustCompile(`SK[0-9a-fA-F]{32}`), "Twilio Account Sid": regexp.MustCompile(`(?i)\b(?:twilio|tw)\s*[_-]?account[_-]?sid\s*[:=]\s*["']?AC[a-zA-Z0-9_\-]{32}["']?`), "Twilio App Sid": regexp.MustCompile(`\bAP[a-fA-F0-9]{32}\b`), "Paypal Braintre Access Token": regexp.MustCompile(`access_token\$production\$[0-9a-z]{16}\$[0-9a-f]{32}`), "Square Oauth Secret": regexp.MustCompile(`sq0csp-[0-9A-Za-z\-_]{43}|sq0[a-z]{3}-[0-9A-Za-z\-_]{22,43}`), "Square Access Token": regexp.MustCompile(`sqOatp-[0-9A-Za-z\-_]{22}`), "Stripe Standard Api": regexp.MustCompile(`sk_live_[0-9a-zA-Z]{24}`), "Stripe Restricted Api": regexp.MustCompile(`rk_live_[0-9a-zA-Z]{24}`), "Authorization Github Token": regexp.MustCompile(`\bghp_[a-zA-Z0-9]{36}\b`), "Github Access Token": regexp.MustCompile(`[a-zA-Z0-9_-]+:[a-zA-Z0-9_\-]{20,}@github\.com\b`), "Rsa Private Key": regexp.MustCompile(`-----BEGIN RSA PRIVATE KEY-----`), "Ssh Dsa Private Key": regexp.MustCompile(`-----BEGIN DSA PRIVATE KEY-----`), "Ssh Dc Private Key": regexp.MustCompile(`-----BEGIN EC PRIVATE KEY-----`), "Pgp Private Block": regexp.MustCompile(`-----BEGIN PGP PRIVATE KEY BLOCK-----`), "Ssh Private Key": regexp.MustCompile(`(?s)-----BEGIN OPENSSH PRIVATE KEY-----[a-zA-Z0-9+\/=\n]+-----END OPENSSH PRIVATE KEY-----`), "Json Web Token": regexp.MustCompile(`\beyJ[A-Za-z0-9_\-]{8,}\.eyJ[A-Za-z0-9_\-]{8,}\.[A-Za-z0-9_\-]{8,}\b`), "Putty Private Key": regexp.MustCompile(`(?s)PuTTY-User-Key-File-2.*?-----END`), "Ssh2 Encrypted Private Key": regexp.MustCompile(`(?s)-----BEGIN SSH2 ENCRYPTED PRIVATE KEY-----[a-zA-Z0-9+\/=\n]+-----END SSH2 ENCRYPTED PRIVATE KEY-----`), "Generic Private Key": regexp.MustCompile(`(?s)-----BEGIN.*PRIVATE KEY-----[a-zA-Z0-9+\/=\n]+-----END.*PRIVATE KEY-----`), "Username Password Combo": regexp.MustCompile(`(?i)\b[a-z]+://[^/\s:@"']{1,64}:[^/\s:@"']{1,128}@[a-zA-Z0-9.\-]{3,255}`), "Facebook Oauth": regexp.MustCompile(`(?i)(?:facebook|fb)[_\-]?(?:app[_\-]?)?(?:secret|client[_\-]?secret|oauth)\s*[:=]\s*['\"]?[0-9a-f]{32}['\"]?`), "Twitter Oauth": regexp.MustCompile(`(?i)\b(?:twitter|tw)\s*[_-]?oauth[_-]?token\s*[:=]\s*["']?[0-9a-zA-Z]{35,44}["']?`), "Github Token": regexp.MustCompile(`(?i)\b(gh[pousr]_[0-9a-zA-Z]{36})\b`), "Google Oauth Client Secret": regexp.MustCompile(`\"client_secret\":\"[a-zA-Z0-9-_]{24}\"`), "Aws Api Key": regexp.MustCompile(`\bAKIA[0-9A-Z]{16}\b`), "Slack Token": regexp.MustCompile(`\"api_token\":\"(xox[a-zA-Z]-[a-zA-Z0-9-]+)\"`), "Ssh Priv Key": regexp.MustCompile(`([-]+BEGIN [^\s]+ PRIVATE KEY[-]+[\s]*[^-]*[-]+END [^\s]+ PRIVATE KEY[-]+)`), "Slack Webhook Url": regexp.MustCompile(`https://hooks.slack.com/services/[A-Za-z0-9]+/[A-Za-z0-9]+/[A-Za-z0-9]+`), "Heroku Api Key 2": regexp.MustCompile(`(?i)\bheroku[_-]?(?:api[_-]?)?key\s*[:=]\s*["']?[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}["']?`), "Dropbox Access Token": regexp.MustCompile(`\bsl\.[A-Za-z0-9_-]{64,200}\b`), "Salesforce Access Token": regexp.MustCompile(`00D[0-9A-Za-z]{15,18}![A-Za-z0-9]{40}`), "Twitter Bearer Token": regexp.MustCompile(`\bAAAAAAAAAAAAAAAAAAAAA[A-Za-z0-9%]{30,80}\b`), "Firebase Url": regexp.MustCompile(`https://[a-z0-9-]+\.firebaseio\.com`), "Pem Private Key": regexp.MustCompile(`-----BEGIN (?:[A-Z ]+ )?PRIVATE KEY-----`), "Google Cloud Sa Key": regexp.MustCompile(`"type": "service_account"`), "Stripe Publishable Key": regexp.MustCompile(`pk_live_[0-9a-zA-Z]{24}`), "Azure Storage Account Key": regexp.MustCompile(`(?i)\b(?:AccountKey|azure[_-]?storage[_-]?key)\s*[:=]\s*["']?[A-Za-z0-9+/]{86}==["']?`), "Instagram Access Token": regexp.MustCompile(`IGQV[A-Za-z0-9._-]{10,}`), "Stripe Test Publishable Key": regexp.MustCompile(`pk_test_[0-9a-zA-Z]{24}`), "Stripe Test Secret Key": regexp.MustCompile(`sk_test_[0-9a-zA-Z]{24}`), "Slack Bot Token": regexp.MustCompile(`xoxb-[A-Za-z0-9-]{24,34}`), "Slack User Token": regexp.MustCompile(`xoxp-[A-Za-z0-9-]{24,34}`), "Google Gmail Api Key": regexp.MustCompile(`\bAIza[0-9A-Za-z_\-]{35}\b`), "Google Gmail Oauth": regexp.MustCompile(`\b[0-9]+-[0-9A-Za-z_]{32}\.apps\.googleusercontent\.com\b`), "Google Oauth Access Token": regexp.MustCompile(`\bya29\.[0-9A-Za-z_\-]{40,}\b`), "Mailchimp Api Key": regexp.MustCompile(`[0-9a-f]{32}-us[0-9]{1,2}`), "Mailgun Api Key": regexp.MustCompile(`key-[0-9a-zA-Z]{32}`), "Google Drive Oauth": regexp.MustCompile(`\b[0-9]+-[0-9A-Za-z_]{32}\.apps\.googleusercontent\.com\b`), "Paypal Braintree Access Token": regexp.MustCompile(`access_token\$production\$[0-9a-z]{16}\$[0-9a-f]{32}`), "Picatic Api Key": regexp.MustCompile(`sk_live_[0-9a-z]{32}`), "Stripe Api Key": regexp.MustCompile(`sk_live_[0-9a-zA-Z]{24}`), "Stripe Restricted Api Key": regexp.MustCompile(`rk_live_[0-9a-zA-Z]{24}`), "Square Access Token 2": regexp.MustCompile(`\bsq0atp-[0-9A-Za-z_\-]{22}\b`), "Square Oauth Secret 2": regexp.MustCompile(`\bsq0csp-[0-9A-Za-z_\-]{43}\b`), "Twitter Access Token": regexp.MustCompile(`(?i)\b(?:twitter|tw)\s*[_-]?access[_-]?token\s*[:=]\s*["']?[0-9]+-[0-9a-zA-Z]{40}["']?`), "Heroku Api Key 3": regexp.MustCompile(`(?i)\bheroku\b[^\n]{0,80}\b[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\b`), "Generic Api Key": regexp.MustCompile(`(?i)\bapi[_-]?key\s*[:=]\s*['\"]?[0-9a-zA-Z]{32,45}['\"]?`), "Generic Secret": regexp.MustCompile(`(?i)\bsecret\s*[:=]\s*['\"]?[0-9a-zA-Z]{32,45}['\"]?`), "Slack Webhook": regexp.MustCompile(`https://hooks[.]slack[.]com/services/T[a-zA-Z0-9_]{8}/B[a-zA-Z0-9_]{8}/[a-zA-Z0-9_]{24}`), "Gcp Service Account": regexp.MustCompile(`\"type\": \"service_account\"`), "Password in Url": regexp.MustCompile(`[a-zA-Z]{3,10}://[^/\s:@"']{3,32}:[^/\s:@"']{3,128}@[a-zA-Z0-9.\-]{3,200}`), "Discord Webhook url": regexp.MustCompile(`https://discord(?:app)?\.com/api/webhooks/[0-9]{18,20}/[A-Za-z0-9_-]{64,}`), "Discord bot Token": regexp.MustCompile(`[MN][A-Za-z\d]{23}\.[\w-]{6}\.[\w-]{27}`), "Okta Api Token": regexp.MustCompile(`00[a-zA-Z0-9]{30}\.[a-zA-Z0-9\-_]{30,}\.[a-zA-Z0-9\-_]{30,}`), "Sendgrid Api Key": regexp.MustCompile(`SG\.[A-Za-z0-9_-]{22}\.[A-Za-z0-9_-]{43}`), "Mapbox Access Token": regexp.MustCompile(`pk\.[a-zA-Z0-9]{60}\.[a-zA-Z0-9]{22}`), "Gitlab Personal Access token": regexp.MustCompile(`glpat-[A-Za-z0-9\-]{20}`), "Datadog Api Key": regexp.MustCompile(`ddapi_[a-zA-Z0-9]{32}`), "shopify Access Token": regexp.MustCompile(`shpat_[A-Za-z0-9]{32}`), "Atlassian Access Token": regexp.MustCompile(`(?i)\b(?:atlassian|jira|confluence)[_-]?(?:api[_-]?)?token\s*[:=]\s*["']?ATATT3[A-Za-z0-9_\-]{180,250}["']?`), "Crowdstrike Api Key": regexp.MustCompile(`(?i)\b(?:crowdstrike|cs)[_-]?(?:api[_-]?)?(?:key|token)\s*[:=]\s*["']?[A-Za-z0-9]{32}\.[A-Za-z0-9]{16}["']?`), "Quickbooks Api Key": regexp.MustCompile(`(?i)\b(?:quickbooks|qbo|intuit)[_-]?(?:api[_-]?)?(?:key|token)\s*[:=]\s*["']?A[0-9a-f]{32}["']?`), "Cisco Api Key": regexp.MustCompile(`(?i)\bcisco[_-]?(?:api[_-]?)?key\s*[:=]\s*["']?[A-Za-z0-9]{30,}["']?`), "Cisco Access Token": regexp.MustCompile(`(?i)\bcisco[_-]?access[_-]?token\s*[:=]\s*["']?[A-Za-z0-9_\-]{20,}["']?`), "Segment Write Key": regexp.MustCompile(`(?i)\b(?:segment[_-]?)?writeKey\s*[:=]\s*["']?[A-Za-z0-9]{32}["']?`), "Tiktok Access Token": regexp.MustCompile(`\btiktok_access_token=[a-zA-Z0-9_]{20,}\b`), "Slack Client Secret": regexp.MustCompile(`xoxs-[0-9]{1,9}.[0-9A-Za-z]{1,12}.[0-9A-Za-z]{24,64}`), "Phone Number": regexp.MustCompile(`(?:^|[\s"'<>:,;(\[])\+\d{9,14}(?:[\s"'<>,;.!?)\]]|$)`), "Email": regexp.MustCompile(`[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}`), "Ali Cloud Access Key": regexp.MustCompile(`\bLTAI[A-Za-z0-9]{12,20}\b`), "Tencent Cloud Access Key": regexp.MustCompile(`\bAKID[A-Za-z0-9]{13,20}\b`), "OpenAI API Key": regexp.MustCompile(`sk-[a-zA-Z0-9]{20}T3BlbkFJ[a-zA-Z0-9]{20}`), "OpenAI API Key Project": regexp.MustCompile(`sk-proj-[a-zA-Z0-9]{48,}`), "OpenAI API Key Svc": regexp.MustCompile(`sk-svcacct-[a-zA-Z0-9_-]{80,}`), "Anthropic API Key": regexp.MustCompile(`sk-ant-api[a-zA-Z0-9-]{37,}`), "HuggingFace Token": regexp.MustCompile(`hf_[a-zA-Z0-9]{34,}`), "Cohere API Key": regexp.MustCompile(`(?i)cohere[_-]?api[_-]?key\s*[:=]\s*["']?[a-zA-Z0-9]{40}["']?`), "Replicate API Token": regexp.MustCompile(`r8_[a-zA-Z0-9]{40}`), "Google AI API Key": regexp.MustCompile(`(?i)(?:gemini|palm|bard)[_-]?api[_-]?key\s*[:=]\s*["']?AIza[a-zA-Z0-9_-]{35}["']?`), "AWS Secret Access Key": regexp.MustCompile(`(?i)(?:aws)?[_-]?secret[_-]?(?:access)?[_-]?key\s*[:=]\s*["']?[A-Za-z0-9/+=]{40}["']?`), "AWS Session Token": regexp.MustCompile(`(?i)aws[_-]?session[_-]?token\s*[:=]\s*["']?[A-Za-z0-9/+=]{100,}["']?`), "MongoDB Connection String": regexp.MustCompile(`mongodb(?:\+srv)?://[a-zA-Z0-9._-]+:[^@\s"']+@[a-zA-Z0-9._-]+`), "PostgreSQL Connection String": regexp.MustCompile(`postgres(?:ql)?://[a-zA-Z0-9._-]+:[^@\s"']+@[a-zA-Z0-9._-]+`), "MySQL Connection String": regexp.MustCompile(`mysql://[a-zA-Z0-9._-]+:[^@\s"']+@[a-zA-Z0-9._-]+`), "Redis Connection String": regexp.MustCompile(`redis://[a-zA-Z0-9._-]+:[^@\s"']+@[a-zA-Z0-9._-]+`), "MSSQL Connection String": regexp.MustCompile(`(?i)(?:server|data source)=[^;]+;.*(?:password|pwd)=[^;]+`), "Database URL Generic": regexp.MustCompile(`(?i)(?:database|db)[_-]?url\s*[:=]\s*["']?[a-z]+://[^:]+:[^@]+@[^\s"']+["']?`), "Azure Client Secret": regexp.MustCompile(`(?i)(?:azure|ad)[_-]?(?:client)?[_-]?secret\s*[:=]\s*["']?[a-zA-Z0-9~._-]{34,}["']?`), "Azure Storage Connection": regexp.MustCompile(`DefaultEndpointsProtocol=https?;AccountName=[^;]+;AccountKey=[a-zA-Z0-9+/=]{86,}`), "Azure SAS Token": regexp.MustCompile(`(?i)[?&]sig=[a-zA-Z0-9%]{43,}`), "Azure SQL Connection": regexp.MustCompile(`(?i)Server=tcp:[^;]+;.*Password=[^;]+`), "DigitalOcean Token": regexp.MustCompile(`dop_v1_[a-f0-9]{64}`), "DigitalOcean OAuth": regexp.MustCompile(`doo_v1_[a-f0-9]{64}`), "DigitalOcean Refresh": regexp.MustCompile(`dor_v1_[a-f0-9]{64}`), "Linode API Token": regexp.MustCompile(`(?i)linode[_-]?(?:api)?[_-]?token\s*[:=]\s*["']?[a-f0-9]{64}["']?`), "Vultr API Key": regexp.MustCompile(`(?i)vultr[_-]?api[_-]?key\s*[:=]\s*["']?[A-Z0-9]{36}["']?`), "Hetzner API Token": regexp.MustCompile(`(?i)hetzner[_-]?(?:api)?[_-]?token\s*[:=]\s*["']?[a-zA-Z0-9]{64}["']?`), "Oracle Cloud API Key": regexp.MustCompile(`(?i)oci[_-]?api[_-]?key\s*[:=]\s*["']?-----BEGIN (?:RSA )?PRIVATE KEY-----`), "IBM Cloud API Key": regexp.MustCompile(`(?i)ibm[_-]?(?:cloud)?[_-]?api[_-]?key\s*[:=]\s*["']?[a-zA-Z0-9_-]{44}["']?`), "NPM Access Token": regexp.MustCompile(`npm_[a-zA-Z0-9]{36}`), "PyPI API Token": regexp.MustCompile(`pypi-[a-zA-Z0-9_-]{100,}`), "NuGet API Key": regexp.MustCompile(`oy2[a-z0-9]{43}`), "RubyGems API Key": regexp.MustCompile(`rubygems_[a-f0-9]{48}`), "CircleCI Token": regexp.MustCompile(`(?i)circle[_-]?(?:ci)?[_-]?token\s*[:=]\s*["']?[a-f0-9]{40}["']?`), "Travis CI Token": regexp.MustCompile(`(?i)travis[_-]?(?:ci)?[_-]?token\s*[:=]\s*["']?[a-zA-Z0-9]{22}["']?`), "Jenkins API Token": regexp.MustCompile(`(?i)jenkins[_-]?(?:api)?[_-]?token\s*[:=]\s*["']?[a-f0-9]{32,}["']?`), "Bitbucket App Password": regexp.MustCompile(`(?i)bitbucket[_-]?(?:app)?[_-]?(?:password|secret)\s*[:=]\s*["']?[a-zA-Z0-9]{18,}["']?`), "Codecov Token": regexp.MustCompile(`(?i)codecov[_-]?token\s*[:=]\s*["']?[a-f0-9-]{36}["']?`), "Vercel Token": regexp.MustCompile(`(?i)vercel[_-]?token\s*[:=]\s*["']?[a-zA-Z0-9]{24}["']?`), "Netlify Token": regexp.MustCompile(`(?i)netlify[_-]?(?:auth)?[_-]?token\s*[:=]\s*["']?[a-zA-Z0-9_-]{40,}["']?`), "Vault Token": regexp.MustCompile(`(?i)(?:vault[_-]?token|hvs)\s*[:=]?\s*["']?(?:hvs\.)?[a-zA-Z0-9_-]{24,}["']?`), "Kubernetes Token": regexp.MustCompile(`eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9\.[a-zA-Z0-9_-]+\.[a-zA-Z0-9_-]+`), "Docker Registry Password": regexp.MustCompile(`(?i)docker[_-]?(?:registry)?[_-]?(?:password|pass|pwd)\s*[:=]\s*["']?[^\s"']{8,}["']?`), "Terraform Cloud Token": regexp.MustCompile(`(?i)(?:tfe|terraform)[_-]?token\s*[:=]\s*["']?[a-zA-Z0-9]{14}\.[a-zA-Z0-9_-]{67}["']?`), "Pulumi Access Token": regexp.MustCompile(`pul-[a-f0-9]{40}`), "Adyen API Key": regexp.MustCompile(`(?i)adyen[_-]?api[_-]?key\s*[:=]\s*["']?AQE[a-zA-Z0-9_-]{50,}["']?`), "Klarna API Key": regexp.MustCompile(`(?i)klarna[_-]?api[_-]?(?:key|secret)\s*[:=]\s*["']?[a-zA-Z0-9_-]{30,}["']?`), "Razorpay Key": regexp.MustCompile(`rzp_(?:live|test)_[a-zA-Z0-9]{14}`), "Coinbase API Secret": regexp.MustCompile(`(?i)coinbase[_-]?(?:api)?[_-]?secret\s*[:=]\s*["']?[a-zA-Z0-9]{64}["']?`), "Binance API Secret": regexp.MustCompile(`(?i)binance[_-]?(?:api)?[_-]?secret\s*[:=]\s*["']?[a-zA-Z0-9]{64}["']?`), "Twilio Auth Token": regexp.MustCompile(`(?i)twilio[_-]?auth[_-]?token\s*[:=]\s*["']?[a-f0-9]{32}["']?`), "Pusher Secret": regexp.MustCompile(`(?i)pusher[_-]?(?:app)?[_-]?secret\s*[:=]\s*["']?[a-f0-9]{20}["']?`), "Vonage API Secret": regexp.MustCompile(`(?i)(?:vonage|nexmo)[_-]?(?:api)?[_-]?secret\s*[:=]\s*["']?[a-zA-Z0-9]{16}["']?`), "Plivo Auth Token": regexp.MustCompile(`(?i)plivo[_-]?auth[_-]?(?:token|id)\s*[:=]\s*["']?[a-zA-Z0-9]{40,}["']?`), "MessageBird API Key": regexp.MustCompile(`(?i)messagebird[_-]?(?:api)?[_-]?key\s*[:=]\s*["']?[a-zA-Z0-9]{25}["']?`), "Intercom Access Token": regexp.MustCompile(`(?i)intercom[_-]?(?:access)?[_-]?token\s*[:=]\s*["']?[a-zA-Z0-9=_-]{60,}["']?`), "Zendesk API Token": regexp.MustCompile(`(?i)zendesk[_-]?(?:api)?[_-]?token\s*[:=]\s*["']?[a-zA-Z0-9]{40}["']?`), "Algolia Admin API Key": regexp.MustCompile(`(?i)algolia[_-]?(?:admin)?[_-]?(?:api)?[_-]?key\s*[:=]\s*["']?[a-f0-9]{32}["']?`), "Elasticsearch API Key": regexp.MustCompile(`(?i)(?:elastic|es)[_-]?(?:api)?[_-]?key\s*[:=]\s*["']?[a-zA-Z0-9_-]{50,}["']?`), "Mixpanel API Secret": regexp.MustCompile(`(?i)mixpanel[_-]?(?:api)?[_-]?secret\s*[:=]\s*["']?[a-f0-9]{32}["']?`), "Amplitude API Key": regexp.MustCompile(`(?i)amplitude[_-]?(?:api)?[_-]?key\s*[:=]\s*["']?[a-f0-9]{32}["']?`), "Segment Write Key Alt": regexp.MustCompile(`(?i)segment[_-]?(?:write)?[_-]?key\s*[:=]\s*["']?[a-zA-Z0-9]{32}["']?`), "New Relic License Key": regexp.MustCompile(`(?i)new[_-]?relic[_-]?license[_-]?key\s*[:=]\s*["']?[a-f0-9]{40}["']?`), "New Relic API Key": regexp.MustCompile(`NRAK-[A-Z0-9]{27}`), "New Relic Insights Key": regexp.MustCompile(`NRI[IQ]-[a-zA-Z0-9_-]{32}`), "Loggly Token": regexp.MustCompile(`(?i)loggly[_-]?(?:customer)?[_-]?token\s*[:=]\s*["']?[a-f0-9-]{36}["']?`), "Splunk HEC Token": regexp.MustCompile(`(?i)splunk[_-]?(?:hec)?[_-]?token\s*[:=]\s*["']?[a-f0-9-]{36}["']?`), "Sumo Logic Access Key": regexp.MustCompile(`(?i)sumo[_-]?logic[_-]?(?:access)?[_-]?(?:key|id)\s*[:=]\s*["']?su[a-zA-Z0-9]{12}["']?`), "Grafana API Key": regexp.MustCompile(`eyJr[a-zA-Z0-9_-]{50,}={0,2}`), "PagerDuty API Key": regexp.MustCompile(`(?i)pagerduty[_-]?(?:api)?[_-]?key\s*[:=]\s*["']?[a-zA-Z0-9+/=_-]{20}["']?`), "Supabase Service Role Key": regexp.MustCompile(`eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9\.[a-zA-Z0-9_-]+\.[a-zA-Z0-9_-]+`), "Firebase Admin SDK Key": regexp.MustCompile(`(?i)firebase[_-]?(?:admin)?[_-]?sdk[_-]?key\s*[:=]\s*["']?[a-zA-Z0-9_-]{100,}["']?`), "Auth0 Client Secret": regexp.MustCompile(`(?i)auth0[_-]?(?:client)?[_-]?secret\s*[:=]\s*["']?[a-zA-Z0-9_-]{64,}["']?`), "Okta API Token Alt": regexp.MustCompile(`(?i)okta[_-]?(?:api)?[_-]?token\s*[:=]\s*["']?00[a-zA-Z0-9_-]{40}["']?`), "Cloudinary Secret": regexp.MustCompile(`(?i)cloudinary[_-]?(?:api)?[_-]?secret\s*[:=]\s*["']?[a-zA-Z0-9_-]{27}["']?`), "Cloudinary URL": regexp.MustCompile(`cloudinary://[0-9]+:[a-zA-Z0-9_-]+@[a-z]+`), "Backblaze Application Key": regexp.MustCompile(`(?i)b2[_-]?(?:application)?[_-]?key\s*[:=]\s*["']?K[a-zA-Z0-9]{30,}["']?`), "Wasabi Access Key": regexp.MustCompile(`(?i)wasabi[_-]?(?:access)?[_-]?key\s*[:=]\s*["']?[A-Z0-9]{20}["']?`), "LaunchDarkly SDK Key": regexp.MustCompile(`(?i)(?:ld)?[_-]?sdk[_-]?key\s*[:=]\s*["']?sdk-[a-f0-9-]{36}["']?`), "LaunchDarkly API Key": regexp.MustCompile(`(?i)launchdarkly[_-]?(?:api)?[_-]?key\s*[:=]\s*["']?api-[a-f0-9-]{36}["']?`), "Split.io API Key": regexp.MustCompile(`(?i)split[_-]?(?:io)?[_-]?(?:api)?[_-]?key\s*[:=]\s*["']?[a-zA-Z0-9]{50,}["']?`), "Statsig Secret": regexp.MustCompile(`(?i)statsig[_-]?(?:secret)?[_-]?key\s*[:=]\s*["']?secret-[a-zA-Z0-9]{50,}["']?`), "GitLab Pipeline Token": regexp.MustCompile(`glptt-[a-f0-9]{40}`), "GitLab Runner Token": regexp.MustCompile(`GR1348941[a-zA-Z0-9_-]{20}`), "GitHub App Private Key": regexp.MustCompile(`-----BEGIN RSA PRIVATE KEY-----[\s\S]+?-----END RSA PRIVATE KEY-----`), "Bitbucket OAuth Secret": regexp.MustCompile(`(?i)bitbucket[_-]?(?:oauth)?[_-]?secret\s*[:=]\s*["']?[a-zA-Z0-9]{32,}["']?`), "Contentful Management Token": regexp.MustCompile(`CFPAT-[a-zA-Z0-9_-]{43}`), "Contentful Delivery Token": regexp.MustCompile(`(?i)contentful[_-]?(?:delivery)?[_-]?token\s*[:=]\s*["']?[a-zA-Z0-9_-]{43}["']?`), "Sanity Token": regexp.MustCompile(`(?i)\bsanity[_-]?(?:api[_-]?)?token\s*[:=]\s*["']?sk[a-zA-Z0-9]{32,}["']?`), "Strapi API Token": regexp.MustCompile(`(?i)strapi[_-]?(?:api)?[_-]?token\s*[:=]\s*["']?[a-f0-9]{256}["']?`), "Postmark Server Token": regexp.MustCompile(`(?i)postmark[_-]?(?:server)?[_-]?token\s*[:=]\s*["']?[a-f0-9-]{36}["']?`), "SparkPost API Key": regexp.MustCompile(`(?i)sparkpost[_-]?(?:api)?[_-]?key\s*[:=]\s*["']?[a-f0-9]{40}["']?`), "Mailjet API Secret": regexp.MustCompile(`(?i)mailjet[_-]?(?:api)?[_-]?secret\s*[:=]\s*["']?[a-f0-9]{32}["']?`), "Mandrill API Key": regexp.MustCompile(`(?i)mandrill[_-]?(?:api)?[_-]?key\s*[:=]\s*["']?[a-zA-Z0-9_-]{22}["']?`), "Customer.io API Key": regexp.MustCompile(`(?i)customer[_-]?io[_-]?(?:api)?[_-]?key\s*[:=]\s*["']?[a-f0-9]{32}["']?`), "Mapbox Secret Token": regexp.MustCompile(`sk\.[a-zA-Z0-9]{60,}\.[a-zA-Z0-9_-]{22,}`), "Here API Key": regexp.MustCompile(`(?i)here[_-]?(?:api)?[_-]?key\s*[:=]\s*["']?[a-zA-Z0-9_-]{43}["']?`), "TomTom API Key": regexp.MustCompile(`(?i)tomtom[_-]?(?:api)?[_-]?key\s*[:=]\s*["']?[a-zA-Z0-9]{32}["']?`), "LinkedIn Client Secret": regexp.MustCompile(`(?i)linkedin[_-]?(?:client)?[_-]?secret\s*[:=]\s*["']?[a-zA-Z0-9]{16}["']?`), "Spotify Client Secret": regexp.MustCompile(`(?i)spotify[_-]?(?:client)?[_-]?secret\s*[:=]\s*["']?[a-f0-9]{32}["']?`), "Dropbox App Secret": regexp.MustCompile(`(?i)dropbox[_-]?(?:app)?[_-]?secret\s*[:=]\s*["']?[a-z0-9]{15}["']?`), "Private Key Inline": regexp.MustCompile(`(?i)(?:private[_-]?key|priv[_-]?key)\s*[:=]\s*["'][a-zA-Z0-9+/=\n]{100,}["']`), "Password Hardcoded": regexp.MustCompile(`(?i)(?:password|passwd|pwd)\s*[:=]\s*["'][^"']{8,50}["']`), "Secret Key Hardcoded": regexp.MustCompile(`(?i)(?:secret[_-]?key|signing[_-]?key|encryption[_-]?key)\s*[:=]\s*["'][a-zA-Z0-9+/=_-]{20,}["']`), } asciiArt = ` ________ __ __ / / __/ / __ _____ / /____ ____ / // /\ \/ _ \/ // / _ \/ __/ -_) __/ \___/___/_//_/\_,_/_//_/\__/\__/_/ ` + version + ` Created by cc1a2b ` ) // progressReader wraps an io.Reader to track download progress type progressReader struct { reader io.Reader total int64 current int64 lastUpdate time.Time onProgress func(int64) } func (pr *progressReader) Read(p []byte) (int, error) { n, err := pr.reader.Read(p) pr.current += int64(n) // Only update progress every 100ms to avoid too many updates if pr.onProgress != nil && time.Since(pr.lastUpdate) > 100*time.Millisecond { pr.onProgress(pr.current) pr.lastUpdate = time.Now() } return n, err } // flagList is a custom type for handling multiple header flags type flagList []string func (f *flagList) String() string { return strings.Join(*f, ", ") } func (f *flagList) Set(value string) error { *f = append(*f, value) return nil } // Config holds all configuration options type Config struct { // Basic options URL, List, JSFile, Output, Regex, Cookies, Proxy string Threads int Quiet, Help, Update, ExtractEndpoints, SkipTLS, FoundOnly bool // Advanced HTTP Headers []string // Custom HTTP headers UserAgent string // Custom User-Agent (single string or randomly selected from file) UserAgents []string // List of User-Agents (when loaded from file) RateLimit int // Delay between requests (ms) Timeout int // Request timeout (seconds) Retry int // Retry failed requests // JS Analysis Deobfuscate, SourceMap, Eval, ObfsDetect bool // Security Analysis Secrets, Tokens, Params, ParamURLs, Internal, GraphQL, Bypass, Firebase, Links bool // Crawling & Scope CrawlDepth int // Recursive JS crawling depth Domain string // Scope to specific domain Ext string // Match specific JS file extensions // Output JSON, CSV, Verbose, Burp bool // v0.6: false-positive pipeline controls. // MinConfidence gates findings; ShowConfidence prints the score inline. // NoFPFilter disables the legacy FP filter (debug only). SelfTest runs // the rule registry against its TP/FP fixtures and exits. MaxBytes caps // response body reads (gzip-bomb defense). AllowInternal opts in to // file://, localhost, and RFC1918 targets — off by default to avoid SSRF. MinConfidence float64 ShowConfidence bool NoFPFilter bool SelfTest bool MaxBytes int64 AllowInternal bool // v0.6+: live verification + observability + extensibility. // Verify enables read-only liveness probes against provider endpoints // (Stripe /v1/balance, GitHub /user, OpenAI /v1/models, Slack auth.test, etc.). // VerifyTimeout bounds each probe; PerHost caps outbound concurrency per host. // Stats prints per-stage counters at end of run. // RulesFile loads an external JSON rule pack at startup. Verify bool VerifyTimeout int PerHost int Stats bool RulesFile string // v0.6++: I/O formats, suppressions, registry introspection, deltas. // SARIF and NDJSON are alternative output modes; IgnoreFile is a // permanent suppression list; DiffFile takes a previous JSON envelope // and reports only new findings; OnlyRules/DisableRule apply a registry // filter; HARFile bypasses the fetcher and reads from a Chrome HAR // archive directly; NoColor disables ANSI color (also auto when stdout // is not a TTY). SARIF bool NDJSON bool IgnoreFile string DiffFile string OnlyRules string DisableRule string HARFile string NoColor bool IgnoreSet *IgnoreList DiffSeen map[string]bool // v0.6+++: page-aware crawling, source maps, cache, robots, concurrent verify. CacheDir string Robots bool InlineHTML bool CSPOrigins bool VerifyWorkers int Cache *DiskCache } func Run() { var ( url, list, jsFile, output, regex, cookies, proxy string threads int quiet, help, update, extractEndpoints, skipTLS, foundOnly bool ) // Advanced HTTP var headers flagList var userAgent string var rateLimit, timeout, retry int // JS Analysis var deobfuscate, sourceMap, eval, obfsDetect bool // Security Analysis var secrets, tokens, params, paramURLs, internal, graphql, bypass, firebase, links bool // Crawling & Scope var crawlDepth int var domain, ext string // Output var jsonOut, csvOut, verbose, burp bool flag.StringVar(&url, "u", "", "Input a URL") flag.StringVar(&url, "url", "", "Input a URL") flag.StringVar(&list, "l", "", "Input a file with URLs (.txt)") flag.StringVar(&list, "list", "", "Input a file with URLs (.txt)") flag.StringVar(&jsFile, "f", "", "Path to JavaScript file") flag.StringVar(&jsFile, "file", "", "Path to JavaScript file") flag.StringVar(&output, "o", "", "Output file path") flag.StringVar(&output, "output", "", "Output file path") flag.StringVar(®ex, "r", "", "RegEx for filtering results (endpoints and sensitive data)") flag.StringVar(®ex, "regex", "", "RegEx for filtering results (endpoints and sensitive data)") flag.StringVar(&cookies, "c", "", "Cookies for authenticated JS files") flag.StringVar(&cookies, "cookies", "", "Cookies for authenticated JS files") flag.StringVar(&proxy, "p", "", "Set proxy (host:port)") flag.StringVar(&proxy, "proxy", "", "Set proxy (host:port)") flag.IntVar(&threads, "t", 5, "Number of concurrent threads") flag.IntVar(&threads, "threads", 5, "Number of concurrent threads") flag.BoolVar(&quiet, "q", false, "Quiet mode: suppress ASCII art output") flag.BoolVar(&quiet, "quiet", false, "Quiet mode: suppress ASCII art output") flag.BoolVar(&help, "h", false, "Display help message") flag.BoolVar(&help, "help", false, "Display help message") flag.BoolVar(&update, "update", false, "Update the tool with latest patterns") flag.BoolVar(&update, "up", false, "Update the tool to latest version") flag.BoolVar(&extractEndpoints, "ep", false, "Extract endpoints from JavaScript files") flag.BoolVar(&extractEndpoints, "end-point", false, "Extract endpoints from JavaScript files") flag.BoolVar(&skipTLS, "k", false, "Skip TLS certificate verification") flag.BoolVar(&skipTLS, "skip-tls", false, "Skip TLS certificate verification") flag.BoolVar(&foundOnly, "fo", false, "Only show results when sensitive data is found (hide MISSING messages)") flag.BoolVar(&foundOnly, "found-only", false, "Only show results when sensitive data is found (hide MISSING messages)") // Advanced HTTP flags flag.Var(&headers, "H", "Custom HTTP headers (repeatable, format: 'Key: Value')") flag.Var(&headers, "header", "Custom HTTP headers (repeatable, format: 'Key: Value')") flag.StringVar(&userAgent, "U", "", "Custom User-Agent string or path to file containing user agents (one per line)") flag.StringVar(&userAgent, "user-agent", "", "Custom User-Agent string or path to file containing user agents (one per line)") flag.IntVar(&rateLimit, "R", 0, "Delay between requests (ms)") flag.IntVar(&rateLimit, "rate-limit", 0, "Delay between requests (ms)") flag.IntVar(&timeout, "T", 30, "Request timeout (seconds)") flag.IntVar(&timeout, "timeout", 30, "Request timeout (seconds)") flag.IntVar(&retry, "y", 2, "Retry failed requests") flag.IntVar(&retry, "retry", 2, "Retry failed requests") // JS Analysis flags flag.BoolVar(&deobfuscate, "d", false, "Deobfuscate minified/obfuscated code") flag.BoolVar(&deobfuscate, "deobfuscate", false, "Deobfuscate minified/obfuscated code") flag.BoolVar(&sourceMap, "m", false, "Parse source maps for original JS") flag.BoolVar(&sourceMap, "sourcemap", false, "Parse source maps for original JS") flag.BoolVar(&eval, "e", false, "Analyze eval() & dynamic code") flag.BoolVar(&eval, "eval", false, "Analyze eval() & dynamic code") flag.BoolVar(&obfsDetect, "z", false, "Detect obfuscation techniques") flag.BoolVar(&obfsDetect, "obfs-detect", false, "Detect obfuscation techniques") // Security Analysis flags flag.BoolVar(&secrets, "s", false, "API keys, tokens, credentials detection") flag.BoolVar(&secrets, "secrets", false, "API keys, tokens, credentials detection") flag.BoolVar(&tokens, "x", false, "JWT/auth tokens extraction") flag.BoolVar(&tokens, "tokens", false, "JWT/auth tokens extraction") flag.BoolVar(¶ms, "P", false, "Hidden parameters discovery") flag.BoolVar(¶ms, "params", false, "Hidden parameters discovery") flag.BoolVar(¶mURLs, "PU", false, "Advanced URL parameter extraction with base URLs") flag.BoolVar(¶mURLs, "param-urls", false, "Advanced URL parameter extraction with base URLs") flag.BoolVar(&internal, "i", false, "Internal/private endpoints only") flag.BoolVar(&internal, "internal", false, "Internal/private endpoints only") flag.BoolVar(&graphql, "g", false, "GraphQL endpoints & queries") flag.BoolVar(&graphql, "graphql", false, "GraphQL endpoints & queries") flag.BoolVar(&bypass, "B", false, "WAF bypass patterns detection") flag.BoolVar(&bypass, "bypass", false, "WAF bypass patterns detection") flag.BoolVar(&firebase, "F", false, "Firebase config/secrets detection") flag.BoolVar(&firebase, "firebase", false, "Firebase config/secrets detection") flag.BoolVar(&links, "L", false, "Extract all links/URLs from JS") flag.BoolVar(&links, "links", false, "Extract all links/URLs from JS") // Crawling & Scope flags flag.IntVar(&crawlDepth, "w", 1, "Recursive JS crawling depth") flag.IntVar(&crawlDepth, "crawl", 1, "Recursive JS crawling depth") flag.StringVar(&domain, "D", "", "Scope to specific domain") flag.StringVar(&domain, "domain", "", "Scope to specific domain") flag.StringVar(&ext, "E", "", "Match specific JS file extensions (comma-separated)") flag.StringVar(&ext, "ext", "", "Match specific JS file extensions (comma-separated)") // Output flags flag.BoolVar(&jsonOut, "j", false, "Structured JSON output") flag.BoolVar(&jsonOut, "json", false, "Structured JSON output") flag.BoolVar(&csvOut, "C", false, "CSV for Excel/Sheets import") flag.BoolVar(&csvOut, "csv", false, "CSV for Excel/Sheets import") flag.BoolVar(&verbose, "v", false, "Detailed analysis output") flag.BoolVar(&verbose, "verbose", false, "Detailed analysis output") flag.BoolVar(&burp, "n", false, "Burp Suite export format") flag.BoolVar(&burp, "burp", false, "Burp Suite export format") // v0.6 — FP pipeline controls var minConfidence float64 var showConfidence, noFPFilter, selfTest, allowInternal bool var maxBytes int64 flag.Float64Var(&minConfidence, "mc", DefaultMinConfidence, "Minimum confidence (0.0-1.0) for a finding to be reported") flag.Float64Var(&minConfidence, "min-confidence", DefaultMinConfidence, "Minimum confidence (0.0-1.0) for a finding to be reported") flag.BoolVar(&showConfidence, "sc", false, "Show confidence score on each printed finding") flag.BoolVar(&showConfidence, "show-confidence", false, "Show confidence score on each printed finding") flag.BoolVar(&noFPFilter, "no-fp-filter", false, "Disable false-positive filter (debug; keep all matches)") flag.BoolVar(&selfTest, "self-test", false, "Run the rule registry against its built-in TP/FP fixtures and exit") flag.Int64Var(&maxBytes, "max-bytes", DefaultMaxBytes, "Cap response body read size in bytes (gzip-bomb defense)") flag.BoolVar(&allowInternal, "allow-internal", false, "Allow file://, localhost, and RFC1918 targets (off by default to prevent SSRF)") // v0.6+ — verifier, stats, extensibility var verify, stats bool var verifyTimeout, perHost int var rulesFile string flag.BoolVar(&verify, "verify", false, "Probe each finding against the provider's read-only endpoint (off by default; opt-in)") flag.IntVar(&verifyTimeout, "verify-timeout", 10, "Timeout in seconds for each verification probe") flag.IntVar(&perHost, "per-host", defaultPerHostConcurrency, "Per-host outbound concurrency cap (avoids getting banned)") flag.BoolVar(&stats, "stats", false, "Print per-stage counters (URLs fetched, FP-drops by reason, findings) on stderr at end of run") flag.StringVar(&rulesFile, "rules-file", "", "Load an external JSON rule pack at startup (additive to built-in registry)") // v0.6++ — I/O formats, suppressions, deltas, registry introspection var sarifOut, ndjsonOut, listRules, noColor bool var explainID, ignoreFile, diffFile, onlyRules, disableRule, harFile string flag.BoolVar(&sarifOut, "sarif", false, "Emit SARIF 2.1.0 (suitable for GitHub code-scanning)") flag.BoolVar(&ndjsonOut, "ndjson", false, "Stream findings as newline-delimited JSON") flag.StringVar(&ignoreFile, "ignore-file", "", "Path to .jshunterignore for permanent suppression") flag.StringVar(&diffFile, "diff", "", "Diff against a previous JSON envelope; only NEW findings reported") flag.BoolVar(&listRules, "list-rules", false, "Print the rule registry as a table and exit") flag.StringVar(&explainID, "explain", "", "Print the full rule definition (incl. TP/FP fixtures) and exit") flag.StringVar(&onlyRules, "only-rules", "", "Comma-separated rule_id patterns; only matching rules run (supports * glob)") flag.StringVar(&disableRule, "disable-rule", "", "Comma-separated rule_id patterns to disable (supports * glob)") flag.StringVar(&harFile, "har", "", "Ingest a Chrome DevTools HAR file instead of fetching URLs") flag.BoolVar(&noColor, "no-color", false, "Disable ANSI color (auto-disabled when stdout is not a TTY)") // v0.6+++ — page-aware crawling, sourcemaps, cache, robots, concurrent verify var cacheDir string var robotsMode, inlineHTML, cspOrigins bool var verifyWorkers int flag.StringVar(&cacheDir, "cache-dir", "", "Persist HTTP responses on disk for ETag-based revalidation") flag.BoolVar(&robotsMode, "robots", false, "Fetch /robots.txt for the target host(s) and print Disallow paths") flag.BoolVar(&inlineHTML, "inline-html", false, "Scan inline