How to Catch Vulnerable Dependencies with Snyk in CI
Most supply-chain incidents trace back to a dependency you never chose. A package you depend on depends on a package that got hijacked. Your package.json looks clean; your tree does not.
Direct answer: to catch vulnerable dependencies with Snyk in CI, add snyk/actions/node@master as a step after install, set SNYK_TOKEN as a repo secret, and use --severity-threshold=high so the build only fails on issues you will actually fix. The free plan covers 5 developers and 400 open-source tests per month.
How do you add Snyk to a GitHub Actions workflow?
Four lines in the workflow plus one secret.
- uses: snyk/actions/node@master
env:
SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
with:
args: --severity-threshold=high --fail-on=upgradable
Get the token from app.snyk.io → Account settings → General → Auth token. Add it under repo Settings → Secrets and variables → Actions.
Place the step after npm ci, not before. Snyk reads the resolved tree, so it needs node_modules present. Running it against a bare lockfile works too but misses peer resolution differences.
For GitLab, the equivalent is a job that runs npx snyk test --severity-threshold=high. For Jenkins, npm install -g snyk then snyk test. The CLI is one binary with the same flags everywhere.
Why does --fail-on=upgradable matter?
Without it, you fail the build on every vulnerability, including ones with no fix available. Teams hit that in week one, disable the check, and never turn it back on.
--fail-on=upgradable narrows the failure to things you can actually act on. Vulnerabilities with no patch get reported in the Snyk dashboard and tracked, but they do not block a release. That is the difference between a check that survives six months and one that gets commented out.
How do you deal with transitive vulnerabilities?
This is where a scanner earns its price. A direct dependency with a CVE is a version bump. A transitive one four levels deep needs either an override or an upstream fix.
npm supports overrides:
{"overrides": {"minimist": "^1.2.8"}}
Python has no equivalent in requirements.txt, so you pin the transitive package directly in your requirements file. Snyk’s suggested fix is usually an override pin — copy it, test, and keep a comment explaining why it exists.
Beware overrides on packages with breaking changes. Forcing lodash@4.17.21 is safe; forcing a major version of an XML parser may not be.
What does the free plan actually cover?
| Plan | Price | Open-source tests | Private repos |
|---|---|---|---|
| Free | $0 | 400/month | unlimited repos, limited scans |
| Team | $25/dev/month | unlimited | unlimited |
| Enterprise | custom | unlimited + SSO | unlimited |
400 tests per month is about 13 CI runs per day for a single project. Run it on pull requests only, not on every push to main, and you stay inside the limit comfortably.
Container scanning on the free tier is heavily limited — 100 tests per month. If you build Docker images, budget for it.
Should you scan containers too?
Yes, and separately. Your base image is where the OS-level CVEs live — glibc, OpenSSL, the things that do not show up in npm audit at all.
snyk container test myapp:latest --severity-threshold=high
Pin a specific base image tag rather than latest. A rebuild that silently pulls a newer base is a deployment of code nobody reviewed, and it can introduce vulnerabilities as easily as remove them.
Where Snyk is the wrong tool
Static analysis of your own code is a separate product (Snyk Code) and a separate bill. If your priority is injection flaws in your handlers rather than CVEs in dependencies, pick a dedicated SAST tool first.
Also, Snyk finds known CVEs. It does not find a malicious package that has not been reported yet. For that you need lockfile review, provenance checks, and a policy about how dependencies get added. npm audit covers the same CVE database for free — Snyk’s advantage is the fix suggestions, reachability analysis, and the dashboard across repos.
For a single small project, npm audit --audit-level=high in CI costs nothing and catches most of it. Snyk starts paying for itself around the third repository.
Key Takeaways
- Add the Snyk action after
npm ci; the resolved dependency tree is what it scans. --fail-on=upgradablekeeps the check useful instead of blocking on unfixable issues.- Transitive CVEs need
overrides(npm) or explicit pins (pip). - Free tier: 5 developers, 400 open-source tests per month — run on PRs only.
- Scan containers separately; OS-level CVEs never appear in package manifests.
