Skip to main content

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 audit and 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

SeverityMTTRJustification
Critical24 hoursRCE or auth bypass; requires immediate action
High48 hoursSignificant flaw; fix within business day
Medium2 weeksModerate risk; schedule in sprint
Low4 weeksEdge case or low exploitability; regular cycle

Severity Levels

LevelCVSSDefinitionExample
Critical9.0–10.0Immediate threat to system integrityRCE in build tool, remote code execution
High7.0–8.9Significant security flawAuth bypass, information disclosure
Medium4.0–6.9Moderate risk; context-dependentModerate vulnerability; harder to exploit
Low0.1–3.9Low risk; edge casesDenial of service in rare condition, low impact

Prerequisite Access & Tools

  • Access: GitHub repo, ability to review/merge PRs, pnpm CLI
  • 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

  1. 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 audit output lists vulnerable package
  2. Extract key details:

    • Package name
    • Current version in lock file
    • CVE ID (if available)
    • CVSS score or severity label
    • Affected version range
  3. 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

  1. If CVSS score provided: Use it to categorize (9.0+ = Critical, etc.)

  2. 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?)
  3. 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 next package (core framework) → CRITICAL
  • Reachable: Vulnerability in axios used in src/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:

  1. Check available versions:

    npm info [package] versions --json | tail -20
    # or via Dependabot PR (usually shows available versions)
  2. Update the package:

    pnpm add [package]@latest
    # or to a specific version:
    pnpm add [package]@4.18.2
  3. 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
  4. Review changes:

    • Check pnpm-lock.yaml for transitive dependency changes
    • Verify no breaking changes to app code
    • Look for new vulnerabilities: pnpm audit should pass
  5. 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

2b) Option 2: Replace the Package

When: No fix available and vulnerability is critical.

Steps:

  1. Find an alternative package:

    • Search npm registry for similar functionality
    • Verify the alternative has no known vulnerabilities
    • Check maintenance status and community adoption
  2. Example: If [vulnerable-package] has no fix and is critical:

    pnpm remove [vulnerable-package]
    pnpm add [replacement-package]
  3. 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
  4. Test thoroughly:

    pnpm quality
    pnpm test:unit
    pnpm build
  5. Create PR with migration details.

2c) Option 3: Accept Risk

When: Update breaks app or risk is acceptable.

Steps:

  1. 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)?
  2. 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]
  3. Update risk register: Add to docs/40-security/risk-register.md

  4. Create tracking issue for future fix.

2d) Option 4: Disable Feature

When: Feature is non-essential and vulnerable.

Steps:

  1. Identify feature: What functionality depends on this package?

  2. Disable temporarily:

    # Example: disable analytics if vulnerable
    # In src/app/layout.tsx:
    // <Analytics /> — temporarily disabled due to CVE-XXXX
  3. 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

  1. 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
  2. Run audit:

    pnpm audit
    # Should show no new vulnerabilities introduced
  3. Verify tests pass:

    pnpm test:unit
    pnpm playwright test # if applicable

3b) Check for Breaking Changes

  1. Review PR diff:

    • Are there unexpected changes in lock file?
    • Did transitive dependencies change significantly?
  2. Test critical flows:

    • If it's a framework update: test build, deploy, routes
    • If it's a library update: test features that use it
  3. Manual smoke test:

    pnpm dev
    # Visit http://localhost:3000 and click through key routes
    # Verify no console errors or warnings

3c) Deploy to Staging

  1. Merge to staging:

    git checkout staging
    git merge --no-ff [your-branch]
    git push origin staging
  2. Monitor staged environment:

    • Verify deployment succeeds
    • Check logs for errors
    • Test critical routes on staging domain
  3. 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:

  1. Detection: How did we find this vulnerability?

    • Dependabot? Manual audit? Third-party report?
    • If manual: Was process efficient? Should we automate?
  2. 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.
  3. 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:

  1. Identify breaking change: git diff package.json
  2. Review package changelog for migration guide
  3. Update code to match new API
  4. Re-run tests
  5. 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:

  1. Check what the conflict is:

    pnpm why [conflicting-package]
  2. Update all conflicting packages together:

    pnpm add [package]@latest [conflicting-package]@latest
  3. If still unresolved: Override in pnpm overrides section of package.json (use cautiously)

Issue: No Fix Available for Critical CVE

Symptom: CVE is critical; vulnerability remains unfixed in all versions.

Resolution:

  1. Immediate: Disable feature or remove package if possible
  2. Short-term: Accept risk with documented mitigation
  3. Long-term:
    • Monitor package for fix
    • Plan migration to alternative
    • Set reminder for 30-day re-assessment

References