Modern web apps ship a lot of code and config to the browser. When credentials
leak into that client-visible surface — hardcoded in JavaScript, tucked into HTML
meta/data-* attributes or comments, or served as raw source/config/deploy
files that were never meant to be public — anyone can read them with curl and a
browser. This skill is a defensive, read-only workflow for finding that class
of exposure on a web app you are authorized to assess.
It maps to OWASP A02:2021 Cryptographic Failures (sensitive data exposure), A05:2021 Security Misconfiguration, and CWE-798 (hardcoded credentials), CWE-200 (sensitive information exposure), CWE-540 (source code in a production build). It only fetches resources the server already hands to any anonymous visitor — it does not exploit, brute-force, or mutate anything.
Set the target once. Every command below reads only what the server serves publicly.
BASE="https://TARGET.example" # authorized target, no trailing path
WORK="$(mktemp -d)"; cd "$WORK"
curl -s -D headers.txt -o body.html "$BASE/"
cat headers.txt
Flag on the headers:
access-control-allow-origin: * — permissive CORS (worse when paired with
credentials).Content-Security-Policy, X-Frame-Options/frame-ancestors,
X-Content-Type-Options: nosniff, Referrer-Policy, Permissions-Policy.Strict-Transport-Security.Server/framework version banners that fingerprint the stack.grep -inE "secret|passwd|password|api[_-]?key|apikey|token|bearer|authorization|\
akia|sk_live|sk_test|pk_live|whsec_|ghp_|aiza|private[_-]?key|mongodb(\+srv)?://|\
data-[a-z-]*(secret|token|key|access)" body.html
grep -inE "<!--" body.html # read every HTML comment
grep -ioE '<meta[^>]+>' body.html # meta tags often carry keys/ids
grep -ioE '<script[^>]+src="[^"]+"' body.html # enumerate JS bundles
Secrets hide in data-* attributes, <meta> tags, hidden <div>s, and
<!-- comments --> at least as often as in scripts.
# extract script srcs, resolve relative paths against $BASE, fetch and scan
grep -ioE 'src="[^"]+\.js"' body.html | sed -E 's/^src="//; s/"$//' \
| while read -r p; do
u="$p"; case "$p" in http*) ;; /*) u="$BASE$p";; *) u="$BASE/$p";; esac
f="js_$(echo "$p" | tr '/:' '__')"
curl -s "$u" -o "$f" && echo "== $u =="
done
grep -rinE "secret|password|api[_-]?key|token|bearer|sk_(live|test)|pk_(live|test)|\
whsec_|akia|aiza|jwt|signing[_-]?key|admin[_-]?token|mongodb|redis://" js_* 2>/dev/null
Also scan any sourcemaps (*.js.map) — they can rebuild original source with
comments intact.
SPAs often have a catch-all rewrite that returns index.html for unknown paths,
so compare response sizes — a path whose size differs from the SPA fallback
is a real, distinct file.
FALLBACK=$(curl -s "$BASE/____nope____$RANDOM" | wc -c) # SPA fallback size
for p in /.env /.env.local /.env.production /.git/config /.git/HEAD \
/package.json /package-lock.json /vercel.json /.vercel/project.json \
/Dockerfile /docker-compose.yml /wrangler.toml /.gitignore \
/server/index.js /src/config/app.config.js /config.js \
/src/services/payment.service.js /webpack.config.js /next.config.js; do
read -r code size < <(curl -s -o /dev/null -w "%{http_code} %{size_download}" "$BASE$p")
[ "$code" = "200" ] && [ "$size" != "$FALLBACK" ] && echo "REAL FILE $code $size $p"
done
For any real file found, fetch it and re-run the Step 2/3 secret grep. Follow
require(...)/import paths inside those files to discover more source files
(routes, controllers, services, webhooks) and repeat.
Rate each finding by blast radius, not by where it was found:
| Severity | Examples |
|---|---|
| Critical | Live provider secret keys (sk_live_, cloud AKIA…+secret, DB URI with password, private signing/JWT secret, admin bearer token) reachable anonymously |
| High | Server-side source/config/deploy files exposed; test-mode secret keys; internal service tokens; webhook signing secrets |
| Medium | CORS *, missing CSP/security headers, verbose banners, weak randomness for security values (Math.random() for tokens/refs) |
| Low / Info | Public keys correctly client-side, analytics IDs, non-secret config, stack fingerprinting |
A key being "test/demo" does not make it safe if the pattern would ship a live key the same way — report the pattern.
Write findings as Markdown using the format in
references/example-report.md: one row/section per finding with
Severity · Category (OWASP/CWE) · Location · Evidence (redacted) · Impact · Remediation. Redact real secret material to a prefix + length. End with
prioritized remediation and a note on which secrets must be rotated, not just
removed (anything committed/served is already compromised).
BASE="https://demo-for-opensource.vercel.app"; curl -s "$BASE/" -o body.html
grep -inE "recaptcha-secret|data-aws-secret|data-webhook-secret|mongodb\+srv" body.html
# -> meta recaptcha-secret=..., data-aws-access=AKIA…/data-aws-secret=…,
# data-webhook-secret=whsec_…, and a hidden JSON blob with a mongodb+srv URI
# (username:password@cluster). All reachable with a single unauthenticated GET.
curl -s "$BASE/public/assets/js/config.js" | \
grep -inE "API_SECRET|ADMIN_TOKEN|JWT_SECRET|DATABASE_PASSWORD|PAYMENT_SIGNING_KEY"
# -> API_SECRET, ADMIN_TOKEN (JWT), JWT_SECRET, DATABASE_PASSWORD, and a payment
# signing key, all assigned as plain string constants shipped to every browser.
FALLBACK=$(curl -s "$BASE/__nope__" | wc -c)
for p in /server/index.js /docker-compose.yml /src/services/payment.service.js; do
sz=$(curl -s "$BASE$p" | wc -c); [ "$sz" != "$FALLBACK" ] && echo "REAL $sz $p"
done
# -> docker-compose.yml leaks a Redis password; payment.service.js leaks
# Stripe/Khalti secret keys + webhook secret. Distinct sizes prove they are
# real files, not the SPA catch-all page.
A full worked report for this target is in references/example-report.md.
require/import chains in any exposed source file to find more files.@web-security-testing.GET requests for resources
the server already serves publicly — no exploitation, brute force, or mutation.data-* attributes or comments.
Solution: Also scan <meta>, data-*, hidden elements, and every
<!-- comment -->, not just <script> bodies.@web-security-testing — broader OWASP Top 10 workflow; use for injection,
auth, and access-control testing this skill does not cover.@dependency-management-deps-audit — pairs well for supply-chain/component risk
once client exposure is triaged.