---
title: 'Vulnerability Management Pattern'
description: 'How to keep the system patched without bottlenecking on a person watching CVE feeds.'
---

# Vulnerability Management Pattern

How to keep the system patched without bottlenecking on a person watching CVE feeds.

## TL;DR (human)

Three legs: an SBOM (you know what you ship), automated CVE triage (Dependabot / Renovate / Snyk feeding into your tracker), and signed releases (consumers can verify what you shipped). Patch SLA varies by severity. Supply chain attacks treated as first-class threats — signed deps, lock files committed, install scripts inspected.

## For agents

### Three legs

| Leg | What it answers | How |
|---|---|---|
| **SBOM** (Software Bill of Materials) | What did we ship? | Generate per release; store + sign |
| **Triage pipeline** | Are any of those things vulnerable? | Automated scanners + watchlists |
| **Signed releases** | Did the consumer get what we shipped? | Sigstore / cosign / GPG attestation |

Without all three, supply-chain attacks become invisible.

### SBOM

Generated at build time. Standard formats: CycloneDX, SPDX.

Contents:

- Every direct and transitive dependency.
- Version pinned (lock-file derived).
- Hash / checksum.
- License.
- Source repo / package URL.

The SBOM is committed at release tag (or attached as an artifact). Customers / auditors / regulators can request it.

Tooling: `npm sbom`, `cdxgen`, `syft`.

### CVE triage

Continuous scan against the SBOM:

- **Dependabot** / **Renovate**: open PRs for patch / minor / major bumps. Per-package config.
- **Snyk** / **GitHub Advanced Security** / **OSV-Scanner**: scan + alert on CVEs.
- **Trivy** / **Grype**: scan container images.

Triage flow:

1. Scanner posts to a tracker (issue, slack channel, dashboard).
2. **Severity classified**: critical / high / medium / low (CVSS-based, with project-specific adjustments).
3. **Owner assigned** (per package, per service).
4. **Patch SLA enforced**:
   - Critical (RCE in your trust boundary): 24h.
   - High: 7 days.
   - Medium: 30 days.
   - Low: next release cycle.
5. **Verification**: after patch, re-scan; CVE no longer fires.

A CVE past its SLA is a tracked overdue item; escalation path defined.

### Severity adjustments

CVSS does not know your context. Adjust based on:

- **Reachability**: is the vulnerable code path actually called? An unreachable CVE is lower priority.
- **Exposure**: is the vulnerable surface internet-facing or behind auth?
- **Mitigation in place**: WAF / firewall / sandbox / input validation that closes the vector.

Document adjustments in the triage record. "CVE-XXX-XXXX downgraded from High to Medium because \<reason\>" — auditable.

### Patch cadence (proactive)

Beyond reactive CVE response, schedule:

- **Weekly**: patch + minor bumps via Renovate / Dependabot auto-merge for low-risk packages.
- **Monthly**: review pending major bumps; pick low-hanging migrations.
- **Quarterly**: review packages with no upstream releases in 12+ months (abandoned dependency risk).

Lagging patches is how known-vulnerable code persists.

### Lock files

Commit the lock file (`pnpm-lock.yaml`, `package-lock.json`, `Cargo.lock`, etc.).

- The lock file IS the deterministic dependency tree.
- CI runs `--frozen-lockfile`: install must match the lock; mismatch fails.
- Lock file updates are diff-visible in PR; reviewers see exactly which transitives moved.

### Install-script discipline

NPM packages can run scripts on install. Audit:

- `pnpm config set ignore-scripts true` for default-deny; explicit allow per package.
- Or: review install-script content in CI; flag novel scripts.
- Be specifically wary of: post-install scripts that touch unexpected paths, fetch external URLs, modify shell config.

### Signed releases

Three signatures matter:

1. **Source release signature**: the release tag / commit is signed (`git tag -s` or sigstore `gitsign`).
2. **Artifact signature**: built binaries / packages are signed (`cosign sign`, `npm publish --provenance`).
3. **Attestation**: the build process itself is attested (which CI built it, which commit, which steps).

Consumers verify:

```bash
# Verify the published artefact came from the expected source + build pipeline
cosign verify <artefact>
```

This converts "we trust the registry" into "we verify the chain".

### Supply-chain threats

The big ones:

| Threat | Defense |
|---|---|
| Typosquatting (`reqeusts` vs `requests`) | Allowlist; explicit dep review on PRs adding new deps |
| Account hijack of maintainer | Signed releases; freeze versions during incident |
| Malicious update from compromised maintainer | Patch-lag-by-days policy (don't auto-update within 24-72h of release); package score tools |
| Build-system compromise | Reproducible builds; verify against attestation |
| Install-script attack | Disable install scripts; explicit allow |

### Vulnerability disclosure (inbound)

A `SECURITY.md` at repo root tells reporters:

- Where to report (`security@\<your-domain\>` is standard).
- What to include.
- Response SLA.
- Coordinated disclosure terms.
- (Optional) bug bounty program.

Process:

1. Acknowledge within 24h.
2. Triage within 72h.
3. Per severity:
   - Critical: emergency patch + coordinated disclosure within 7 days.
   - High: patch within 30 days.
   - Medium / low: next release.
4. Public advisory after fix lands.

### Dependency policy

Documented criteria for adding a new dependency:

- **Maturity**: > 1 year of releases; recent commits.
- **Maintenance**: active issue triage; not single-maintainer in a critical role.
- **Trust**: well-known author / org, or a thorough read of the source.
- **Necessity**: the functionality cannot be reasonably inlined.
- **License**: compatible with the project license.
- **Size**: a 2-line problem solved by a 200KB package is overhead.

Each new dep is an attack surface and a maintenance burden. Add deliberately.

### Common failure modes

- **No SBOM.** Vulnerability scan tells you which CVE exists; you cannot tell if you ship it. → Generate SBOM per release.
- **Dependabot PRs auto-merged with no test guard.** Compromised dep gets in. → Tests must pass; signed-by-known-maintainer check.
- **Critical CVE waiting weeks for triage.** No SLA; no escalation. → SLAs documented; escalation path.
- **Lock file not committed.** Different dev / CI sees different versions; CVE scan is unreliable. → Commit; CI uses frozen install.
- **Install scripts enabled by default.** One malicious dep runs arbitrary code on every install. → Disable by default.
- **No signature verification on download.** Consumer doesn't verify; spoofed artefact accepted. → Verify in CI / production install.

### Tooling stack (typical)

| Concern | Tool |
|---|---|
| SBOM generation | syft, cdxgen, `npm sbom` |
| CVE scan (deps) | osv-scanner, Snyk, Dependabot, Renovate |
| CVE scan (images) | trivy, grype |
| Signing | sigstore (cosign, gitsign), gpg |
| Provenance | SLSA framework, GitHub Actions native provenance |
| Bounty / disclosure | HackerOne, Bugcrowd, security@\<domain\> mailbox |

### See also

- [`universal.md`](./universal.md) — Rule 4 (vault refs), Rule 9 (rotation).
- [`audit-ledger-pattern.md`](./audit-ledger-pattern.md) — security incidents logged here.
- [`threat-model-template.md`](./threat-model-template.md) — supply-chain row in the threats table.
