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:

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:

  1. Assume missing ownership checks on CRUD.
  2. Assume secrets may be in the client bundle.
  3. Assume CORS was opened to silence a browser error.
  4. Assume tests cover only the happy path.
  5. 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.

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, open postMessage handlers).
  • Exercise the API routes the SPA calls.
  • Check auth cookies/Authorization handling 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

  1. npm audit / OSV
  2. Secret scan on repo + built dist
  3. Bundle size review (accidental source maps)
  4. 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 /api leave 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

START FREE SCAN