Skip to content
Security
Skill

/hunt-source-leak

Hunt source code and build artifact leakage — JavaScript source maps (.js.map) reconstructing TypeScript/ES6 source, Swagger/OpenAPI JSON endpoint discovery, .env/.git exposure, webpack chunks with hardcoded secrets, robots.txt/security.txt recon, build-info files,

From plugin
claude-bughunter
3.3k82 skills15 commands
Install
$ npx -y skills add elementalsouls/Claude-BugHunter --skill hunt-source-leak --agent claude-code

How it fires

How this skill gets triggered: by you, by Claude, or both.

  • Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
  • You can call itInvoke it directly when you want it.
  • Slash command/hunt-source-leak

Context preview

The summary Claude sees to decide when to auto-load this skill.

Hunt source code and build artifact leakage — JavaScript source maps (.js.map) reconstructing TypeScript/ES6 source, Swagger/OpenAPI JSON endpoint discovery, .env/.git exposure, webpack chunks with hardcoded secrets, robots.txt/security.txt recon, build-info files,

SKILL.md

hunt-source-leak.SKILL.md
name: hunt-source-leak
description: Hunt source code and build artifact leakage — JavaScript source maps (.js.map) reconstructing TypeScript/ES6 source, Swagger/OpenAPI JSON endpoint discovery, .env/.git exposure, webpack chunks with hardcoded secrets, robots.txt/security.txt recon, build-info files, asset-manifest.json API route discovery, .DS_Store file listing. Use at the START of every recon session — these findings often unlock the entire attack surface.
sources: hackerone_public, offensive_research
report_count: 31

HUNT-SOURCE-LEAK — Source Code & Build Artifact Leakage

Crown Jewel Targets

Source map exposing TypeScript source = see all API routes, auth logic, secrets. Swagger/OpenAPI JSON = complete API surface map.

**Highest-value findings:**

  • **`.js.map` source maps** — reconstruct full TypeScript/ES6 source code → find hardcoded API keys, internal endpoints, auth logic bypasses
  • **`swagger.json` / `openapi.json`** — complete REST API specification with all endpoints, parameters, auth schemes, and internal route names
  • **`.env` / `.env.production`** — APP_KEY, DB_PASSWORD, API_KEY, SECRET_KEY in plaintext
  • **`.git/` exposure** — `git clone` the entire source history → all past hardcoded secrets
  • **`asset-manifest.json` / `_next/static/`** — all JS bundle paths → systematic source map discovery
  • **`build-info` / `info.json`** — git commit hash, build timestamp, dependency versions → CVE targeting

---

Phase 1 — Quick Wins (Run First)

# These 10 requests take <30 seconds and often yield Critical findings
for PATH in \
  "/.env" \
  "/.env.production" \
  "/.env.local" \
  "/.git/HEAD" \
  "/swagger.json" \
  "/api/swagger.json" \
  "/v1/swagger.json" \
  "/openapi.json" \
  "/api/openapi.json" \
  "/api-docs"; do
  STATUS=$(curl -s -o /tmp/sl_test -w "%{http_code}" "https://$TARGET$PATH")
  if [ "$STATUS" = "200" ]; then
    echo "[+] HIT: https://$TARGET$PATH"
    head -5 /tmp/sl_test
    echo "---"
  fi
done

---

Phase 2 — Source Map Discovery

> **Always resolve the CURRENT build hash before testing, and again before > re-verifying.** Bundle filenames are content-hashed, so they rotate on every > deploy. A `.map` URL recorded yesterday can 404 today while the map is still > fully exposed under a new name. **A 404 at the old URL is not remediation** — > it is a new build. > > ```bash > # ALWAYS derive the hash live, never reuse a recorded URL > HASH=$(curl -s "https://$TARGET/" | grep -oE 'main\.[a-f0-9]+\.js' | head -1) > curl -s -o /dev/null -w '%{http_code} %{size_download} %{content_type}\n' \ > "https://$TARGET/static/js/${HASH}.map" > ``` > > **Lesson from an authorized engagement.** A large production map was found at > `main.<hashA>.js.map`. On re-verification that URL returned a small HTML > soft-404 and the finding was nearly closed as fixed. The bundle had rotated to > `main.<hashB>.js` — and the map was still published at `main.<hashB>.js.map`, > same size. Nothing had been remediated. > > Tell the client this explicitly in the report: **redeploying does not fix source > map exposure.** Only `GENERATE_SOURCEMAP=false` (or stripping `.map` at deploy) > plus a CDN purge closes it. A team that redeploys and re-checks the old link > will wrongly declare victory. > > Same rule applies to any content-hashed artifact: chunk files, CSS maps, > `asset-manifest.json`, and staging equivalents.

