Runbook: Dependency Vulnerability Response
Purpose
Provide a deterministic procedure for responding to dependency vulnerabilities (CVEs, audit findings, security alerts). Ensures vulnerabilities are detected quickly, triaged consistently, and remediated with documented MTTR targets.
Scope
Use when
- CVE alert: Dependabot or GitHub Security creates a vulnerability PR
- Manual audit: Developer runs
pnpm auditand finds issues - Third-party report: External party reports a vulnerability in a dependency
- Security review: Periodic audit identifies overlooked CVE
Do not use when
- The issue is unrelated to dependencies (e.g., configuration, deploy, code bug)
- Dealing with infrastructure vulnerabilities (use Vercel/cloud provider runbooks)
MTTR Targets
| Severity | MTTR | Justification |
|---|---|---|
| Critical | 24 hours | RCE or auth bypass; requires immediate action |
| High | 48 hours | Significant flaw; fix within business day |
| Medium | 2 weeks | Moderate risk; schedule in sprint |
| Low | 4 weeks | Edge case or low exploitability; regular cycle |
Severity Levels
| Level | CVSS | Definition | Example |
|---|---|---|---|
| Critical | 9.0–10.0 | Immediate threat to system integrity | RCE in build tool, remote code execution |
| High | 7.0–8.9 | Significant security flaw | Auth bypass, information disclosure |
| Medium | 4.0–6.9 | Moderate risk; context-dependent | Moderate vulnerability; harder to exploit |
| Low | 0.1–3.9 | Low risk; edge cases | Denial of service in rare condition, low impact |
Prerequisite Access & Tools
- Access: GitHub repo, ability to review/merge PRs,
pnpmCLI - Tools:
pnpm audit,git log,grep, browser (CVE lookup) - Context: Package name, CVE ID (if known), affected versions
Procedure
Phase 0: Detection (≤5 min)
Goal: Identify and assess the vulnerability.
0a) Gather Information
-
Identify the vulnerability:
- Dependabot PR title: "Bump [package] from X to Y" with security context
- GitHub Security Alert: "CVE-XXXX-XXXXX in [package]"
- Manual audit:
pnpm auditoutput lists vulnerable package
-
Extract key details:
- Package name
- Current version in lock file
- CVE ID (if available)
- CVSS score or severity label
- Affected version range
-
Example Dependabot PR:
Title: Bump express from 4.17.0 to 4.18.2 (Security Update)
Body:
Dependabot has found a vulnerability in express.
CVE: CVE-2022-12345
Severity: High
CVSS: 7.5
Fixed in: 4.18.2
0b) Assess Severity
-
If CVSS score provided: Use it to categorize (9.0+ = Critical, etc.)
-
If not: Assess based on:
- Type of vulnerability (RCE > auth bypass > info disclosure)
- Reachability in portfolio app (dev-only vs runtime)
- Exploitability (is it actually exploitable in this context?)
-
Document initial severity (may be refined in triage)
Phase 1: Triage (≤1 hour)
Goal: Quickly decide on remediation strategy.
1a) Determine Reachability
Key Question: Is the vulnerable code path actually used by the Portfolio App?
Examples:
- ✅ Reachable: Vulnerability in
nextpackage (core framework) → CRITICAL - ✅ Reachable: Vulnerability in
axiosused insrc/lib/api.ts→ HIGH - ⚠️ Conditional: Vulnerability in optional dependency only used in dev → MEDIUM (downgrade)
- ❌ Unreachable: Vulnerability in test-only dependency (e.g.,
jest) → LOW (downgrade)
Audit the dependency tree:
# See which packages depend on the vulnerable package
pnpm why [vulnerable-package-name]
# Check if it's a dev dependency
grep -A5 '"devDependencies"' pnpm-lock.yaml | grep [package-name]
1b) Categorize & Prioritize
Decision Tree:
Is it Critical (RCE, auth bypass)?
├─ Yes → Remediate within 24 hours (PHASE 2)
├─ No: Is it High (significant flaw)?
│ ├─ Yes → Remediate within 48 hours (PHASE 2)
│ ├─ No: Is it Medium or Low?
│ │ ├─ Medium → Remediate within 2 weeks (PHASE 2)
│ │ ├─ Low → Address in next dependency cycle (PHASE 2, low priority)
Document decision in PR comment or issue.
1d) Framework CVE escalation (React2Shell-class)
If the CVE affects React/Next.js or server-rendering packages:
- Treat as Critical by default.
- Patch immediately and re-run full verification.
- Confirm CSP nonce, input validation, CSRF, and rate limiting controls remain intact.
1c) Create a Tracking Issue (if not auto-created by Dependabot)
**Title:** [Security] CVE-2022-12345 in [package] v4.17.0
**Severity:** High
**MTTR Target:** 48 hours
**Vulnerability:** [description from CVE database]
**Remediation Plan:** Update to [version] or [alternative]
**Status:** In Triage → Remediation → Verification → Closed
Phase 2: Remediation (≤2 hours for Critical)
Goal: Fix or mitigate the vulnerability.
2a) Option 1: Update the Package
When: A fixed version is available.
Steps:
-
Check available versions:
npm info [package] versions --json | tail -20
# or via Dependabot PR (usually shows available versions) -
Update the package:
pnpm add [package]@latest
# or to a specific version:
pnpm add [package]@4.18.2 -
Run CI locally:
pnpm install # Update lock file
pnpm quality # Lint, format, type check
pnpm test:unit # Ensure tests pass
pnpm build # Verify production build -
Review changes:
- Check
pnpm-lock.yamlfor transitive dependency changes - Verify no breaking changes to app code
- Look for new vulnerabilities:
pnpm auditshould pass
- Check
-
Create/update PR:
- Title:
fix: [package] security update to X.Y.Z (CVE-XXXX) - Description: Link to CVE, CVSS score, MTTR status
- Attach CI logs showing tests pass
- Title:
2b) Option 2: Replace the Package
When: No fix available and vulnerability is critical.
Steps:
-
Find an alternative package:
- Search npm registry for similar functionality
- Verify the alternative has no known vulnerabilities
- Check maintenance status and community adoption
-
Example: If
[vulnerable-package]has no fix and is critical:pnpm remove [vulnerable-package]
pnpm add [replacement-package] -
Update imports:
# Find all files importing the old package
grep -r "from '[vulnerable-package]'" src/
# Update imports to new package
sed -i "s/from '[vulnerable-package]/from '[replacement-package]/g" src/**/*.ts -
Test thoroughly:
pnpm quality
pnpm test:unit
pnpm build -
Create PR with migration details.
2c) Option 3: Accept Risk
When: Update breaks app or risk is acceptable.
Steps:
-
Assess acceptability:
- Is vulnerability likely to be exploited in this context?
- Is the package used in a way that triggers the vulnerability?
- Is there a mitigating factor (e.g., not exposed to user input)?
-
Document acceptance:
## Risk Acceptance Memo
**Vulnerability:** CVE-XXXX in [package]
**Reason for Acceptance:** [explanation]
**Mitigating Factors:** [why risk is acceptable]
**Review Date:** [when to re-assess]
**Owner:** [who approved] -
Update risk register: Add to docs/40-security/risk-register.md
-
Create tracking issue for future fix.
2d) Option 4: Disable Feature
When: Feature is non-essential and vulnerable.
Steps:
-
Identify feature: What functionality depends on this package?
-
Disable temporarily:
# Example: disable analytics if vulnerable
# In src/app/layout.tsx:
// <Analytics /> — temporarily disabled due to CVE-XXXX -
Create issue for re-enabling after fix.
Phase 3: Verification (≤30 min)
Goal: Confirm fix is effective and no new issues introduced.
3a) Verify Vulnerability is Fixed
-
Check lock file:
# Grep for the old version; should be gone (or only in transitive chain if unavoidable)
grep -c "[vulnerable-package]@[old-version]" pnpm-lock.yaml
# Should return 0 or be in a chain where direct dependency is fixed -
Run audit:
pnpm audit
# Should show no new vulnerabilities introduced -
Verify tests pass:
pnpm test:unit
pnpm playwright test # if applicable
3b) Check for Breaking Changes
-
Review PR diff:
- Are there unexpected changes in lock file?
- Did transitive dependencies change significantly?
-
Test critical flows:
- If it's a framework update: test build, deploy, routes
- If it's a library update: test features that use it
-
Manual smoke test:
pnpm dev
# Visit http://localhost:3000 and click through key routes
# Verify no console errors or warnings
3c) Deploy to Staging
-
Merge to staging:
git checkout staging
git merge --no-ff [your-branch]
git push origin staging -
Monitor staged environment:
- Verify deployment succeeds
- Check logs for errors
- Test critical routes on staging domain
-
If staging passes: Promote to production (separate PR or merge to main)
Phase 4: Postmortem (Async, within 1 week)
Goal: Learn from the vulnerability; improve processes.
4a) Document Learnings
Questions to answer:
-
Detection: How did we find this vulnerability?
- Dependabot? Manual audit? Third-party report?
- If manual: Was process efficient? Should we automate?
-
MTTR: Did we meet the target?
- If delayed: What blocked us? (e.g., breaking changes, review bottleneck)
- If fast: What went well? Document the process.
-
Prevention: Could we have prevented this?
- Was the dependency already outdated? (Update cadence issue)
- Was it a new CVE? (Unavoidable; process worked)
4b) Update Processes
- Audit Policy: Should dependency update cadence change?
- Runbook: Did this runbook need clarification? Update it.
- Risk Register: Any risks re-assessed or accepted? Update risk-register.md.
- Alert Threshold: Should low-severity CVEs trigger different workflow?
4c) Close Issue & PR
- Link PR to tracking issue
- Tag post-mortem findings
- Close with summary of actions taken
Troubleshooting
Issue: Breaking Changes After Update
Symptom: Update completes; tests fail or build breaks.
Resolution:
- Identify breaking change:
git diff package.json - Review package changelog for migration guide
- Update code to match new API
- Re-run tests
- If too complex: Consider alternative package or accept risk (documented)
Issue: Transitive Dependency Conflict
Symptom: Update package causes conflict in other dependencies.
Could not find a version for [other-package]
that satisfies [dependency of new-package]
Resolution:
-
Check what the conflict is:
pnpm why [conflicting-package] -
Update all conflicting packages together:
pnpm add [package]@latest [conflicting-package]@latest -
If still unresolved: Override in
pnpmoverrides section ofpackage.json(use cautiously)
Issue: No Fix Available for Critical CVE
Symptom: CVE is critical; vulnerability remains unfixed in all versions.
Resolution:
- Immediate: Disable feature or remove package if possible
- Short-term: Accept risk with documented mitigation
- Long-term:
- Monitor package for fix
- Plan migration to alternative
- Set reminder for 30-day re-assessment
References
- Risk register: ../../40-security/risk-register.md
- Security policies: ../../40-security/security-policies.md
- Secrets incident runbook: ./rbk-portfolio-secrets-incident.md
- CVE lookup: https://cve.mitre.org/
- npm audit docs: https://docs.npmjs.com/cli/v8/commands/npm-audit