What you'll learn
Quick Answer
npm audit reports known vulnerabilities in your dependency tree. Severity describes the worst case for the library, not your exposure — check whether your project uses the affected feature before treating it as urgent.
Why dependencies are the risk
A modern project has few direct dependencies and an enormous transitive tree. Running an audit on a fairly ordinary site here reported 432 total dependencies — almost none of them chosen deliberately.
Every one is code you execute and did not write. A vulnerability anywhere in that tree is a vulnerability in your application, and a compromised maintainer account can push malicious code that installs automatically on your next build.
This is supply chain risk, and it is the reason auditing matters more than most application-level hardening for a typical project.
npm audit # Node
pip-audit # Python
./gradlew dependencyCheckAnalyze # Java
Reading a real report
Here is a genuine result from a static site built with Astro:
vulnerabilities: {"low":2, "high":8, "critical":0, "total":10}
total dependencies: 432
astro <=7.0.9
Severity: high
XSS in define:vars via incomplete </script> tag sanitization
XSS via unescaped transition:* directive values
Host header SSRF in prerendered error page fetch
Server island encrypted parameters vulnerable to replay
...
fix available via `npm audit fix --force`
Will install astro@7.2.4, which is a breaking change
Eight high-severity issues, and the suggested fix is a two-major-version upgrade. The instinct is either to panic or to ignore it. Both are wrong.
The correct response is to work out which of these can actually affect this project.
Severity is not your risk
Severity scores describe the worst realistic case for the library across all its users. Your exposure depends on whether you use the affected code path at all, and with what input.
Taking that report item by item:
- Host header SSRF in prerendered error page fetch — requires a running server. This site builds to static files with no server rendering, so there is no request handling to attack. Not applicable.
- Server island parameter replay — a check found zero uses of server islands in the codebase. Not applicable.
- XSS via
define:vars— the project does use it, in 9 files. But XSS requires attacker-controlled input reaching that directive. Here the values are the site's own course and lesson titles from local data files, fixed at build time. Applicable in principle, not exploitable in practice. - XSS via
transition:values — used in 50 files, same reasoning. The values are authored, not user-supplied.
So: eight high-severity advisories, and realistic exposure close to zero — because a statically generated site has no user input at build time and no server at runtime.
That does not mean ignore it. It means this is scheduled maintenance rather than an emergency, and knowing the difference is what stops teams either panicking or becoming numb to audit output.
The questions to ask for each finding
- Is it in code I actually run? A vulnerability in a development-only dependency such as a test runner or build tool is not in your production application.
npm audit --omit=devseparates these. - Do I use the affected feature? Advisories usually name the function or directive. If you never call it, you are not exposed.
- Can an attacker reach it? The vulnerable path must be reachable with input they control. Build-time-only code with authored input generally is not.
- What does exploitation require? Some need authenticated access or a specific unusual configuration.
- Is there a fix, and what does it cost? A patch release is trivial; a major version upgrade needs testing and planning.
This is why npm audit fix --force deserves caution — it applies breaking major upgrades to silence warnings, which can break your build in exchange for fixing something that never applied to you.
A sustainable practice
- Run audits in CI but do not fail the build on every finding, or the check gets disabled within a month. Fail on critical, report the rest.
- Use automated update pull requests — Dependabot or Renovate — so patch and minor updates arrive continuously in small, reviewable pieces. Staying current is far easier than catching up.
- Commit your lockfile. Without it, builds resolve different versions at different times and are not reproducible.
- Prefer fewer dependencies. The most reliable way to reduce supply chain risk is to have less supply chain. A one-line utility does not need a package.
- Check a package before adding it — recent releases, open issues, number of maintainers. An unmaintained package is a future problem.
- Schedule upgrades rather than reacting. Major versions are far cheaper to adopt one at a time than three at once, which is exactly the situation the report above describes.
