Docs

How Secret Detection Works

AIExposureTool scans your publicly accessible JavaScript bundles for 17 known API key and secret patterns. Here's exactly how it works, what it finds, and what to do if a key is detected.

How the scan works

1

Fetch homepage HTML

The scanner requests your homepage URL and downloads the full HTML response.

2

Extract all JavaScript bundle URLs

All <script src='...'> tags are parsed. Both inline and external scripts are collected, including lazy-loaded chunks referenced in the bundle manifest.

3

Fetch each JavaScript bundle

Each bundle file is downloaded in full — these are the same files your users' browsers download when they visit your site.

4

Scan against 17 regex patterns

The full content of each file is scanned against 17 regular expression patterns for known secret formats — Stripe, OpenAI, Supabase, AWS, and 13 more.

5

Report matches with context

Any match is reported with the file it was found in and the line context (with the key value partially redacted in the UI).

Passive only: The scan is completely read-only. No login attempts, no form submissions, no fuzzing. It only reads files that are already publicly accessible — the same files any attacker (or employee) could download.

Why AI-built apps are especially vulnerable

When a developer prompts Cursor, Claude, or ChatGPT to "add Stripe payments" or "connect to Supabase", the AI often generates working code quickly — but may write the API key directly into the component:

// AI-generated code — key hardcoded in client component
const stripe = loadStripe('sk_live_abc123...');  // ❌ secret key, not publishable key

const supabase = createClient(url, 'eyJhbGciOiJIUzI1NiJ9...');  // ❌ service_role key

This code works perfectly — but the secret key ends up in the JS bundle that browsers download. Anyone who opens devtools, or any automated scanner, can extract it in seconds.

All 17 detected patterns

Secret Type
Severity
If exposed

Stripe secret key

sk_live_[a-zA-Z0-9]{24,}
Critical
Full Stripe account access — create refunds, modify subscriptions, charge arbitrary amounts

Stripe webhook secret

whsec_[a-zA-Z0-9+/]{32,}
High
Can forge webhook events, bypass payment verification

OpenAI API key

sk-[a-zA-Z0-9]{32,}
Critical
Full API access — can drain quota, access fine-tuned models, incur large bills

Anthropic / Claude key

sk-ant-[a-zA-Z0-9\-]{32,}
Critical
Full Claude API access — quota drain, billing abuse

Google Gemini key

AIza[a-zA-Z0-9\-_]{35}
Critical
Access to Gemini, Maps, Firebase, and other Google APIs

Supabase service role key

eyJ[a-zA-Z0-9]{100,} (JWT with role:service_role)
Critical
Full database read/write/delete — bypasses Row Level Security entirely

AWS access key

AKIA[A-Z0-9]{16}
Critical
Access to AWS services — S3, EC2, Lambda, IAM — depending on attached policy

Razorpay key

rzp_(live|test)_[a-zA-Z0-9]{14}
High
Razorpay payment account access

Twilio auth token

[a-f0-9]{32} (near Twilio account SID pattern)
High
Send SMS/calls at account owner's expense, access call logs

SendGrid API key

SG\.[a-zA-Z0-9\-_]{22}\.[a-zA-Z0-9\-_]{43}
High
Send email as your domain, access contact lists, damage sender reputation

GitHub personal access token

gh[pousr]_[a-zA-Z0-9]{36}
High
Repository read/write, user data access depending on token scopes

Slack bot token

xoxb-[0-9a-zA-Z\-]{50,}
High
Post messages as your bot, access workspace data

Mailgun API key

key-[a-zA-Z0-9]{32}
High
Send email, access mailing lists, domain settings

HubSpot API key

[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}
Medium
CRM access — contacts, companies, deals, email sequences

Cloudflare API token

[a-zA-Z0-9_\-]{40}
High
DNS changes, firewall rules, zone settings depending on token scope

Intercom API key

dG9rOm[a-zA-Z0-9]{40,}
Medium
Customer conversation access, contact data, send messages

Braintree access token

access_token\$production\$[a-z0-9]{16}\$[a-f0-9]{32}
High
Payment processing access

FAQ

How does the scanner access my JavaScript bundles?

The scanner fetches your homepage HTML, parses all <script src='...'> tags, then fetches each JavaScript bundle file. This is exactly what a browser does — the scanner sees only publicly accessible files.

What if my key is minified — does the scanner still find it?

Yes. Minification changes variable names and whitespace but does not change string values. API keys are string literals that survive minification unchanged. The regex patterns match against the raw bundle content regardless of minification.

I got a false positive — the scanner found something that isn't a real key. What do I do?

False positives can occur when a string matches the format of a key pattern but isn't actually a secret (e.g., a UUID that resembles a HubSpot key). If you're confident it's a false positive, verify by checking whether the string is actually used as an API credential in your code. If it is a real key but it's public-intentional (e.g., a Stripe publishable key, not a secret key), that's expected — publishable keys are designed to be public.

My Stripe publishable key (pk_live_...) was flagged — is that a problem?

No — Stripe publishable keys (pk_live_...) are designed to be public and used in client-side code. AIExposureTool specifically checks for secret keys (sk_live_...) and webhook secrets (whsec_...), which should never be client-side. If you see a publishable key flagged, check that it isn't being reported as a secret key.

What should I do immediately if a secret key is found?

1. Rotate the key immediately in the provider's dashboard (Stripe, OpenAI, Supabase, etc.). 2. Check your provider's access logs for any unauthorized usage. 3. Move the key to a server-side environment variable. 4. Redeploy your site. 5. Verify the key no longer appears in your JS bundles by re-scanning.

Scan your site for exposed secrets

Free — no signup. Results in 15 seconds.

How Secret Detection Works — API Key Leak Detection in JavaScript Bundles | AIExposureTool | AIExposureTool