A JavaScript security scanner is a tool that inspects your JavaScript or TypeScript code and its dependencies for security vulnerabilities, and choosing one well means understanding that "scanning" actually covers three different jobs. When people look for a JavaScript security scanner they usually want one tool to catch everything, but the flaws in a Node or browser codebase fall into distinct categories, and each needs a different kind of scanner. This guide explains what each type finds, where the real risk in JavaScript projects lives, and how to run them so they help rather than nag.
The three jobs a scanner does
Security scanning for JavaScript breaks into three complementary techniques.
Software composition analysis (SCA) scans your dependencies. For a typical project this is where most of the risk actually is, because a modern node_modules tree pulls in hundreds of packages you never chose directly. SCA reads your package.json and lockfile, resolves the full transitive tree, and matches every package against vulnerability databases. Our overview of software composition analysis goes deeper on why the transitive layer matters most.
Static application security testing (SAST) scans the code you wrote. It parses your source into an abstract syntax tree and looks for dangerous patterns: unsanitized input flowing into eval or innerHTML, prototype pollution, insecure randomness, or a SQL query built by string concatenation. SAST is good at finding injection and XSS sinks that a human reviewer skims past.
Secret scanning looks for credentials committed to the repository: API keys, tokens, and private keys that ended up in source or config. This is a distinct scan because secrets are not code flaws or dependency flaws, but they are one of the most common ways real systems get breached.
A complete JavaScript security scanner setup runs all three, because a clean dependency tree with a hardcoded AWS key in config.js is not secure.
Where the real risk lives: the dependency tree
The npm ecosystem's strength, tiny composable packages, is also its largest attack surface. Installing one package can pull in dozens of transitive dependencies, any of which can carry a known vulnerability or, in the worst case, be actively malicious.
Two distinct dependency threats matter:
- Known vulnerabilities in legitimate packages. These are tracked as CVEs and GitHub advisories, and SCA flags them. The fix is usually a version bump.
- Malicious packages introduced through typosquatting (a package named to look like a popular one), or through a maintainer account being compromised and a trojaned version published. These are harder to catch with a CVE database alone and are why supply chain provenance matters.
The built-in starting point is npm audit, which checks your installed tree against the npm advisory database:
npm audit --audit-level=high
It is free, always available, and a reasonable first gate. Its limitations are real, though: it is noisy about issues in dev dependencies that never ship, it does not do reachability analysis, and it only covers the npm advisory feed. Dedicated SCA tools add broader databases, reachability, and remediation guidance.
Static analysis for the code you wrote
For your own source, ESLint with security plugins is the accessible entry point and catches a meaningful set of issues in code you already lint:
npm install --save-dev eslint-plugin-security
{
"plugins": ["security"],
"extends": ["plugin:security/recommended"]
}
For deeper data-flow analysis, Semgrep offers strong JavaScript and TypeScript rulesets that trace tainted input to dangerous sinks. It runs fast enough for CI and its rules are readable, so you can tune them to your codebase:
semgrep --config "p/javascript" ./src
The classes worth prioritizing in JavaScript specifically are cross-site scripting via unsafe DOM writes, prototype pollution, eval-family injection, and insecure deserialization. A scanner that catches these before merge saves you from finding them in a penetration test.
Wiring scanners into CI
Scanners only work if they run automatically and block bad merges. A minimal pipeline stage for a Node project:
security:
steps:
- run: npm ci
- run: npm audit --audit-level=high
- run: npx semgrep --config "p/javascript" --error ./src
- run: npx eslint . --max-warnings 0
Two rules of thumb keep this sustainable. First, gate on severity, not on every finding, or the team learns to ignore a wall of red. Start by failing only on high and critical, then tighten. Second, run SCA continuously, not just on commit, because new vulnerabilities are disclosed against packages that were clean when you last built. A scanner that only runs at merge time goes stale within days.
Runtime and dynamic testing round it out
Static scanning cannot see everything. Logic flaws, authentication bypasses, and issues that only appear when the app is assembled and running need dynamic testing against the deployed application. Pairing a static JavaScript security scanner with dynamic application security testing covers both the code as written and the app as it behaves, which is the combination that actually reduces incidents.
FAQ
What is the difference between SAST and SCA for JavaScript?
SAST scans the code you wrote for dangerous patterns like injection and XSS. SCA scans your dependencies for known vulnerabilities. Most of the risk in a typical JavaScript project is in dependencies, so SCA is often the higher-value scan, but you need both.
Is npm audit enough on its own?
It is a reasonable free starting point but limited. It is noisy about dev dependencies, does not do reachability analysis, and covers only the npm advisory feed. Dedicated SCA tools add broader coverage, reachability, and remediation guidance.
How do I catch malicious npm packages, not just vulnerable ones?
CVE databases catch known-vulnerable packages but not freshly published malicious ones. Defenses include pinning versions with a lockfile, verifying package provenance, watching for typosquatted names, and monitoring for unexpected new transitive dependencies.
Should a JavaScript security scanner run on every commit?
Fast scans like SAST and secret scanning should run on every commit and gate merges. SCA should also run continuously on a schedule, because new vulnerabilities are disclosed against dependencies that were clean when you last committed.