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 -lFor Python:
pip list | wc -lAlso 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=0Write 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=devPython:
pip install pip-audit
# Audit the active environment
pip-audit
# Or audit a requirements file
pip-audit -r requirements.txtEach 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:
- Does it ship? Dev-only dependencies (test runners, linters, build tools) rarely create production risk. Deprioritize them.
- 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.
- 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-nameThis 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-runAvoid 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.modifiedA 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 --summaryReview 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.