BEST SECURITY SCANNERS FOR JAVASCRIPT, REACT & NODE.JS (2026)
JavaScript and React apps leak secrets in bundles, trust client-side checks, and expose APIs without authz. A scanner that tests the running app, not only the source tree.
SCAN YOUR REACT APP NOW
Enter your deployed React/JS URL — we inspect the bundle for secrets and probe APIs for auth gaps.
Why JavaScript Apps Need Specialized Scanners
JavaScript and its ecosystem have unique security characteristics that generic SAST tools handle poorly. Prototype pollution – where attackers modify Object.prototype to inject properties across an entire application – is a class of vulnerability that barely exists outside JavaScript. Tools built for Java or C simply do not have rules for it.
React introduces its own surface area. The dangerouslySetInnerHTML prop is the most obvious vector for XSS, but subtler issues exist: unescaped URL parameters in href attributes can enable javascript: protocol attacks, and server-side rendering with unsanitized data creates hydration-based XSS that client-only scanners miss entirely.
The npm ecosystem is the largest package registry in the world, with over 2.5 million packages. That scale means supply chain risk is not theoretical – it is constant. Typosquatting, dependency confusion, and maintainer account takeovers hit npm packages every month. The event-stream incident, ua-parser-js compromise, and colors.js sabotage all demonstrated how a single compromised dependency can cascade to millions of downstream applications.
Node.js on the server adds path traversal via fs.readFile(userInput), command injection through child_process.exec(), and server-side request forgery when HTTP clients accept user-controlled URLs. A scanner that understands the full JavaScript stack – browser, server, and build tooling – catches issues that language-agnostic tools miss.
AI coding tools make the problem denser, not different: they paste insecure tutorial patterns at high volume. Completions that wire dangerouslySetInnerHTML, open CORS, or concatenate SQL appear in Cursor, Copilot, and Claude Code sessions constantly. A JS-aware pipeline is how you keep shipping without re-learning XSS every sprint.
npm audit and GitHub Dependabot
Every Node.js project already has a security scanner built in. Running npm audit checks your dependency tree against the GitHub Advisory Database and reports known vulnerabilities with severity ratings. It is free, requires zero setup, and runs in under a second on most projects.
What npm audit catches: Known CVEs in direct and transitive dependencies, including prototype pollution in lodash, ReDoS in validator packages, and arbitrary code execution in build tools. The npm audit fix command can automatically update to patched versions when semver-compatible fixes exist.
GitHub Dependabot goes a step further by automatically opening pull requests when new advisories are published. It also handles version updates on a schedule, keeping dependencies current before vulnerabilities are discovered. Enable it by adding a dependabot.yml file to your repository’s .github directory.
# .github/dependabot.yml
version: 2
updates:
- package-ecosystem: npm
directory: "/"
schedule:
interval: weekly
open-pull-requests-limit: 10
Limitations: Both tools only scan dependencies, not your application code. They cannot detect XSS in your React components, prototype pollution in your own utility functions, or insecure configurations in your Express middleware. They also lag behind zero-day disclosures – a vulnerability must be reported to the advisory database before these tools flag it. Use npm audit as a baseline, not as your entire security strategy.
For AI-generated apps, also watch for hallucinated package names that never appear in advisories because they are brand-new attacker-owned packages. Pair SCA with the Package Hallucination Scanner.
ESLint Security Plugins
ESLint is already running in most JavaScript projects for code style. Adding security plugins turns it into a lightweight SAST scanner that catches vulnerabilities during development, before code reaches CI.
eslint-plugin-security is the most established option, with rules for detecting eval() usage, non-literal require() calls, non-literal regular expressions (ReDoS risk), and object injection via bracket notation with user input. Install with npm install eslint-plugin-security --save-dev and add "plugin:security/recommended" to your ESLint config.
eslint-plugin-no-unsanitized (maintained by Mozilla) detects direct DOM manipulation methods like innerHTML, outerHTML, and document.write() that accept unsanitized input. This is particularly valuable in codebases that mix React with vanilla DOM manipulation.
React-specific rules: The built-in react/no-danger rule flags dangerouslySetInnerHTML, while react/jsx-no-target-blank catches missing rel="noreferrer" on external links. Combine these with eslint-plugin-security for broad coverage of both React-specific and general JavaScript vulnerabilities.
// eslint.config.js (flat config sketch)
import security from "eslint-plugin-security";
import react from "eslint-plugin-react";
export default [
{
plugins: { security, react },
rules: {
...security.configs.recommended.rules,
"react/no-danger": "error",
"react/jsx-no-target-blank": "error",
},
},
];
The main advantage of ESLint security plugins is developer experience. Findings appear as squiggly lines in VS Code, not as CI failures twenty minutes later. The downside is that ESLint operates on single files without cross-file dataflow analysis, so it cannot trace tainted data from an API endpoint through middleware into a database query. For that, you need Semgrep or Snyk.
Snyk for JavaScript Projects
Snyk has the deepest npm integration of any commercial security platform. Running snyk test in a Node.js project scans your lockfile, identifies vulnerable packages, and shows the dependency path that introduced each vulnerability. Unlike npm audit, Snyk adds reachability analysis – it checks whether your code actually calls the vulnerable function, dramatically reducing false positives.
Snyk Code for JavaScript: Beyond dependencies, Snyk Code provides SAST analysis specifically tuned for JavaScript and TypeScript. It detects XSS in React components, SQL injection in Node.js database queries, path traversal in Express route handlers, and hardcoded secrets in configuration files. Results appear as inline PR comments with fix suggestions.
.snyk policies: Create a .snyk file in your project root to ignore specific findings, set custom severity thresholds, or apply patches to vulnerabilities that do not have official fixes yet. This prevents false positive fatigue while maintaining a clean security posture.
PR checks: Enable Snyk’s GitHub integration to automatically scan every pull request. New vulnerabilities block the merge, while existing (already-tracked) issues pass through. This prevents security debt from growing without blocking developer velocity on known issues being addressed.
The free tier includes 200 tests per month across dependency and code scanning, unlimited projects for open-source repositories, and basic reporting. For most startups and small teams, the free tier covers everything. Paid plans start at $25/developer/month and add priority support, advanced reporting, and higher test limits.
npx snyk auth
npx snyk test --severity-threshold=high
npx snyk code test
Semgrep Rules for React and Node
Semgrep’s pattern-matching approach is particularly effective for JavaScript because the language’s dynamic nature creates patterns that dataflow-based analyzers struggle with. You can write a Semgrep rule to detect any code pattern in minutes, without building an AST plugin or understanding compiler internals.
React-specific rules: The Semgrep Registry includes rules for detecting XSS via dangerouslySetInnerHTML with unsanitized variables, insecure use of useEffect with external data that bypasses sanitization, URL injection through window.location manipulation, and server-side rendering pitfalls where user data gets embedded in the initial HTML payload without encoding.
Node.js rules: Semgrep ships rules for path traversal via unsanitized file system operations, command injection through child_process with user input, SSRF in HTTP client libraries (axios, node-fetch, got), SQL injection in raw query builders, and insecure JWT verification that accepts algorithm: "none".
Custom rules: The real power of Semgrep is writing rules specific to your codebase. If your team uses a custom ORM, write a rule that flags raw SQL queries outside the ORM. If you have an internal sanitization library, write a rule that detects when developers bypass it. Custom rules are YAML files that live in your repository and run alongside the community ruleset.
# .semgrep/no-raw-sql.yml
rules:
- id: no-string-concat-sql
patterns:
- pattern-either:
- pattern: |
$DB.query(`...${...}...`)
- pattern: |
$DB.query("..." + $X)
message: Use parameterized queries instead of string construction
languages: [javascript, typescript]
severity: ERROR
Run Semgrep locally with semgrep --config=p/javascript --config=p/react --config=p/nodejs . to scan your entire project against all JavaScript-related community rules. Scanning typically completes in under 30 seconds for projects with 50,000 lines of code.
Setting Up a JavaScript Security Pipeline
The most effective approach combines multiple tools in a single GitHub Actions workflow. Each tool covers a different attack surface: npm audit for known dependency CVEs, Semgrep for code-level vulnerabilities, and Snyk for reachability-aware dependency analysis with automatic fix PRs.
Pipeline strategy: Run npm audit with continue-on-error: true so it reports but does not block merges on medium-severity dependency findings (which are often transitive and outside your control). Semgrep runs next and blocks on any finding above the configured severity. Snyk runs last with --severity-threshold=high to catch only critical dependency issues that npm audit may have missed due to advisory database timing.
# .github/workflows/js-security.yml
name: js-security
on: [pull_request]
jobs:
scan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with: { node-version: "20" }
- run: npm ci
- run: npm audit --audit-level=high
continue-on-error: true
- uses: returntocorp/semgrep-action@v1
with:
config: >-
p/javascript
p/react
p/nodejs
- run: npx snyk test --severity-threshold=high
env:
SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
Local development: Add ESLint security plugins for instant feedback in the editor. Run Semgrep as a pre-commit hook with semgrep --config=p/javascript --error so developers catch issues before pushing. This shifts security left without slowing down CI.
Total setup time is under 15 minutes. The npm audit step requires no configuration. Semgrep needs a free account for the app token (or skip the token for anonymous scanning with rate limits). Snyk requires a free account and token. Once configured, every pull request gets scanned automatically with results appearing as PR comments and check status updates.
What source scanners still miss
Even a perfect SAST + SCA pipeline cannot see:
- Secrets inlined only after
NEXT_PUBLIC_/VITE_build - Supabase RLS or Firebase rules that exist only in the cloud console
- BOLA that needs two real sessions
- Middleware matchers that fail only on the deployed host
- Public storage buckets and open CORS on production
That is why a runtime layer matters for JS apps that ship to Vercel, Netlify, Railway, or similar. Point a live scanner at the URL:
- Vibe Code Scanner — AI-shaped failures on the deployed app
- Token Leak Checker — keys in the browser bundle
- Security Headers Checker — CSP/HSTS posture
Example runtime checks you should automate
# No secret-shaped strings in the production JS
curl -sL https://app.example.com | true
# use Token Leak Checker UI or download main chunks and grep:
# sk_live_, sk-, AKIA, service_role
# Unauthenticated API should not return private data
curl -s -o /dev/null -w '%{http_code}\n' https://app.example.com/api/me
React-specific threat checklist
| Sink / pattern | Risk | Scanner layer |
|---|---|---|
dangerouslySetInnerHTML |
XSS | ESLint, Semgrep, manual |
href={userInput} |
javascript: URL | ESLint, review |
| SSR + unsanitized props | Hydration XSS | Semgrep SSR rules |
| Client-only auth redirects | A01 bypass | Runtime DAST |
| Open redirects in routers | Phishing | Review + DAST |
| LLM markdown rendered raw | XSS | Review + CSP |
For LLM HTML specifically, see LLM-rendered HTML/Markdown.
Node.js-specific threat checklist
| Pattern | Risk | Notes |
|---|---|---|
child_process.exec(user) |
Command injection | Prefer spawn + allowlist |
fs.readFile(userPath) |
Path traversal | Resolve under a root |
axios.get(userUrl) |
SSRF | Allowlist hosts |
Raw SQL / Mongo $where |
Injection | Parameterize / validate |
JWT algorithms: ['none'] |
Auth bypass | Pin algorithms |
| Missing auth middleware | A01 | Router-level defaults |
Prototype pollution remains relevant in deep merge utilities and query parsers — Semgrep and Snyk both carry rules; still review custom merge helpers.
AI-generated JavaScript: extra detectors
When the code was written by Cursor, Claude Code, Copilot, Lovable, or Bolt:
- Assume missing ownership checks on CRUD.
- Assume secrets may be in the client bundle.
- Assume CORS was opened to silence a browser error.
- Assume tests cover only the happy path.
- Assume new deps were not verified on npm.
Add CI gates for removed auth calls (see Agentic Code Review Guide) and always scan the deploy URL after agent sessions.
Choosing tools by team stage
| Stage | Minimum viable stack |
|---|---|
| Solo weekend project | npm audit + ESLint security + Token Leak Checker on deploy |
| Early startup | + Semgrep CI + Dependabot + VibeEval on preview |
| Growth | + Snyk reachability / paid SCA + secret scanning (gitleaks) + scheduled DAST |
| Enterprise | + commercial SAST policy, SBOM, SSO-gated scanners, formal pentest cadence |
Do not buy enterprise SAST before you have env hygiene and authz tests. Most AI-app incidents are A01/A02/A05, not exotic static analysis misses.
Comparing approaches
| Approach | Strengths | Weaknesses |
|---|---|---|
| npm audit / Dependabot | Free, fast SCA | No app code, lag on zero-days |
| ESLint security | Instant DX | No cross-file taint |
| Semgrep | Custom rules, speed | Needs rule ownership |
| Snyk | Reachability, PR UX | Cost at scale |
| Runtime DAST (VibeEval) | Real exploit evidence | Needs a URL / auth fixtures |
| Manual pentest | Business logic depth | Expensive, point-in-time |
The winning program stacks left (ESLint/Semgrep), middle (SCA), and right (DAST) without expecting any single product to cover the whole JS threat model.
Related Resources
SAST Tools for AI Code
Snyk vs Semgrep vs Checkmarx for AI-generated code
Between SAST and Pentest
Where automated gates end and probing begins
Vibe Code Scanner
Live scanning for AI-built web apps
OWASP Top 10 for AI Code
Priority map for what scanners should catch first
Package Hallucination Scanner
AI-specific dependency risk
Client-side vs server-side responsibilities in React apps
React can validate forms for UX; it cannot enforce authorization. Scanners that only parse JSX miss the real API. A JavaScript/React security scan should:
- Parse the production bundle for secrets and dangerous sinks (
dangerouslySetInnerHTML,eval, openpostMessagehandlers). - Exercise the API routes the SPA calls.
- Check auth cookies/
Authorizationhandling on those routes. - Flag dependency CVEs in the lockfile separately (SCA).
Common React sinks in AI output
// XSS sink — model "renders markdown quickly"
<div dangerouslySetInnerHTML={{ __html: userMarkdown }} />
// Better — sanitize or use a safe markdown component with hardened options
Also watch href={userUrl} for javascript: URLs, and target="_blank" without rel="noopener noreferrer".
SPA auth storage
Tokens in localStorage are XSS-complete account takeovers. Prefer httpOnly cookies with careful CSRF strategy, or short-lived memory tokens plus refresh rotation. AI templates default to localStorage because demos are easier.
Build pipeline checks
npm audit/ OSV- Secret scan on repo + built
dist - Bundle size review (accidental source maps)
- Runtime scan of the hosted SPA URL
Prototype pollution in the JS scanner stack
Language-specific scanners must cover deep merge and query parsers — see prototype pollution and DOM attacks. Add Semgrep rules for __proto__ and runtime probes on JSON merge endpoints. ESLint alone will not catch exploitability.
postMessage and third-party widgets
React apps embed chat, billing, and analytics iframes. Handlers without event.origin checks are common in AI completions. Runtime DAST with a headless browser is the reliable detector; pure SAST misses dynamic registration of listeners.
Mapping tools to OWASP for JS apps
| OWASP | Primary JS tools |
|---|---|
| A01 Access control | Runtime dual-user DAST, manual IDOR |
| A02 Crypto / secrets | gitleaks, Token Leak Checker, bundle grep |
| A03 Injection | Semgrep, ESLint no-unsanitized, Snyk Code |
| A05 Misconfig | Headers checker, CORS review, source maps |
| A06 Components | npm audit, Dependabot, Snyk SCA |
| A07 Auth | Runtime auth flow tests |
| A10 SSRF | Semgrep HTTP client rules + review |
Full priority map: OWASP Top 10 for AI code.
Next.js App Router specifics
Next.js on Vercel blurs client/server. Scanners must understand:
- Server Actions are POST endpoints — auth inside the action, not only the page.
cookies()/headers()force dynamic rendering; missing them can cache private HTML.- Route Handlers under
app/api/**are public unless you check session. - Middleware matchers that omit
/apileave APIs open while pages redirect.
SAST that only flags dangerouslySetInnerHTML will miss Server Action BOLA. Prefer runtime tests against preview URLs after each agent PR.
Express / Fastify baseline rules
For Node APIs generated by Cursor/Claude Code:
// Router-level defaults
app.use("/api", requireAuth);
app.use(helmet());
app.use(rateLimit({ windowMs: 60_000, max: 100 }));
// Per-resource
app.get("/api/items/:id", async (req, res) => {
const item = await db.item.findById(req.params.id);
if (!item || item.ownerId !== req.user.id) return res.status(404).end();
res.json(item);
});
Semgrep custom rules for findById without owner compare catch a large fraction of AI CRUD bugs.
Source maps in production
AI deploys often leave *.js.map public. That accelerates secret hunting and IP theft. Fail CI if source maps upload to the CDN for production:
find dist -name '*.map' | grep . && exit 1 || true
Use Source Map Checker on the live host.
Scan Your JavaScript App in Seconds
VibeEval combines dependency-aware testing habits with live security probing for JavaScript, React, and Node.js projects. Catch XSS sinks, prototype-pollution patterns, secret leaks in the bundle, and authorization gaps before users do — especially when the code was AI-generated.
SCAN THE RUNNING REACT APP
Source review misses what ships in the bundle and what APIs return without auth. Get both on one URL.
14-day free trial · No credit card · Cancel anytime