A working dependency-CVE strategy that doesn't drown your team
Run npm audit against any non-trivial JavaScript project. You'll get a list of 40-200 vulnerabilities. Run pip-audit against Python. Same story. bundler-audit for Ruby, cargo audit for Rust — same. Most teams react in one of three ways: (a) try to fix everything and quickly burn out, (b) ignore everything and rationalize it, or (c) npm audit fix --force and break production. None of these work.
Here's a working strategy.
The framing change: not every CVE is a vulnerability
The single most important shift: a CVE listed against a dependency is not automatically a vulnerability in your application. The CVE describes a flaw in the dependency's code. Whether it's exploitable in your deployment depends on three things:
- Whether you use the affected code path. A
lodashCVE in a method you never call doesn't affect you. - Whether the dependency is reachable from untrusted input. A vulnerable
image-processinglibrary in a server-side dev tool you only run locally is different from the same library processing user-uploaded photos in production. - Whether the attack scenario requires conditions you don't have. A "DoS via specific malformed JSON" CVE in a library only used to parse your own internal config files is not exploitable in your context.
Most CVE auditing tools don't make these distinctions. They flag every transitive vulnerability regardless of reachability. This is why the lists are so long and so demoralizing.
The triage workflow that scales
A working triage flow:
1. Filter to direct + first-tier transitive dependencies first. Anything 4+ levels deep is often not under your control. You'll patch these when the intermediate library updates. Spend triage time on what you can actually act on.
2. Filter by severity. Start with Critical and High. The Medium/Low list is real but should be a quarterly cleanup task, not an interrupt.
3. For each remaining CVE, answer three questions in 30 seconds each:
- Do we use the affected feature? (Look at the CVE description and the methods/modules it names.)
- Is the affected code path reachable from untrusted input?
- Does the attack scenario match our deployment?
If all three are yes → fix this week. If two are yes → fix this month. If one is yes → accept the risk with a documented note; revisit next quarter. If none → ignore with a documented note.
4. Track every decision. Most CVE-management tools (Snyk, Dependabot, Mend) let you suppress a vulnerability with a justification. Use that. The justification is what your auditor wants to see during SOC 2 review — not zero open CVEs, but evidence that every open CVE was consciously decided about.
What auditors actually want to see
Contrary to many founders' fears, SOC 2 auditors don't expect zero open CVEs. They expect:
- A documented severity → SLA mapping. Example: critical 7 days, high 30 days, medium 90 days, low quarterly review.
- A tracker showing CVEs are being addressed within the SLA. Or accepted with a written justification if not.
- Periodic re-review of the accepted ones (usually quarterly).
- A real human accountable for the process (the auditor will ask who).
That's it. They will not count your open vulns and fail you for having too many. They will check that the process is real and being followed.
The tools that actually help
For the scan: Most languages have a free first-party tool that's good enough. npm audit, pip-audit, bundler-audit, cargo audit, govulncheck. For multi-language repos, Trivy, Grype, and osv-scanner all work well.
For the triage workflow: Snyk and Mend (formerly WhiteSource) are the commercial leaders. They cost money but integrate well into PR workflows. Dependabot (GitHub) and Renovate (open source) are excellent for the auto-update side. For most early-stage SaaS, Dependabot + a manual quarterly triage sprint is enough.
For the SBOM: If you're selling to enterprise, generate a Software Bill of Materials. Syft (free, open source) does this in 30 seconds. Most enterprise security reviews now ask for one.
The hard cases
Old major versions you can't upgrade. You're on Rails 5, the CVE requires Rails 7. The fix is "upgrade to Rails 7", which is a quarter of work. Document the decision, get the upgrade on a roadmap, accept the risk explicitly. Don't pretend.
CVEs that only matter for specific environments. "SSRF via host header" matters if you have an attacker-controlled host header reaching your dependency. If your reverse proxy strips host headers (most do), the CVE doesn't apply. Document the mitigating control.
Disputed CVEs. Sometimes the maintainer of a library disputes the CVE on the grounds that the "vulnerability" requires the developer to deliberately misuse the library. Read the dispute. If you agree, document the position and suppress. If you disagree, treat it as a real CVE.
A reasonable cadence for a small SaaS team
For a team of 5-20 engineers without a dedicated security person:
- Daily: Dependabot opens PRs for new vulnerabilities at critical + high severity. On-call engineer reviews and merges or rationalizes.
- Weekly: 30 minutes in the engineering meeting reviewing critical + high CVE status. Anything past SLA gets explicit owner + date.
- Monthly: 1 hour reviewing the medium-severity backlog.
- Quarterly: Half-day sprint clearing the low-severity backlog and re-reviewing accepted-risk decisions.
- Annually: Full dependency audit during the pen test. CyberGrid's automated assessment includes a
retire.js/CVE scan as part of every engagement.
This is what passing-SOC-2-without-suffering looks like. The teams that drown are the ones who try to be at zero open vulns; the teams that fail audits are the ones who can't show a process at all. Land in the middle and you're fine.
Want to see this in practice?
Run a free single-domain scan in three minutes — same engine, smaller scope, no signup. We'll email you the PDF.
Run a free scan