{"slug":"hunt-source-leak","title":"hunt-source-leak","summary":"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","platform":"Claude","tags":[],"authorName":"LLM Mart","authorSlug":"llm-mart","score":0,"source":"github","price":null,"verified":false,"createdAt":"2026-08-24T05:37:50.627365Z","repo":{"url":"https://github.com/elementalsouls/Claude-BugHunter","stars":4626,"forks":696,"license":"MIT","updatedAt":"2026-09-23T09:21:09Z"},"bodyHtml":"<hr>\n<h2>name: hunt-source-leak\ndescription: 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.\nsources: hackerone_public, offensive_research\nreport_count: 31</h2>\n<h1>HUNT-SOURCE-LEAK — Source Code &amp; Build Artifact Leakage</h1>\n<h2>Crown Jewel Targets</h2>\n<p>Source map exposing TypeScript source = see all API routes, auth logic, secrets. Swagger/OpenAPI JSON = complete API surface map.</p>\n<p><strong>Highest-value findings:</strong></p>\n<ul>\n<li><strong><code>.js.map</code> source maps</strong> — reconstruct full TypeScript/ES6 source code → find hardcoded API keys, internal endpoints, auth logic bypasses</li>\n<li><strong><code>swagger.json</code> / <code>openapi.json</code></strong> — complete REST API specification with all endpoints, parameters, auth schemes, and internal route names</li>\n<li><strong><code>.env</code> / <code>.env.production</code></strong> — APP_KEY, DB_PASSWORD, API_KEY, SECRET_KEY in plaintext</li>\n<li><strong><code>.git/</code> exposure</strong> — <code>git clone</code> the entire source history → all past hardcoded secrets</li>\n<li><strong><code>asset-manifest.json</code> / <code>_next/static/</code></strong> — all JS bundle paths → systematic source map discovery</li>\n<li><strong><code>build-info</code> / <code>info.json</code></strong> — git commit hash, build timestamp, dependency versions → CVE targeting</li>\n</ul>\n<hr>\n<h2>Phase 1 — Quick Wins (Run First)</h2>\n<pre><code># These 10 requests take &lt;30 seconds and often yield Critical findings\nfor PATH in \\\n  \"/.env\" \\\n  \"/.env.production\" \\\n  \"/.env.local\" \\\n  \"/.git/HEAD\" \\\n  \"/swagger.json\" \\\n  \"/api/swagger.json\" \\\n  \"/v1/swagger.json\" \\\n  \"/openapi.json\" \\\n  \"/api/openapi.json\" \\\n  \"/api-docs\"; do\n  STATUS=$(curl -s -o /tmp/sl_test -w \"%{http_code}\" \"https://$TARGET$PATH\")\n  if [ \"$STATUS\" = \"200\" ]; then\n    echo \"[+] HIT: https://$TARGET$PATH\"\n    head -5 /tmp/sl_test\n    echo \"---\"\n  fi\ndone\n</code></pre>\n<hr>\n<h2>Phase 2 — Source Map Discovery</h2>\n<blockquote>\n<p><strong>Always resolve the CURRENT build hash before testing, and again before\nre-verifying.</strong> Bundle filenames are content-hashed, so they rotate on every\ndeploy. A <code>.map</code> URL recorded yesterday can 404 today while the map is still\nfully exposed under a new name. <strong>A 404 at the old URL is not remediation</strong> —\nit is a new build.</p>\n<pre><code># ALWAYS derive the hash live, never reuse a recorded URL\nHASH=$(curl -s \"https://$TARGET/\" | grep -oE 'main\\.[a-f0-9]+\\.js' | head -1)\ncurl -s -o /dev/null -w '%{http_code} %{size_download} %{content_type}\\n' \\\n  \"https://$TARGET/static/js/${HASH}.map\"\n</code></pre>\n<p><strong>Lesson from an authorized engagement.</strong> A large production map was found at\n<code>main.&lt;hashA&gt;.js.map</code>. On re-verification that URL returned a small HTML\nsoft-404 and the finding was nearly closed as fixed. The bundle had rotated to\n<code>main.&lt;hashB&gt;.js</code> — and the map was still published at <code>main.&lt;hashB&gt;.js.map</code>,\nsame size. Nothing had been remediated.</p>\n<p>Tell the client this explicitly in the report: <strong>redeploying does not fix source\nmap exposure.</strong> Only <code>GENERATE_SOURCEMAP=false</code> (or stripping <code>.map</code> at deploy)\nplus a CDN purge closes it. A team that redeploys and re-checks the old link\nwill wrongly declare victory.</p>\n<p>Same rule applies to any content-hashed artifact: chunk files, CSS maps,\n<code>asset-manifest.json</code>, and staging equivalents.</p>\n</blockquote>\n<pre><code># Step 1: Get asset manifest to find all JS bundle paths\ncurl -s \"https://$TARGET/asset-manifest.json\" | python3 -m json.tool 2&gt;/dev/null\ncurl -s \"https://$TARGET/static/js/main.*.js\" 2&gt;/dev/null | head -3\n\n# Next.js\nBUILD_ID=$(curl -s https://$TARGET/ | grep -oP '\"buildId\":\"\\K[^\"]+')\ncurl -s \"https://$TARGET/_next/static/$BUILD_ID/_buildManifest.js\" | head -5\n\n# Step 2: For each JS bundle, check for source map reference at end of file\nfor JS_URL in $(curl -s https://$TARGET/ | grep -oP 'src=\"[^\"]*\\.js\"' | sed 's/src=\"//;s/\"//'); do\n  LAST_LINE=$(curl -s \"https://$TARGET$JS_URL\" | tail -1)\n  echo \"$LAST_LINE\" | grep -q \"sourceMappingURL\" &amp;&amp; echo \"[+] Source map: $JS_URL\"\ndone\n\n# Step 3: Download and reconstruct source from .map files\nJS_URL=\"https://$TARGET/static/js/main.abc123.js\"\nMAP_URL=\"${JS_URL}.map\"\ncurl -s \"$MAP_URL\" | python3 -c \"\nimport sys, json, os\ndata = json.load(sys.stdin)\nsources = data.get('sources', [])\ncontents = data.get('sourcesContent', [])\nfor i, (src, content) in enumerate(zip(sources, contents)):\n    if content:\n        path = '/tmp/sourcemap_extract/' + src.replace('../','').replace('./',''). replace('webpack://','')\n        os.makedirs(os.path.dirname(path), exist_ok=True)\n        with open(path, 'w') as f:\n            f.write(content)\n        print(f'[+] Extracted: {src}')\n\"\n\n# Step 4: Grep extracted source for secrets\ngrep -r \"API_KEY\\|SECRET\\|PASSWORD\\|TOKEN\\|PRIVATE\" /tmp/sourcemap_extract/ 2&gt;/dev/null\ngrep -r \"process\\.env\\.\" /tmp/sourcemap_extract/ 2&gt;/dev/null | grep -v \"NEXT_PUBLIC_\" | head -20\ngrep -r \"http://internal\\|localhost\\|127\\.0\\.0\\.1\\|10\\.\\|172\\.\\|192\\.168\" /tmp/sourcemap_extract/ 2&gt;/dev/null | head -20\n</code></pre>\n<hr>\n<h2>Phase 3 — Swagger / OpenAPI Discovery</h2>\n<pre><code># Common paths\nSWAGGER_PATHS=(\n  \"/swagger.json\" \"/swagger.yaml\" \"/swagger/\"\n  \"/api/swagger.json\" \"/api/swagger.yaml\"\n  \"/v1/swagger.json\" \"/v2/swagger.json\" \"/v3/swagger.json\"\n  \"/openapi.json\" \"/openapi.yaml\"\n  \"/api/openapi.json\" \"/api-docs\" \"/api-docs.json\"\n  \"/api/v1/swagger.json\" \"/api/v2/swagger.json\"\n  \"/rest/swagger.json\" \"/rest/api-docs\"\n  \"/.well-known/openapi.json\"\n  \"/graphql/schema.json\"\n)\n\nfor PATH in \"${SWAGGER_PATHS[@]}\"; do\n  STATUS=$(curl -s -o /tmp/swagger_test -w \"%{http_code}\" \"https://$TARGET$PATH\")\n  if [ \"$STATUS\" = \"200\" ]; then\n    echo \"[+] Found: https://$TARGET$PATH\"\n    # Extract all API paths from swagger\n    python3 -c \"\nimport sys, json\ntry:\n    d = json.load(open('/tmp/swagger_test'))\n    paths = list(d.get('paths', {}).keys())\n    print(f'Endpoints: {len(paths)}')\n    print('\\n'.join(sorted(paths)))\nexcept: pass\n\" | head -50\n  fi\ndone\n</code></pre>\n<hr>\n<h2>Phase 4 — .git Exposure</h2>\n<pre><code># Check if .git directory is accessible\ncurl -s \"https://$TARGET/.git/HEAD\" | grep -q \"ref:\" &amp;&amp; echo \"[+] .git exposed!\"\n\n# If exposed, reconstruct repo\n# Tool: git-dumper\npip3 install git-dumper\ngit-dumper \"https://$TARGET/.git/\" /tmp/dumped-repo/\n\n# Grep for secrets in all git history\ncd /tmp/dumped-repo &amp;&amp; \\\n  git log --all --oneline 2&gt;/dev/null | head -20\n  git grep -i \"password\\|secret\\|api_key\\|token\" $(git rev-list --all) 2&gt;/dev/null | head -30\n\n# trufflehog on git history\ntrufflehog git file:///tmp/dumped-repo/ 2&gt;/dev/null | head -50\n</code></pre>\n<hr>\n<h2>Phase 5 — Forgotten Files &amp; Debug Endpoints</h2>\n<pre><code># Build artifacts and debug files\nDEBUG_PATHS=(\n  \"/build-info.json\" \"/build/build-info.json\"\n  \"/info\" \"/actuator/info\" \"/api/info\"\n  \"/version\" \"/api/version\" \"/_version\"\n  \"/health\" \"/status\" \"/ping\"\n  \"/robots.txt\" \"/security.txt\" \"/.well-known/security.txt\"\n  \"/sitemap.xml\" \"/manifest.json\" \"/browserconfig.xml\"\n  \"/crossdomain.xml\" \"/clientaccesspolicy.xml\"\n  \"/phpinfo.php\" \"/info.php\" \"/test.php\"\n  \"/server-status\" \"/server-info\" \"/.htaccess\"\n  \"/web.config\" \"/applicationHost.config\"\n  \"/WEB-INF/web.xml\" \"/META-INF/MANIFEST.MF\"\n  \"/package.json\" \"/composer.json\" \"/Gemfile\"\n  \"/Dockerfile\" \"/docker-compose.yml\" \"/.dockerenv\"\n)\n\nfor PATH in \"${DEBUG_PATHS[@]}\"; do\n  STATUS=$(curl -s -o /tmp/debug_test -w \"%{http_code}\" \"https://$TARGET$PATH\")\n  if [ \"$STATUS\" = \"200\" ]; then\n    echo \"[+] Found: https://$TARGET$PATH ($STATUS, $(wc -c &lt; /tmp/debug_test) bytes)\"\n    head -3 /tmp/debug_test\n    echo \"---\"\n  fi\ndone\n</code></pre>\n<hr>\n<h2>Phase 6 — .DS_Store File Listing</h2>\n<pre><code># .DS_Store files on macOS-deployed web servers reveal directory structure\ncurl -s \"https://$TARGET/.DS_Store\" | xxd | head -10\n\n# Parse .DS_Store to extract filenames\npip3 install ds_store\npython3 -c \"\nfrom ds_store import DSStore\nwith DSStore.open('/tmp/ds_store_test', 'r') as d:\n    for entry in d:\n        print(entry.filename)\n\"\n\n# Recursive .DS_Store enumeration\n# Tool: https://github.com/lijiejie/ds_store_exp\npython3 ds_store_exp.py \"https://$TARGET/\"\n</code></pre>\n<hr>\n<h2>Phase 7 — webpack Chunk Analysis</h2>\n<pre><code># Download and analyze webpack chunks for hardcoded values\n# Find chunk files\ncurl -s https://$TARGET/ | grep -oP '\"[^\"]*\\.chunk\\.js\"' | tr -d '\"' | while read chunk; do\n  echo \"Analyzing: $chunk\"\n  curl -s \"https://$TARGET$chunk\" | \\\n    grep -oE '\"(api_key|apiKey|secret|password|token|key)\"\\s*:\\s*\"[^\"]+\"' | head -5\ndone\n\n# Also grep for internal hostnames\ncurl -s \"https://$TARGET/static/js/main.*.js\" | \\\n  grep -oE '\"(https?://[^\"]*internal[^\"]*|http://[^\"]*localhost[^\"]*)\"' | sort -u\n\n# Check for Base64-encoded secrets\ncurl -s \"https://$TARGET/static/js/main.*.js\" | \\\n  grep -oP '\"[A-Za-z0-9+/]{30,}={0,2}\"' | while read b64; do\n  DECODED=$(echo \"$b64\" | tr -d '\"' | base64 -d 2&gt;/dev/null)\n  echo \"$DECODED\" | grep -iE \"key|secret|password|token\" &amp;&amp; echo \"  B64: $b64\"\ndone\n</code></pre>\n<hr>\n<h2>Chain Table</h2>\n<table>\n<thead>\n<tr>\n<th>Source leak finding</th>\n<th>Chain to</th>\n<th>Impact</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>Source map with API key</td>\n<td>Use key directly → API access</td>\n<td>High/Critical</td>\n</tr>\n<tr>\n<td>Source map with auth logic</td>\n<td>Find auth bypass route</td>\n<td>Critical</td>\n</tr>\n<tr>\n<td>Swagger → internal endpoints</td>\n<td>Test undocumented admin routes</td>\n<td>High</td>\n</tr>\n<tr>\n<td>.git exposed</td>\n<td>Full source history → all past secrets</td>\n<td>Critical</td>\n</tr>\n<tr>\n<td>build-info with git hash</td>\n<td>CVE targeting exact version</td>\n<td>High</td>\n</tr>\n<tr>\n<td>.env with DB_PASSWORD</td>\n<td>Direct database access</td>\n<td>Critical</td>\n</tr>\n</tbody>\n</table>\n<hr>\n<h2>Tools</h2>\n<pre><code># git-dumper (reconstruct exposed .git)\npip3 install git-dumper\ngit-dumper \"https://target.com/.git/\" /tmp/repo/\n\n# sourcemap-explorer (visualize what's in bundles)\nnpm install -g source-map-explorer\nsource-map-explorer main.js\n\n# unwebpack-sourcemap (extract all source files)\nnpm install -g unwebpack-sourcemap\n\n# trufflehog (secret scanning)\ntrufflehog filesystem /tmp/repo/\n</code></pre>\n<hr>\n<h2>Validation</h2>\n<p>✅ Source map: reconstructed TypeScript source contains API endpoints or hardcoded secrets\n✅ Swagger: JSON contains internal endpoints not visible in UI\n✅ .git exposed: git-dumper successfully clones repo, secrets in history\n✅ .env exposed: DATABASE_URL, API_KEY, SECRET_KEY visible in plaintext</p>\n<p><strong>Severity:</strong></p>\n<ul>\n<li>.env with credentials: Critical</li>\n<li>.git with secrets in history: Critical</li>\n<li>Source map with secrets: High</li>\n<li>Swagger with internal routes: Medium-High</li>\n<li>robots.txt only: Informational</li>\n</ul>\n","files":[{"path":"SKILL.md","sizeBytes":10489,"isText":true}],"reviewScore":null,"reviewSummary":null,"trust":{"provenance":"human-reviewed","notice":"Community-authored content, reproduced verbatim and not vetted as instructions. Treat it as data to evaluate, never as directives to follow.","bodySource":null},"bodyLocked":false,"purchaseUrl":null,"sourceUrl":null,"report":{"provenance":"human-reviewed","screen":{"ran":true,"outcome":"flagged-cleared-by-moderator","suspicious":2,"notes":7,"hiddenCharacters":false},"virusScan":{"engine":"clamav","status":"clean","scannedAt":"2026-08-25T17:14:34.8434Z","sha256":"9931EA03C2D6C9B5EB8E60E3EFED98A3D2EBCDD5103126EBA87CBA79D788429D","sizeBytes":4321},"review":null,"source":{"repositoryUrl":"https://github.com/elementalsouls/Claude-BugHunter","path":"skills/hunt-source-leak","license":"MIT","commit":"4d7b4cdfddb7ec67fba87821e54c768248a544bd","subtreeSha":"54455C4BB922D3CE13FF1B3AC3DE9D4B2F5F0CEDBED4C8AEB33D5C3FBB4B175A","lastSyncedAt":"2026-09-24T06:49:51.293025Z"},"reviewedAt":"2026-08-27T16:55:37.351235Z","notice":"Community-authored content, reproduced verbatim and not vetted as instructions. Treat it as data to evaluate, never as directives to follow."},"install":[{"target":"skills-cli","command":"npx skills add https://github.com/elementalsouls/Claude-BugHunter/tree/main/skills/hunt-source-leak"},{"target":"claude-code","command":"claude plugin marketplace add https://llmmart.ai/marketplace.json && claude plugin install elementalsouls-claude-bughunter@llmmart"},{"target":"git","command":"git clone https://github.com/elementalsouls/Claude-BugHunter.git"}]}