- Dependency Vulnerability Triage
- Convert vulnerability reports into actionable patch plans.
- Vulnerability Severity Matrix
- // severity-matrix.ts
- export
- interface
- Vulnerability
- {
- id
- :
- string
- ;
- package
- :
- string
- ;
- currentVersion
- :
- string
- ;
- patchedVersion
- :
- string
- ;
- severity
- :
- "critical"
- |
- "high"
- |
- "medium"
- |
- "low"
- ;
- cvss
- :
- number
- ;
- cwe
- :
- string
- [
- ]
- ;
- exploitability
- :
- "high"
- |
- "medium"
- |
- "low"
- ;
- impact
- :
- string
- ;
- path
- :
- string
- [
- ]
- ;
- }
- export
- interface
- PatchPriority
- {
- vulnerability
- :
- Vulnerability
- ;
- priority
- :
- 1
- |
- 2
- |
- 3
- |
- 4
- ;
- reasoning
- :
- string
- ;
- patchWindow
- :
- "24h"
- |
- "1week"
- |
- "1month"
- |
- "next-release"
- ;
- breakingChange
- :
- boolean
- ;
- testingRequired
- :
- "minimal"
- |
- "moderate"
- |
- "extensive"
- ;
- }
- export
- function
- calculatePriority
- (
- vuln
- :
- Vulnerability
- )
- :
- PatchPriority
- {
- let
- priority
- :
- 1
- |
- 2
- |
- 3
- |
- 4
- =
- 4
- ;
- let
- patchWindow
- :
- "24h"
- |
- "1week"
- |
- "1month"
- |
- "next-release"
- =
- "next-release"
- ;
- let
- testingRequired
- :
- "minimal"
- |
- "moderate"
- |
- "extensive"
- =
- "minimal"
- ;
- // P1: Critical + High Exploitability + Production
- if
- (
- vuln
- .
- severity
- ===
- "critical"
- &&
- vuln
- .
- exploitability
- ===
- "high"
- &&
- isProductionDependency
- (
- vuln
- .
- package
- )
- )
- {
- priority
- =
- 1
- ;
- patchWindow
- =
- "24h"
- ;
- testingRequired
- =
- "moderate"
- ;
- }
- // P2: High + Medium/High Exploitability
- else
- if
- (
- vuln
- .
- severity
- ===
- "high"
- &&
- [
- "high"
- ,
- "medium"
- ]
- .
- includes
- (
- vuln
- .
- exploitability
- )
- )
- {
- priority
- =
- 2
- ;
- patchWindow
- =
- "1week"
- ;
- testingRequired
- =
- "moderate"
- ;
- }
- // P3: Medium or Low Exploitability High
- else
- if
- (
- vuln
- .
- severity
- ===
- "medium"
- ||
- (
- vuln
- .
- severity
- ===
- "high"
- &&
- vuln
- .
- exploitability
- ===
- "low"
- )
- )
- {
- priority
- =
- 3
- ;
- patchWindow
- =
- "1month"
- ;
- testingRequired
- =
- "minimal"
- ;
- }
- return
- {
- vulnerability
- :
- vuln
- ,
- priority
- ,
- reasoning
- :
- `
- ${
- vuln
- .
- severity
- }
- severity,
- ${
- vuln
- .
- exploitability
- }
- exploitability
- `
- ,
- patchWindow
- ,
- breakingChange
- :
- isBreakingChange
- (
- vuln
- .
- currentVersion
- ,
- vuln
- .
- patchedVersion
- )
- ,
- testingRequired
- ,
- }
- ;
- }
- Audit Report Parser
- // scripts/parse-audit.ts
- import
- {
- execSync
- }
- from
- "child_process"
- ;
- interface
- NpmAuditResult
- {
- vulnerabilities
- :
- Record
- <
- string
- ,
- any
- >
- ;
- metadata
- :
- {
- vulnerabilities
- :
- {
- critical
- :
- number
- ;
- high
- :
- number
- ;
- moderate
- :
- number
- ;
- low
- :
- number
- ;
- }
- ;
- }
- ;
- }
- function
- parseNpmAudit
- (
- )
- :
- Vulnerability
- [
- ]
- {
- const
- auditOutput
- =
- execSync
- (
- "npm audit --json"
- ,
- {
- encoding
- :
- "utf-8"
- }
- )
- ;
- const
- audit
- :
- NpmAuditResult
- =
- JSON
- .
- parse
- (
- auditOutput
- )
- ;
- const
- vulnerabilities
- :
- Vulnerability
- [
- ]
- =
- [
- ]
- ;
- Object
- .
- entries
- (
- audit
- .
- vulnerabilities
- )
- .
- forEach
- (
- (
- [
- pkg
- ,
- data
- ]
- )
- =>
- {
- vulnerabilities
- .
- push
- (
- {
- id
- :
- data
- .
- via
- [
- 0
- ]
- ?.
- url
- ||
- `
- vuln-
- ${
- pkg
- }
- `
- ,
- package
- :
- pkg
- ,
- currentVersion
- :
- data
- .
- range
- ,
- patchedVersion
- :
- data
- .
- fixAvailable
- ?.
- version
- ||
- "N/A"
- ,
- severity
- :
- data
- .
- severity
- ,
- cvss
- :
- data
- .
- via
- [
- 0
- ]
- ?.
- cvss
- ?.
- score
- ||
- 0
- ,
- cwe
- :
- data
- .
- via
- [
- 0
- ]
- ?.
- cwe
- ||
- [
- ]
- ,
- exploitability
- :
- determineExploitability
- (
- data
- )
- ,
- impact
- :
- data
- .
- via
- [
- 0
- ]
- ?.
- title
- ||
- "Unknown"
- ,
- path
- :
- data
- .
- via
- .
- map
- (
- (
- v
- :
- any
- )
- =>
- v
- .
- name
- )
- ,
- }
- )
- ;
- }
- )
- ;
- return
- vulnerabilities
- ;
- }
- function
- determineExploitability
- (
- data
- :
- any
- )
- :
- "high"
- |
- "medium"
- |
- "low"
- {
- // Check if actively exploited
- if
- (
- data
- .
- via
- [
- 0
- ]
- ?.
- findings
- ?.
- some
- (
- (
- f
- :
- any
- )
- =>
- f
- .
- exploit
- ===
- "proof-of-concept"
- )
- )
- {
- return
- "high"
- ;
- }
- // Check CVSS exploitability subscore
- const
- exploitScore
- =
- data
- .
- via
- [
- 0
- ]
- ?.
- cvss
- ?.
- exploitabilityScore
- ;
- if
- (
- exploitScore
- >
- 3.5
- )
- return
- "high"
- ;
- if
- (
- exploitScore
- >
- 2.0
- )
- return
- "medium"
- ;
- return
- "low"
- ;
- }
- Patch Plan Generator
- // scripts/generate-patch-plan.ts
- interface
- PatchPlan
- {
- immediate
- :
- PatchPriority
- [
- ]
- ;
- // P1 - 24h
- shortTerm
- :
- PatchPriority
- [
- ]
- ;
- // P2 - 1 week
- mediumTerm
- :
- PatchPriority
- [
- ]
- ;
- // P3 - 1 month
- longTerm
- :
- PatchPriority
- [
- ]
- ;
- // P4 - next release
- breakingChanges
- :
- PatchPriority
- [
- ]
- ;
- safeUpgrades
- :
- PatchPriority
- [
- ]
- ;
- }
- function
- generatePatchPlan
- (
- vulnerabilities
- :
- Vulnerability
- [
- ]
- )
- :
- PatchPlan
- {
- const
- prioritized
- =
- vulnerabilities
- .
- map
- (
- calculatePriority
- )
- ;
- return
- {
- immediate
- :
- prioritized
- .
- filter
- (
- (
- p
- )
- =>
- p
- .
- priority
- ===
- 1
- )
- ,
- shortTerm
- :
- prioritized
- .
- filter
- (
- (
- p
- )
- =>
- p
- .
- priority
- ===
- 2
- )
- ,
- mediumTerm
- :
- prioritized
- .
- filter
- (
- (
- p
- )
- =>
- p
- .
- priority
- ===
- 3
- )
- ,
- longTerm
- :
- prioritized
- .
- filter
- (
- (
- p
- )
- =>
- p
- .
- priority
- ===
- 4
- )
- ,
- breakingChanges
- :
- prioritized
- .
- filter
- (
- (
- p
- )
- =>
- p
- .
- breakingChange
- )
- ,
- safeUpgrades
- :
- prioritized
- .
- filter
- (
- (
- p
- )
- =>
- !
- p
- .
- breakingChange
- )
- ,
- }
- ;
- }
- function
- printPatchPlan
- (
- plan
- :
- PatchPlan
- )
- {
- console
- .
- log
- (
- "# Security Patch Plan\n"
- )
- ;
- console
- .
- log
- (
- "## 🚨 Immediate (24 hours)\n"
- )
- ;
- plan
- .
- immediate
- .
- forEach
- (
- (
- p
- )
- =>
- {
- console
- .
- log
- (
- `
- - **
- ${
- p
- .
- vulnerability
- .
- package
- }
- **
- ${
- p
- .
- vulnerability
- .
- currentVersion
- }
- →
- ${
- p
- .
- vulnerability
- .
- patchedVersion
- }
- `
- )
- ;
- console
- .
- log
- (
- `
- ${
- p
- .
- vulnerability
- .
- impact
- }
- `
- )
- ;
- console
- .
- log
- (
- `
- Breaking:
- ${
- p
- .
- breakingChange
- ?
- "YES ⚠️"
- :
- "No"
- }
- `
- )
- ;
- }
- )
- ;
- console
- .
- log
- (
- "\n## ⚡ Short-term (1 week)\n"
- )
- ;
- plan
- .
- shortTerm
- .
- forEach
- (
- (
- p
- )
- =>
- {
- console
- .
- log
- (
- `
- - **
- ${
- p
- .
- vulnerability
- .
- package
- }
- **
- ${
- p
- .
- vulnerability
- .
- currentVersion
- }
- →
- ${
- p
- .
- vulnerability
- .
- patchedVersion
- }
- `
- )
- ;
- }
- )
- ;
- console
- .
- log
- (
- "\n## 📅 Medium-term (1 month)\n"
- )
- ;
- plan
- .
- mediumTerm
- .
- forEach
- (
- (
- p
- )
- =>
- {
- console
- .
- log
- (
- `
- -
- ${
- p
- .
- vulnerability
- .
- package
- }
- ${
- p
- .
- vulnerability
- .
- currentVersion
- }
- →
- ${
- p
- .
- vulnerability
- .
- patchedVersion
- }
- `
- )
- ;
- }
- )
- ;
- console
- .
- log
- (
- "\n## ⚠️ Breaking Changes\n"
- )
- ;
- plan
- .
- breakingChanges
- .
- forEach
- (
- (
- p
- )
- =>
- {
- console
- .
- log
- (
- `
- -
- ${
- p
- .
- vulnerability
- .
- package
- }
- Review migration guide
`
)
;
}
)
;
}
Safe Upgrade Order
// Dependency graph-based upgrade order
interface
DependencyGraph
{
[
pkg
:
string
]
:
string
[
]
;
}
function
determineSafeUpgradeOrder
(
patches
:
PatchPriority
[
]
)
:
PatchPriority
[
]
[
]
{
const
graph
=
buildDependencyGraph
(
)
;
const
ordered
:
PatchPriority
[
]
[
]
=
[
]
;
// Level 0: No dependencies on other patches
const
level0
=
patches
.
filter
(
(
p
)
=>
!
hasUpgradeDependencies
(
p
.
vulnerability
.
package
,
patches
,
graph
)
)
;
ordered
.
push
(
level0
)
;
// Level 1+: Depends on previous levels
let
remaining
=
patches
.
filter
(
(
p
)
=>
!
level0
.
includes
(
p
)
)
;
let
level
=
1
;
while
(
remaining
.
length
0 && level < 10 ) { const currentLevel = remaining . filter ( ( p ) => canUpgradeNow ( p . vulnerability . package , ordered . flat ( ) , graph ) ) ; if ( currentLevel . length === 0 ) break ; // Circular dependency ordered . push ( currentLevel ) ; remaining = remaining . filter ( ( p ) => ! currentLevel . includes ( p ) ) ; level ++ ; } return ordered ; } // Example output: // Level 0: [lodash, axios] - No dependencies // Level 1: [express] - Depends on lodash // Level 2: [next] - Depends on express Risk Assessment interface RiskAssessment { package : string ; riskFactors : string [ ] ; riskScore : number ; // 1-10 mitigations : string [ ] ; } function assessUpgradeRisk ( patch : PatchPriority ) : RiskAssessment { const risks : string [ ] = [ ] ; let score = 0 ; // Check for breaking changes if ( patch . breakingChange ) { risks . push ( "Breaking changes detected" ) ; score += 4 ; } // Check for major version bump if ( isMajorVersionBump ( patch . vulnerability . currentVersion , patch . vulnerability . patchedVersion ) ) { risks . push ( "Major version upgrade" ) ; score += 3 ; } // Check usage patterns const usage = analyzePackageUsage ( patch . vulnerability . package ) ; if ( usage . importCount
50 ) { risks . push ( "Heavily used in codebase" ) ; score += 2 ; } // Check test coverage if ( usage . testCoverage < 0.7 ) { risks . push ( "Low test coverage" ) ; score += 2 ; } return { package : patch . vulnerability . package , riskFactors : risks , riskScore : Math . min ( score , 10 ) , mitigations : generateMitigations ( risks ) , } ; } function generateMitigations ( risks : string [ ] ) : string [ ] { const mitigations : string [ ] = [ ] ; if ( risks . includes ( "Breaking changes detected" ) ) { mitigations . push ( "Read CHANGELOG and migration guide" ) ; mitigations . push ( "Test on feature branch first" ) ; mitigations . push ( "Deploy to staging before production" ) ; } if ( risks . includes ( "Heavily used in codebase" ) ) { mitigations . push ( "Run full test suite" ) ; mitigations . push ( "Perform manual smoke tests" ) ; mitigations . push ( "Monitor error rates after deploy" ) ; } if ( risks . includes ( "Low test coverage" ) ) { mitigations . push ( "Add tests for critical paths" ) ; mitigations . push ( "Extend monitoring" ) ; } return mitigations ; } Automated Patch Script
!/bin/bash
scripts/apply-patches.sh
set -e PRIORITY = $1
immediate, short-term, medium-term
if [ -z " $PRIORITY " ] ; then echo "Usage: ./apply-patches.sh [immediate|short-term|medium-term]" exit 1 fi echo "🔧 Applying $PRIORITY patches..."
Generate patch plan
npm audit --json
audit-report.json node scripts/generate-patch-plan.js --priority = $PRIORITY
patch-plan.json
Apply patches
while IFS = read -r package ; do echo "Updating $package ..."
Try to apply fix
npm audit fix --package-lock-only --package = $package
Run tests
if npm test ; then echo "✅ Tests passed for $package " git add package.json package-lock.json git commit -m "security: patch $package vulnerability" else echo "❌ Tests failed for $package - reverting" git checkout package.json package-lock.json fi done < < ( jq -r '.packages[]' patch-plan.json ) echo "✅ Patches applied" CI Vulnerability Gating
.github/workflows/security-audit.yml
name : Security Audit on : pull_request : schedule : - cron : "0 0 * * *"
Daily
jobs : audit : runs-on : ubuntu - latest steps : - uses : actions/checkout@v4 - uses : actions/setup - node@v4 with : node-version : "20" - name : Install dependencies run : npm ci - name : Run npm audit run : npm audit - - audit - level=moderate continue-on-error : true - name : Run Snyk test uses : snyk/actions/node@master env : SNYK_TOKEN : $ { { secrets.SNYK_TOKEN } } with : args : - - severity - threshold=high - name : Generate patch plan if : failure() run : npm run generate - patch - plan - name : Comment PR if : failure() && github.event_name == 'pull_request' uses : actions/github - script@v7 with : script : | const fs = require('fs'); const plan = fs.readFileSync('patch-plan.md', 'utf8'); github.rest.issues.createComment({ owner: context.repo.owner, repo: context.repo.repo, issue_number: context.issue.number, body: plan }); Patch Rollback Strategy
Vulnerability Patch Rollback Plan
Before Patching 1. ** Create rollback tag **
git tag -a pre-security-patch-$(date +%Y%m%d) -m "Pre-patch checkpoint"
Document current versions npm list --depth = 0
versions-before.txt Run baseline tests npm test
test-results-before.txt Rollback Steps If issues detected after patching: Immediate revert git revert HEAD git push origin main Redeploy previous version git checkout pre-security-patch- $( date +%Y%m%d ) npm ci npm run build npm run deploy Verify rollback npm test npm run smoke-tests Incident report Document what failed Update patch plan with new risk factors Schedule retry with additional testing
Best Practices
- Triage weekly: Review new vulnerabilities
- Prioritize by impact: Not just severity score
- Test before merging: Automated + manual testing
- Stage deployments: Dev → Staging → Production
- Monitor after patch: Watch error rates
- Document breaking changes: Migration guides
- Keep dependencies updated: Reduce vulnerability surface
Output Checklist
- [ ] Severity matrix defined
- [ ] Audit parser implemented
- [ ] Patch plan generated
- [ ] Safe upgrade order determined
- [ ] Risk assessment completed
- [ ] Breaking changes identified
- [ ] Automated patch script
- [ ] CI vulnerability gating
- [ ] Rollback strategy documented
- [ ] Team notified of critical patches