Skip to content

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):

# Scan lock files
osv-scanner --lockfile=package-lock.json
osv-scanner --lockfile=requirements.txt

Built-in Tools

npm:

npm audit
npm audit --json  # Machine-readable
npm audit fix     # Auto-fix where possible

pip:

pip install pip-audit
pip-audit

Bundler (Ruby):

bundle audit check --update

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:

# npm
npm update vulnerable-package
npm audit fix

# pip
pip install --upgrade vulnerable-package

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.

# In requirements.txt
vulnerable-package @ git+https://github.com/yourfork/package@patched

This works but creates maintenance burden—you now own that fork.

Override transitive versions: Force a specific version of a transitive dependency.

// package.json (npm)
"overrides": {
  "vulnerable-package": "2.0.1"
}
# pyproject.toml (Poetry)
[tool.poetry.dependencies]
vulnerable-package = "^2.0.1"

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