Beginner

How to Run a Dependency Audit

Your application is mostly other people’s code. A typical Node project pulls in hundreds of transitive packages, and any one of them can carry a known vulnerability, an abandoned maintainer, or a license your lawyer would not enjoy. A dependency audit answers three questions: what am I shipping, what is known to be broken, and what do I do about it.

This tutorial walks through a full audit in under an hour using free tools. Examples cover Node and Python, and step 3 covers everything else. For the reasoning behind each check, see auditing third party dependencies.

What you’ll need

  • A project with a lockfile (package-lock.json, yarn.lock, poetry.lock, requirements.txt, or similar)
  • Node and npm, or Python and pip, depending on your stack
  • About 45 minutes

1. Take inventory of what you actually ship

Start by finding out how big your supply chain really is, because most teams guess low by an order of magnitude.

For Node:

# Direct dependencies
npm ls --depth=0

# Total installed packages, direct plus transitive
npm ls --all --parseable | wc -l

For Python:

pip list | wc -l

Also separate what runs in production from what only runs on developer machines. A vulnerability in your bundler matters far less than one in your HTTP framework:

npm ls --omit=dev --depth=0

Write the numbers down. Direct count, total count, production count. These are your audit scope.

2. Run the ecosystem native audit

Every major ecosystem has a built-in or first-party auditor that checks your installed versions against a vulnerability database.

Node:

# Audit everything
npm audit

# Audit only what ships to production
npm audit --omit=dev

Python:

pip install pip-audit

# Audit the active environment
pip-audit

# Or audit a requirements file
pip-audit -r requirements.txt

Each finding lists the package, the vulnerable version range, a severity, and an advisory link. Resist the urge to react to the raw count. A hundred findings in dev tooling can matter less than one critical in a request handler, which is why triage gets its own step.

3. Cross-check with osv-scanner

No single database is complete. Google’s osv-scanner queries the OSV database, which aggregates advisories across ecosystems, and it scans every lockfile it finds in one pass, which is useful in polyglot repos:

# macOS
brew install osv-scanner

# Scan the whole repo recursively
osv-scanner -r .

It picks up package-lock.json, poetry.lock, go.mod, Cargo.lock, Gemfile.lock, and more. Compare its output with step 2. Anything one tool flagged and the other missed deserves a manual read of the advisory. This is the same layered logic behind combining analysis techniques in general; see SAST vs DAST for how the static and dynamic sides of that split work.

4. Triage findings by reachability and exposure

Now turn the raw list into decisions. For each finding, ask three questions in order:

  1. Does it ship? Dev-only dependencies (test runners, linters, build tools) rarely create production risk. Deprioritize them.
  2. Is the vulnerable code path reachable? Read the advisory. A prototype pollution bug in a function you never call is a lower priority than a request smuggling bug in your web server.
  3. Is the package exposed to untrusted input? Anything that parses user input, handles auth, or touches the network goes to the top of the list.

Find out why a flagged transitive package is even installed:

npm ls vulnerable-package-name

This prints the dependency chain, which tells you which direct dependency to upgrade. Sort everything into three buckets: fix now (reachable, production, untrusted input), fix this sprint (production but low reachability), and accept with a note (dev-only or provably unreachable).

5. Fix, upgrade, or replace

Take the buckets in order. Start with the free wins:

# Apply non-breaking upgrades automatically
npm audit fix

# See what a breaking fix would change before doing it
npm audit fix --dry-run

Avoid the --force variant of npm audit fix on a first pass, because it will happily apply semver-major upgrades and break your build. For those, upgrade the direct dependency deliberately, read its changelog, and run your tests.

While you are in there, check for abandonment, which no vulnerability scanner reports:

npm view some-package time.modified

A package with no release in two years and open security issues is a finding even with zero CVEs. Plan to replace it or vendor it.

6. Check licenses and lock the process in

Vulnerabilities are not the only dependency risk. A copyleft license deep in your tree can create obligations you did not sign up for:

npx license-checker --summary

Review anything that is not in your accepted set (most teams accept MIT, Apache-2.0, BSD, ISC). Flag GPL and AGPL packages for a real decision rather than a shrug.

Finally, make the audit continuous instead of annual. Add the scanners to CI so pull requests fail on new critical findings, and enable automated update PRs (Dependabot or Renovate) so upgrades arrive in small, reviewable pieces instead of a terrifying yearly batch. Our security review checklist template includes the dependency section you can paste into your team’s process.

Get a second pair of eyes (free)

Scanners match versions against databases. They cannot tell you that you depend on three HTTP clients when one would do, that a critical package is maintained by one anonymous account, or that your lockfile and your deploy process disagree. After running your own audit, get a free code audit: a Webisoft engineer will manually review your dependency tree along with the rest of your codebase and send you a prioritized, human-written report. Free, no strings, and usually eye-opening.

FAQ

Frequently asked questions

npm audit and osv-scanner disagree. Which one do I trust?

Trust the union. They query different vulnerability databases, no single database is complete, and that gap is exactly why the cross-check step exists. Anything one tool flags and the other misses deserves a manual read of the advisory before you decide.

Do I need to fix every vulnerability the scanners report?

No, and trying to will drown you in noise. Triage by whether the package ships to production, whether the vulnerable code path is reachable, and whether it handles untrusted input. Dev-only findings and provably unreachable ones can be accepted with a written note.

Is npm audit fix safe to run without looking?

The plain form only applies semver-compatible upgrades, so it is usually safe, but run your test suite afterward anyway. Avoid the force variant on a first pass, because it will happily apply semver-major upgrades and break your build. For breaking fixes, upgrade the direct dependency deliberately and read its changelog.

A package has zero CVEs but no release in two years. Is that really a finding?

Yes. An abandoned package means the next vulnerability found in it may never be patched, and no scanner reports abandonment because there is no advisory to match against. Plan to replace it, vendor it, or accept the risk consciously and in writing.

How often should I redo this audit?

Continuously, not annually. Vulnerability databases update daily, so a clean scan today can be red tomorrow with no change on your side. Put the scanners in CI so pull requests fail on new critical findings, and let Dependabot or Renovate deliver upgrades in small reviewable pieces.

Free · no strings shown later

Get your free code audit

A repo URL and two minutes of your time. A Webisoft engineer sends back written findings (security, tech debt, performance) with file-and-line specifics.

Request my free audit