Vulnerability Management¶
Vulnerabilities are inevitable. Every dependency you use has bugs, and some of those bugs are security vulnerabilities. What matters is how quickly you can find them, assess them, and respond.
Not If, When
Vulnerability scanning without a plan to fix things is just anxiety automation. I've watched organizations generate hundreds of vulnerability alerts and act on exactly zero of them. The scanner isn't the point. The response process is.
The Vulnerability Lifecycle¶
Discovery¶
Vulnerabilities are found through:
- Security research — Researchers probing for weaknesses
- Bug bounties — Paid programs that incentivize finding flaws
- Internal audits — Code review and security testing
- Accidents — Someone notices something odd (xz utils was found during performance debugging)
- Active exploitation — The worst case: discovered because it's being used
Disclosure¶
Once found, vulnerabilities go through disclosure:
- Private disclosure — Researcher contacts maintainer, gives time to patch
- Coordinated disclosure — Researcher, maintainer, and affected parties coordinate timing
- Public disclosure — Details released publicly (ideally after patch available)
- Zero-day — Vulnerability known to attackers before defenders
The time between disclosure and your patch is when you're most vulnerable.
Assignment¶
Vulnerabilities get identifiers:
- CVE — Common Vulnerabilities and Exposures (CVE-YEAR-NUMBER)
- GHSA — GitHub Security Advisory
- Ecosystem-specific — npm advisories, PyPI advisories, etc.
CVE is the canonical identifier, but not all vulnerabilities get CVEs, and assignment can be slow.
Scoring¶
Severity is rated using CVSS (Common Vulnerability Scoring System):
| Score | Severity | Meaning |
|---|---|---|
| 9.0-10.0 | Critical | Drop everything, patch now |
| 7.0-8.9 | High | Patch soon, within days |
| 4.0-6.9 | Medium | Patch in normal cycle |
| 0.1-3.9 | Low | Patch when convenient |
CVSS scores are context-independent. A "Critical" vulnerability in a component you don't use in a vulnerable way might be lower risk for you specifically. But CVSS gives you a starting point for triage.
Vulnerability Databases¶
CVE / NVD¶
CVE (cve.org) is the identifier system. NVD (nvd.nist.gov) is NIST's database that enriches CVE data with:
- CVSS scores
- Affected version ranges
- References and patches
- CWE classification
NVD is the authoritative source for vulnerability information, but it can lag behind initial disclosure.
GitHub Advisory Database¶
GitHub maintains a curated database covering:
- npm, PyPI, RubyGems, Maven, Go, Rust, and more
- Powers Dependabot alerts
- Community-contributed advisories
Often faster than NVD for ecosystem-specific vulnerabilities.
Ecosystem-Specific Databases¶
| Ecosystem | Database |
|---|---|
| npm | npm Advisories |
| Python | PyPI Advisory Database, OSV |
| Go | Go Vulnerability Database |
| Rust | RustSec Advisory Database |
| Ruby | Ruby Advisory Database |
OSV (Open Source Vulnerabilities)¶
Google's aggregated database that normalizes vulnerabilities across ecosystems. Good for cross-ecosystem queries.
Vulnerability Scanning¶
When to Scan¶
On every commit: Catch new vulnerabilities introduced by dependency changes.
On schedule: Daily or weekly scans catch newly disclosed vulnerabilities in existing dependencies.
Before deployment: Gate deployments on vulnerability status.
On demand: When a major CVE drops, scan immediately.
Scanning Tools¶
Open Source¶
Grype (Anchore):
# Scan a directory
grype dir:./my-project
# Scan from SBOM
grype sbom:./sbom.json
# Scan container image
grype myimage:latest
Trivy (Aqua Security):
# Scan filesystem
trivy fs ./my-project
# Scan container image
trivy image myimage:latest
# Scan as part of CI
trivy fs --exit-code 1 --severity HIGH,CRITICAL .
OSV-Scanner (Google):
Built-in Tools¶
npm:
pip:
Bundler (Ruby):
Commercial (Free Tiers Available)¶
- Snyk — Comprehensive SCA with good ecosystem support
- Dependabot — GitHub-integrated, automatic PRs
- Renovate — Self-hostable, highly configurable
- Socket — Focuses on supply chain attacks, not just CVEs
CI/CD Integration¶
Example GitHub Actions workflow:
name: Security Scan
on:
push:
branches: [main]
pull_request:
schedule:
- cron: '0 6 * * *' # Daily at 6 AM
jobs:
vulnerability-scan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Run Trivy vulnerability scanner
uses: aquasecurity/trivy-action@master
with:
scan-type: 'fs'
scan-ref: '.'
severity: 'HIGH,CRITICAL'
exit-code: '1' # Fail on high/critical
The Response Workflow¶
When a vulnerability is discovered:
1. Triage¶
Are we affected?
- Do we use the affected component?
- Do we use the affected version?
- Do we use the affected functionality?
A vulnerability in a logging library's UDP handler doesn't affect you if you only use file logging.
Is it exploitable in our context?
- Is the vulnerable code path reachable?
- Do we have mitigating controls?
- What's the attack vector?
2. Assess¶
What's the blast radius?
- Which systems are affected?
- What data could be exposed?
- What's the worst-case impact?
What's the fix?
- Is there a patched version?
- Is there a workaround?
- How hard is the upgrade?
3. Decide¶
Based on severity, exploitability, and fix difficulty:
| Severity | Exploitable? | Fix Available? | Action |
|---|---|---|---|
| Critical | Yes | Yes | Patch immediately |
| Critical | Yes | No | Mitigate, monitor, escalate |
| Critical | No | Yes | Patch within days |
| High | Yes | Yes | Patch within a week |
| Medium | — | Yes | Include in next release |
| Low | — | — | Track, don't rush |
4. Remediate¶
Update the dependency:
If update breaks things: - Can you pin to a fixed version that's still compatible? - Can you fork and patch? - Can you disable the vulnerable feature? - Can you add mitigating controls?
5. Verify¶
- Confirm the patched version is deployed
- Re-scan to verify vulnerability is resolved
- Check that the fix didn't break functionality
- Update your SBOM
6. Document¶
For audit trails and future reference:
- What vulnerability was found?
- When was it discovered?
- What was the decision?
- When was it remediated?
- Who approved the decision?
When You Can't Update¶
Sometimes you can't just update:
- Breaking changes — New version isn't compatible
- Transitive dependency — You don't control the version directly
- No fix available — Maintainers haven't patched it
- Abandoned package — No one is maintaining it
Options¶
Fork and patch: Clone the repository, apply the fix yourself, point your dependency at your fork.
This works but creates maintenance burden—you now own that fork.
Override transitive versions: Force a specific version of a transitive dependency.
Test thoroughly—version overrides can break things.
Mitigating controls: If you can't patch, reduce risk:
- Network segmentation
- Input validation
- WAF rules
- Enhanced monitoring
Accept the risk: Sometimes the risk is acceptable. Document:
- What risk you're accepting
- Why it's acceptable
- What would change your assessment
- When you'll revisit
Staying Ahead¶
Automated Updates¶
Dependabot or Renovate can create PRs automatically:
# .github/dependabot.yml
version: 2
updates:
- package-ecosystem: "npm"
directory: "/"
schedule:
interval: "weekly"
open-pull-requests-limit: 10
Automated PRs plus good test coverage means updates can often merge without manual intervention.
Security Alerts¶
Subscribe to security announcements:
- GitHub Security Advisories (automatic for repos)
- Mailing lists for critical dependencies
- CISA alerts for high-profile vulnerabilities
Regular Audits¶
Even with automated scanning, periodic manual review helps:
- Are we using any abandoned packages?
- Are any dependencies far behind current versions?
- Have any maintainers changed recently?
- Was code AI-generated? Check for deprecated APIs, insecure defaults, missing validation
AI-generated code deserves special attention during audits. AI models learn from training data that may include outdated patterns, deprecated libraries, or code with known vulnerabilities. Review AI-generated sections for security anti-patterns the AI might have learned from dated examples. See Vibe Coding for more.
The Real Disaster
I've been through enough vulnerability fire drills to know the difference between teams that handle them well and teams that don't.
The teams that handle them well have: an inventory of what they're running (SBOMs), automated scanning that runs continuously, a triage process that separates "patch now" from "track and plan," and test coverage that gives confidence in updates.
The teams that struggle have none of these, and they discover both the vulnerability and the difficulty of patching at the same time.
The vulnerability itself is rarely the disaster. The disaster is not knowing you're affected, or knowing but being unable to patch safely. Fix those problems before the next critical CVE drops.
Quick Reference¶
Severity Response Times¶
| CVSS | Severity | Target Response |
|---|---|---|
| 9.0+ | Critical | Hours to days |
| 7.0-8.9 | High | Days to 1 week |
| 4.0-6.9 | Medium | 2-4 weeks |
| <4.0 | Low | Next release cycle |
Scanning Tool Comparison¶
| Tool | Type | Best For |
|---|---|---|
| Trivy | Multi-purpose | General scanning, containers |
| Grype | Multi-purpose | SBOM-based scanning |
| npm audit | Built-in | Node.js projects |
| pip-audit | CLI | Python projects |
| Snyk | Commercial | Enterprise, broad coverage |
| Dependabot | GitHub | Automatic PRs |