dependency-vulnerability-triage
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**
```bash
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
1. **Triage weekly**: Review new vulnerabilities
2. **Prioritize by impact**: Not just severity score
3. **Test before merging**: Automated + manual testing
4. **Stage deployments**: Dev → Staging → Production
5. **Monitor after patch**: Watch error rates
6. **Document breaking changes**: Migration guides
7. **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
More from monkey1sai/openai-cli
multi-tenant-safety-checker
Ensures tenant isolation at query and policy level using Row Level Security, automated testing, and security audits. Prevents data leakage between tenants. Use for "multi-tenancy", "tenant isolation", "RLS", or "data security".
10modal-drawer-system
Implements accessible modals and drawers with focus trap, ESC to close, scroll lock, portal rendering, and ARIA attributes. Includes sample implementations for common use cases like edit forms, confirmations, and detail views. Use when building "modals", "dialogs", "drawers", "sidebars", or "overlays".
10eslint-prettier-config
Configures ESLint and Prettier for consistent code quality with TypeScript, React, and modern best practices. Use when users request "ESLint setup", "Prettier config", "linting configuration", "code formatting", or "lint rules".
9api-security-hardener
Hardens API security with rate limiting, input validation, authentication, and protection against common attacks. Use when users request "API security", "secure API", "rate limiting", "input validation", or "API protection".
9secure-headers-csp-builder
Implements security headers and Content Security Policy with safe rollout strategy (report-only → enforce), testing, and compatibility checks. Use for "security headers", "CSP", "HTTP headers", or "XSS protection".
9security-incident-playbook-generator
Creates response procedures for security incidents with containment steps, communication templates, and evidence collection. Use for "incident response", "security playbook", "breach response", or "IR plan".
9