Why dependencies are a risk
A small Node.js app with 20 direct dependencies can pull in more than 500 transitive packages. Each one is code you run but did not write and rarely read. A vulnerability deep in the tree, such as Log4Shell in log4j, affects every app that uses it. Analysing dependencies is called SCA (Software Composition Analysis).
Lockfiles first
A lockfile records the exact version and hash of every resolved package: package-lock.json, yarn.lock, poetry.lock, Pipfile.lock, go.sum, Cargo.lock. Commit lockfiles to Git and install from them in CI:
npm ci # install exactly from package-lock.json, fail on mismatch
pip install -r requirements.txt --require-hashes
Without a lockfile each build may resolve different versions, and scanners do not know what you use.
Scanning tools
| Ecosystem | Command |
|---|---|
| npm | npm audit, npm audit --audit-level=high |
| Python | pip-audit -r requirements.txt |
| Go | govulncheck ./... (reports only functions actually called) |
| Multi-language | trivy fs ., osv-scanner scan -r ., OWASP Dependency-Check, Snyk |
govulncheck is a good example of reachability: it reports a CVE only when your code calls the vulnerable function, which cuts noise a lot.
Automatic updates
flowchart LR ADV["New advisory"] --> BOT["Dependabot / Renovate"] BOT --> PR["Pull request bumping the version"] PR --> CI["CI: tests + scan"] CI -- "green" --> M["Review and merge"]
# .github/dependabot.yml
version: 2
updates:
- package-ecosystem: npm
directory: /
schedule: { interval: weekly }
groups:
minor-and-patch: { update-types: [minor, patch] }
Renovate is more flexible, runs on GitLab, Azure DevOps and Bitbucket, and can auto-merge patch releases when tests pass. Small, regular updates are far easier than jumping three major versions after two years.
Good practice
- Keep dependencies few: do not add a whole library for a few-line function.
- Check a package's trustworthiness before adding it: maintainers, downloads, latest release, OpenSSF Scorecard.
- Check licences (GPL or AGPL may not suit a commercial product).
- Traps: scanning only direct dependencies; running
npm audit fix --forceblindly and breaking the app; ignoring Dependabot PRs until dozens pile up.