Dependency Security Checks Every Small Dev Team Can Run
A fresh Node project with a web framework, a test runner and a linter can pull in 700 or more packages before you write a line of your own code. You chose maybe twelve of them.

A fresh Node project with a web framework, a test runner and a linter can pull in 700 or more packages before you write a line of your own code. You chose maybe twelve of them. The other several hundred arrived as dependencies of dependencies, written by people you will never meet and maintained, in some cases, by nobody at all.
Dependency security is the habit of knowing what those packages are, which of them carry known problems and what you will do when one goes bad. Small teams rarely need a dedicated tool budget for it. They need four or five checks that run without anyone remembering to run them, and a short written plan for the day an advisory lands on a package they depend on.
What Dependency Security Actually Protects You From
There are three distinct risks hiding under one label, and they call for different defences.
- Known vulnerabilities. A package version has a published flaw, usually with an advisory ID. This is the most common case and the easiest to detect, because databases already list it.
- Malicious releases. An attacker takes over a maintainer account or publishes a lookalike name, and a new version ships with code that steals tokens at install time. Scanners often miss these for the first few hours or days.
- Abandonment. The package works today, has no advisory, and has not seen a commit in four years. Nobody will patch it when a flaw is found.
Most teams only defend against the first. The checks below cover all three.
Check One: Commit the Lockfile and Install From It
A lockfile pins every package, including the transitive ones, to an exact version and checksum. Without it, two developers running the same install command a week apart can get different code. With it, an attacker who publishes a poisoned minor release cannot slip into your build unless someone updates the lockfile on purpose.
The lockfile only helps if your CI uses it strictly. In the npm world that means npm ci rather than npm install. For Python, use a tool that writes hashes, such as pip-tools with hash generation or Poetry, and install with hash checking enabled. Go and Rust handle this through go.sum and Cargo.lock.
A mistake we see often: the lockfile is committed, but a Dockerfile runs a plain install that ignores it. Search your build files for every install command and confirm each one respects the lock.
Check Two: Run an Audit on Every Pull Request
Every major ecosystem now ships an audit command that compares your lockfile against an advisory database:
| Ecosystem | Command | Typical run time |
|---|---|---|
| Node | npm audit | 2 to 5 seconds |
| Python | pip-audit | 5 to 20 seconds |
| Go | govulncheck | 10 to 40 seconds |
| Rust | cargo audit | 2 to 5 seconds |
Put it in CI, not in a pre-commit hook. Audits need network access and an up to date database, which makes them slow and flaky on a laptop. Slow local checks get disabled within a month, as our piece on pre-commit hooks that small teams keep using explains.
Fail the build only on high and critical findings at first. If you fail on everything from day one, the first run will show forty moderate warnings, someone will add a flag to ignore them all, and the check is dead before it starts.
Why Zero Audit Warnings Is the Wrong Goal
A lot of security advice treats a clean audit report as the finish line. In practice, chasing zero leads to worse decisions than living with a few known warnings.
Many advisories describe a flaw in a code path you never call. A regular expression denial of service bug in a command line argument parser, pulled in by your test runner, cannot be reached from the internet. Forcing an upgrade can break the test runner and leave odd pins nobody remembers.
Go gets this right: govulncheck reports only vulnerabilities in functions your code actually reaches. For other ecosystems, keep a short exceptions file that lists each ignored advisory ID, one sentence on why it does not apply and a review date no more than 90 days out. An ignored warning with a reason and an expiry is honest. A warning silenced by a blanket flag is a liability.
Check Three: Make Updates Small and Frequent
Teams sitting two major versions behind suffer most during a big advisory, because one patch becomes a migration done under pressure.
Turn on an automated update bot, either Dependabot or Renovate, with these settings as a starting point:
- Group all patch and minor updates for development dependencies into one weekly pull request.
- Open individual pull requests for production dependency updates so each can be reviewed and reverted alone.
- Set a minimum release age of three to seven days before the bot proposes a new version.
- Cap open update pull requests at five, so the queue never becomes wallpaper.
The minimum release age matters more than it looks. Most malicious releases are spotted and pulled from the registry within a few days. Waiting a week before adopting a new version costs you almost nothing and filters out a large share of that risk.
Check Four: Look at Who Maintains What You Add
Before adding a new direct dependency, spend two minutes on its repository. You are looking for a few signals, none of which is decisive alone:
- A commit or release within the last 12 months.
- More than one person with publish rights.
- Issues that get answered, even if slowly.
- A small number of its own dependencies. A date formatting helper that pulls in 40 packages is a warning sign.
Also ask if you need the package at all. A twenty line utility function copied into your codebase with its licence notice is easier to audit than a package you will never read.
Check Five: Keep Install Scripts and Tokens Apart
Malicious packages usually do their damage at install time, through postinstall scripts that read environment variables and send them somewhere. Your CI runner often has a registry token, a cloud deploy key and a database URL sitting in its environment during that step.
Split the pipeline so dependency installation runs in a job without deploy secrets, and only later jobs receive credentials. In npm you can disable lifecycle scripts by default in the project configuration file and allow them for the handful of packages that genuinely need a build step. If a token does leak, you will want the steps from our guide on rotating API keys in small engineering teams ready to go.
Frequently Asked Questions
Do we need a paid dependency scanner?
Not to start. The free audit commands and an update bot cover known vulnerabilities for most small teams. Paid tools earn their cost once you run dozens of repositories and need one dashboard or licence reporting.
How fast should we patch a critical advisory?
If the vulnerable code is reachable from the internet, aim for 48 hours. If it sits in a development tool or an unreachable path, record it in the exceptions file and fix it in the next normal update cycle.
Should we vendor all our dependencies?
Vendoring gives you an offline copy and a clear diff on every update, which some teams value. It also makes your repository much larger and does not remove the need to audit. A strict lockfile with checksums gives most of the benefit with less weight.
A One Afternoon Starting Point
Pick one repository this week. Confirm the lockfile is committed and every build step installs from it. Add the audit command to CI, failing on high and critical only. Switch on Renovate or Dependabot with a seven day release age. Write an empty exceptions file with a column for the review date. That is about three hours of work, and it moves you from hoping your dependencies are fine to knowing which ones are not. The rest of our security guides build on the same idea: small checks that run on their own beat big audits that run once.
More in Security
Security
Fail2ban Setup to Stop Brute Force Logins on a Small Server
Open the auth log on any public Linux server and you will find thousands of failed SSH logins from addresses you have never...
Security
SSH Key Authentication Setup for a Small Linux Server
Put a fresh Linux server on a public IP address and check the auth log an hour later.
Security
How Passkeys Replace Passwords Without the Jargon
You have probably been offered a passkey already, maybe when signing in somewhere and getting asked to use your face or...