# Step 1: Get asset manifest to find all JS bundle paths
curl -s "https://$TARGET/asset-manifest.json" | python3 -m json.tool 2>/dev/null
curl -s "https://$TARGET/static/js/main.*.js" 2>/dev/null | head -3

# Next.js
BUILD_ID=$(curl -s https://$TARGET/ | grep -oP '"buildId":"\K[^"]+')
curl -s "https://$TARGET/_next/static/$BUILD_ID/_buildManifest.js" | head -5

# Step 2: For each JS bundle, check for source map reference at end of file
for JS_URL in $(curl -s https://$TARGET/ | grep -oP 'src="[^"]*\.js"' | sed 's/src="//;s/"//'); do
  LAST_LINE=$(curl -s "https://$TARGET$JS_URL" | tail -1)
  echo "$LAST_LINE" | grep -q "sourceMappingURL" && echo "[+] Source map: $JS_URL"
done

# Step 3: Download and reconstruct source from .map files
JS_URL="https://$TARGET/static/js/main.abc123.js"
MAP_URL="${JS_URL}.map"
curl -s "$MAP_URL" | python3 -c "
import sys, json, os
data = json.load(sys.stdin)
sources = data.get('sources', [])
contents = data.get('sourcesContent', [])
for i, (src, content) in enumerate(zip(sources, contents)):
    if content:
        path = '/tmp/sourcemap_extract/' + src.replace('../','').replace('./',''). replace('webpack://','')
        os.makedirs(os.path.dirname(path), exist_ok=True)
        with open(path, 'w') as f:
            f.write(content)
        print(f'[+] Extracted: {src}')
"

# Step 4: Grep extracted source for secrets
grep -r "API_KEY\|SECRET\|PASSWORD\|TOKEN\|PRIVATE" /tmp/sourcemap_extract/ 2>/dev/null
grep -r "process\.env\." /tmp/sourcemap_extract/ 2>/dev/null | grep -v "NEXT_PUBLIC_" | head -20
grep -r "http://internal\|localhost\|127\.0\.0\.1\|10\.\|172\.\|192\.168" /tmp/sourcemap_extract/ 2>/dev/null | head -20

---

Phase 3 — Swagger / OpenAPI Discovery

# Common paths
SWAGGER_PATHS=(
  "/swagger.json" "/swagger.yaml" "/swagger/"
  "/api/swagger.json" "/api/swagger.yaml"
  "/v1/swagger.json" "/v2/swagger.json" "/v3/swagger.json"
  "/openapi.json" "/openapi.yaml"
  "/api/openapi.json" "/api-docs" "/api-docs.json"
  "/api/v1/swagger.json" "/api/v2/swagger.json"
  "/rest/swagger.json" "/rest/api-docs"
  "/.well-known/openapi.json"
  "/graphql/schema.json"
)

for PATH in "${SWAGGER_PATHS[@]}"; do
  STATUS=$(curl -s -o /tmp/swagger_test -w "%{http_code}" "https://$TARGET$PATH")
  if [ "$STATUS" = "200" ]; then
    echo "[+] Found: https://$TARGET$PATH"
    # Extract all API paths from swagger
    python3 -c "
import sys, json
try:
    d = json.load(open('/tmp/swagger_test'))
    paths = list(d.get('paths', {}).keys())
    print(f'Endpoints: {len(paths)}')
    print('\n'.join(sorted(paths)
Read more
Ships withclaude-bughunter

A self-contained Claude skill bundle for bug hunting and external red-team work · 82 skills · 15 slash commands · 681 disclosed-report patterns across 24 core vulnerability classes · enterprise identity + infrastructure attack matrices · engagement-folder

Get the whole plugin

Other skills on claude-bughunter.