setup/Skills/js-code-analysis/SKILL.md
Specialized JavaScript/TypeScript static analysis for bug bounty hunting. Covers Node.js, Express.js, Next.js, NestJS, Fastify, and modern frameworks. Uses AST-grep and Grep tool to find high-impact vulnerabilities (RCE, SSRF, SQLi, SSTI, Prototype Pollution, JWT, DOM XSS, GraphQL, ReDoS, CORS, CSRF) via strict Source-to-Sink Taint Analysis. Every finding MUST have concrete evidence.
npx skillsauth add mswell/dotfiles js-code-analysisInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Advanced static analysis of JS/TS codebases for high-severity vulnerabilities.
Rule #1: NO HALLUCINATION. Every finding MUST be backed by concrete code evidence (file path + line + snippet) and a clear, reproducible exploit path.
Rule #2: Taint Analysis is MANDATORY. Prove how user-controlled input (Source) reaches the dangerous function (Sink) without proper sanitization.
Rule #3: BRAIN DUMP MANDATE. Before listing vulnerabilities, document your reasoning, searches performed, dead ends, and false positive eliminations.
Rule #4: Use Claude Code tools. Prefer Grep tool over rg via Bash. Use Glob for file discovery. Reserve Bash for ast-grep (sg) and npm audit only.
Subagents MUST use Claude Code's dedicated tools:
rg or grep via Bash)find or ls via Bash)cat/head/tail via Bash)ast-grep (sg) commands, node scripts, npm auditAvailable scripts (invoke via Bash):
scripts/analyze.js --target <path> --category <cat> — ast-grep scanner by vulnerability categoryscripts/check_safety.js --target <domain> --platform <name> — Safe Harbor verificationscripts/pattern_validator.js --patterns-dir <dir> --fixtures-dir <dir> — validate ast-grep patternsALL searches MUST exclude: node_modules/, dist/, build/, .next/, .nuxt/, coverage/, .git/, vendor/, __pycache__/, .cache/, .turbo/
Use Grep tool's glob parameter to filter (e.g., glob: "!node_modules/**"), or target specific source directories.
Goal: Prepare workspace, detect framework, check for exposed files.
mkdir -p .js-audit
package.json — identify framework, dependencies, scriptsexpress), Next.js (next), NestJS (@nestjs/core), Fastify (fastify)tsconfig.jsonlerna.json, pnpm-workspace.yaml, turbo.json, nx.json.env*, *.pem, *.key, firebase*.json, serviceAccount*.json, google-services.json.gitignoreDelegate to 3 parallel subagents, then compile report.
Wave 1 (PARALLEL — launch all three in a single message):
├── js-security-expert agent → Phases 1 + 2 + 3 (Recon + Frontend + Auth/Session)
├── api-security agent → Phases 4 + 5 (Dangerous Sinks + Injection Flaws)
└── webapp-security agent → Phase 6 (Logic, DOM XSS, File Upload, Config)
Wave 2 (SEQUENTIAL — after Wave 1 completes):
└── report-writer agent → Phase 7: Taint validation + compile .js-audit/report.md
Steps:
Agent calls in parallel. Provide each with:
references/vulnerability-patterns.md for pattern matchingreferences/cwe-checklist.md for CWE mappingreport-writer with all findings + references/escalation-guide.md for chain identification..js-audit/report.md path.Phase 1: Recon & Routing → Routes, endpoints, Server Actions, middleware chain
Phase 2: Frontend-to-Backend → SSRF vectors, postMessage, WebSocket, CORS, state encoding
Phase 3: Auth & Session → JWT, OAuth2, CSRF, cookies, session, IDOR, rate limiting
Phase 4: Dangerous Sinks → RCE, SSRF, SSTI, Deserialization, Path Traversal
Phase 5: Injection Flaws → SQLi, NoSQLi, GraphQL, ReDoS, SSR XSS
Phase 6: Logic & Client-side → Prototype Pollution, Mass Assignment, DOM XSS, File Upload, Config
Phase 7: Taint & Report → Source-to-Sink validation + report generation
Goal: Identify where user input enters the application (Sources).
Search patterns (use Grep tool, targeting source directories only):
Express.js:
app\.(get|post|put|delete|patch|all|use)\(router\.(get|post|put|delete|patch|all|use)\(Next.js (Pages Router + App Router + Server Actions):
export (async )?function (GET|POST|PUT|DELETE|PATCH) (App Router)export default function handler (Pages Router)"use server" (Server Actions)getServerSideProps, getStaticPropsNestJS:
@(Get|Post|Put|Delete|Patch|All)\( (route decorators)@Controller\(, @Injectable\(Fastify:
fastify\.(get|post|put|delete|patch)\(User input sources (ALL frameworks):
req\.(body|query|params|headers|cookies|files|file)ctx\.(request|params|query|body)@Body\(\), @Query\(\), @Param\(\), @Headers\(\) (NestJS decorators)Middleware chain (auth bypass vector):
app\.use\(, router\.use\(next() called without auth validationFor each route, document:
Goal: Discover hidden attack surfaces from frontend-backend interaction.
Search patterns:
SSRF vectors (frontend passing URLs to backend):
fetch\( with variable URL arguments (not string literals)axios\.(get|post|put|request)\( with variable argumentsurl, target, path, endpoint, webhook, callback, redirect, proxy, destpostMessage vulnerabilities (deep analysis):
addEventListener\(["']message["'] — find ALL handlers, then for each:
event.origin checked against a HARDCODED allowlist? Domain-only checks are insufficienttrusted.isSameOrigin(untrusted) instead of untrusted.isSameOrigin(trusted) — when trusted has null fields (sandboxed iframe), all checks pass trivially/^https:\/\/.*facebook\.com$/ — unescaped dot allows evilfacebook.com. Check for \. before domain namesevent.origin stored to localStorage/variable then used to construct script.src, iframe.src, or fetch() URLs = criticalinnerHTML, outerHTML, document.write(), .html(), form.action set from event.data = DOM XSS even with valid origintypeof x === "string" gate bypassed by arrays with malicious .toString()postMessage\(.*,\s*['"]?\*['"]?\) — wildcard targetOrigin with sensitive data (tokens, codes, blobs) = ALWAYS a bugnew MessageChannel — after port sharing, verify subsequent messages on channel still authenticate; port reuse without re-validation = hijackMath\.random\(\) used to generate tokens, nonces, or shared secrets for cross-window auth — predictable via V8 PRNG state reconstruction (Z3 solver with 4+ sequential outputs)window\.name — persists across navigations; leaks data cross-origin if iframe navigated to attacker domainWebSocket:
new WebSocket\(, io\(, io\.connect\(, socket\.on\(wss:// not ws://)? Message validation?CORS misconfiguration:
Access-Control-Allow-Origin — search for * or reflected origincors\( config — check origin: and credentials: combinationres\.(header|setHeader)\(.*Access-ControlState encoding (deserialization vectors):
btoa\(JSON\.stringify\(, Buffer\.from\(.*base64JSON\.parse\(atob\(, JSON\.parse\(Buffer\.from\(Hidden/admin routes:
\/api\/(internal|admin|debug|test|_|health|metrics|graphql|graphiql)window\.location\.(origin|href|hash) — open redirect sourcesClient-Side Path Traversal (CSPT2CSRF):
fetch('/api/' + userInput + '/action') — path traversal via ../ in userInputfetch(`/api/${param}/data`) — template literal URL path with unsanitized inputaxios.get(baseUrl + variable + '/endpoint') — concatenated path segmentslocation.hash, location.search, URLSearchParams.get(), database-injected valuesfetch(), axios.*(), XMLHttpRequest.open() with user input in URL pathGoal: Find authentication bypass, session management flaws, broken access control.
Search patterns:
JWT issues:
jwt\.sign\( — hardcoded secret? weak algorithm?jwt\.verify\( — algorithms option set? (prevents algorithm confusion)jwt\.decode\( — decode WITHOUT verify = no signature check (WARNING)algorithm.*none — "none" algorithm attackOAuth2 misconfiguration:
redirect_uri, callback_url — validated against allowlist?state parameter — generated and checked? (CSRF in OAuth flow)client_secret in frontend code = leaked secretcode_verifier, code_challenge present for public clients?Session & cookies:
httpOnly, secure, sameSite flagsexpress-session config: hardcoded secret? resave? saveUninitialized?req.session.regenerate)CSRF protection:
csrf, csurf, csrf-csrf in dependencies?SameSite cookie attribute?IDOR / Broken Access Control:
req.params.id without ownership check (WHERE owner = req.user.id)findById, findOne, findByPk with user-controlled IDreq.user.role, req.body.role — can role be tampered client-side?Rate limiting:
express-rate-limit, rate-limiter-flexible in dependencies?/login, /register, /forgot-password) without limiting = brute forcePassword reset:
OAuth redirect_uri bypass patterns:
redirect_uri.startsWith(registeredCallback) — bypassed with ../ path traversal (e.g., /callback/../../open_redirect?next=evil.com)new URL(redirect_uri).hostname === allowedHost without .pathname check — attacker uses path to open redirect on allowed domainfallback_redirect_uri or base_uri parameters with domain-only validation — path component not checkedres.redirect(req.cookies.redirect_url) — attacker pre-sets cookie via CSRFresponse_type=token with redirect chains — token persists in URL fragment across HTTP redirects through subdomainsparam[0=value overriding param=value server-side; test redirect_uri[0=evil.com alongside legitimate redirect_uriGraphQL-specific authorization:
doc_id values for same resource type — edit/mutation doc_id may expose private fields (linked_*, shadow_*, internal_*) not visible in view doc_idactor_id or user_id in mutation variables NOT validated against the authenticated session — spoofable"No such class: OBJECT_TYPE" in production{result=NAME:$.field} enables cross-request data exfiltrationapplication/x-www-form-urlencoded (form-submittable) = CSRF without tokenGoal: Find functions that execute code, make requests, or access files.
Use BOTH Grep tool AND ast-grep (sg via Bash):
RCE / Command Injection:
child_process, exec\(, execSync\(, spawn\(, spawnSync\(eval\(, new Function\(, vm\.runIn, vm\.createScriptexec($CMD), exec(\$CMD`), execSync($CMD), spawn($CMD, $$)`eval($CODE), new Function($CODE)setTimeout($STR, $$) — string argument (not function) = eval-likeSSRF:
fetch\(, axios, got\(, request\(, http\.get\(, https\.request\(, undicifetch($URL), fetch($URL, $$), axios.get($URL), axios($CFG)got($URL), http.get($URL)Deserialization:
unserialize\(, node-serializeyaml\.load\( (js-yaml without safeLoad / SAFE_SCHEMA)JSON\.parse\( with user input flowing to prototype-sensitive operationsSSTI:
res\.render\(.*req\.(body|query|params) — user input in template contextejs\.render\(, pug\.compile\(, handlebars\.compile\(, nunjucks\.renderString\(res.send(`...${req.query.x}...`)Path Traversal / LFI:
fs\.readFile, fs\.readFileSync, fs\.createReadStream — check if path includes user inputfs\.writeFile, fs\.writeFileSync — arbitrary file writepath\.join\( or path\.resolve\( with user-controlled segments without ../ checkres\.sendFile\(, res\.download\(Critical: If ANY sink receives user input via interpolation or concatenation WITHOUT validation → Critical finding.
Goal: SQL/NoSQL injection, GraphQL abuse, ReDoS, SSR XSS.
SQL Injection:
query(`SELECT ... WHERE id = ${id}`)"SELECT * FROM " + table\.query\(, \.raw\(, knex\.raw\(, sequelize\.query\(, prisma\.\$queryRaw$DB.query(\$SQL`), knex.raw($SQL)`$1, ?, :name placeholders)NoSQL Injection (MongoDB/Mongoose):
\.(find|findOne|updateOne|deleteOne)\(.*req\.(body|query){ $ne: null }, { $regex: ".*" } operatorsmongo-sanitize, String() casting, explicit $eqGraphQL:
introspection:\s*true or not explicitly disabledgraphql-depth-limit, graphql-query-complexityDid you mean in error responsesReDoS (Regex Denial of Service):
new RegExp\( with user-controlled pattern = arbitrary ReDoS(a+)+, (a|a)*, ([a-z]+)*\.match\(, \.replace\(, \.test\( with dynamic regex argumentSSR XSS:
dangerouslySetInnerHTML with user-derived datares\.send\(.*req\.(body|query|params) — unsanitized in responseParser Differentials / MIME Confusion:
Content-Type: application/json;,text/html — server validator sees application/json, browser renders text/htmlif (parse(ct) === 'application/json') res.setHeader('Content-Type', ct) — MUST use parsed value, not originalX-Content-Type-Options: nosniff + user-controlled Content-Type = MIME sniffing XSSres.setHeader('Content-Type', req.headers['content-type']) or type parameter reflected in response headerHTML-to-PDF / Server-Side Rendering SSRF:
wkhtmltopdf, puppeteer, playwright, headless-chrome, phantom, pdf-lib with HTML input<iframe src="file:///etc/passwd"> for LFI<iframe src="http://169.254.169.254/..."> for SSRF to cloud metadatapdf, render.*html, puppeteer, wkhtmltopdf, headless in same code path as user inputGoal: Prototype Pollution, Mass Assignment, DOM XSS, file upload, configuration.
Prototype Pollution:
Object\.assign\(.*req\.body, \.merge\(, lodash\.merge, _.merge, _.defaultsDeep__proto__, constructor, prototype)Object.assign($T, req.body), _.merge($T, $S)Mass Assignment:
$M.create(req.body), $M.update(req.body, $$)fields whitelist, Mongoose without strict schemaprisma.$M.create({ data: req.body })DOM-based XSS (frontend):
location\.(search|hash|href), document\.referrer, document\.URL, window\.name\.innerHTML, \.outerHTML, document\.write, eval\(, jQuery\.html\(, \$\(.*\)\.html\(dangerouslySetInnerHTML with user-derived data (React)v-html (Vue), [innerHTML] (Angular) with dynamic bindingFile upload:
multer, formidable, busboy — check:
../)?Environment & config exposure:
.env files committed (check .gitignore)process\.env values leaked to client-side bundleNEXT_PUBLIC_ prefix exposes vars to client — audit what's prefixedNODE_ENV.*development, DEBUG=*.map files in build outputRace conditions:
await on critical async operationsPromise.all on dependent operations that should be sequentialXS-Leaks (Cross-Site Information Leaks):
onload/onerror difference in <script> tag reveals stateX-Frame-Options: deny based on user-supplied params (__user) → timing/behavioral difference reveals identityexport default { userId } — pollute Function.prototype.default/.__esModule before script loads to intercept user IDSupply-chain XSS via shared scripts:
fbevents.js, capig-events.js) served across many domains.js file content with user-derived valuesDebug code in production:
document.write in OAuth callback scripts with debug flag enabled/debug/, /_debug/, ?debug=true) returning sensitive dataconsole.log with sensitive variables (tokens, passwords) in production builds*.map) exposing original sourceMANDATORY Taint Analysis for every finding:
req.query.url)fetch(url))References for report-writer agent:
references/cwe-checklist.md for CWE mapping and CVSS scoringreferences/escalation-guide.md for identifying attack chain escalationsreferences/h1-examples.md for real-world precedentreferences/writeup-insights.md for real-world postMessage, OAuth, CSPT, parser differential, and XS-Leak patterns from Meta/Facebook bug bounty writeupsOutput: Write .js-audit/report.md
# Brain Dump
## Project Overview
- **Framework:** [detected from Phase 0]
- **Language:** JS / TS
- **Entry points:** [count of routes/endpoints]
- **Auth mechanism:** [JWT / session / OAuth / none]
- **Key dependencies:** [security-relevant packages]
## Attack Surface Summary
- **Routes without auth:** [list]
- **Dangerous sinks found:** [count by type]
- **External integrations:** [APIs, databases, cloud services]
## Analysis Log
- [Key decisions, patterns investigated, reasoning]
- [Interesting code paths and potential attack chains]
## Dead Ends & False Positive Elimination
- [Sinks found but properly validated/sanitized — with explanation]
- [Patterns searched but not present in this codebase]
- [Findings investigated and discarded — specific reason]
---
# JS/TS Bug Bounty Report: [Project Name]
## Executive Summary
- **Findings:** N total | Critical: X | High: X | Medium: X | Low: X
- **Framework:** [detected]
- **Key Risks:** [1-2 sentences]
---
## [VULN-001] Title — SEVERITY
**CWE:** CWE-XXX — [Title]
**CVSS 3.1:** X.X (AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:N)
**Impact:** [Concrete impact]
### Evidence & Taint Analysis
**Source:** `src/routes/api.ts:15`
```typescript
const userUrl = req.query.url; // SOURCE
Propagator: (if intermediate processing exists)
Sink: src/routes/api.ts:20
const response = await fetch(userUrl); // SINK — no validation
Flow: req.query.url → userUrl → fetch(userUrl) — unvalidated
GET /api/proxy?url=http://169.254.169.254/latest/meta-data/ HTTP/1.1
Host: target.com
// Specific fix with secure code example
development
Create distinctive, production-grade frontend interfaces with high design quality. Use this skill when the user asks to build web components, pages, or applications. Generates creative, polished code that avoids generic AI aesthetics.
tools
Turn the current conversation context into a PRD and publish it to the project issue tracker. Use when user wants to create a PRD from the current context.
tools
Break a plan, spec, or PRD into independently-grabbable issues on the project issue tracker using tracer-bullet vertical slices. Use when user wants to convert a plan into issues, create implementation tickets, or break down work into issues.
development
Test-driven development with red-green-refactor loop. Use when user wants to build features or fix bugs using TDD, mentions "red-green-refactor", wants integration tests, or asks for test-first development.