dependency-vulnerability-triage
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseDependency Vulnerability Triage
依赖项漏洞分类处理
Convert vulnerability reports into actionable patch plans.
将漏洞报告转换为可执行的补丁计划。
Vulnerability Severity Matrix
漏洞严重性矩阵
typescript
// 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,
};
}typescript
// 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
审计报告解析器
typescript
// 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";
}typescript
// 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
补丁计划生成器
typescript
// 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`);
});
}typescript
// 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
安全升级顺序
typescript
// 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 expresstypescript
// 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 expressRisk Assessment
风险评估
typescript
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;
}typescript
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
自动化补丁脚本
bash
#!/bin/bashbash
#!/bin/bashscripts/apply-patches.sh
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..."
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
Generate patch plan
npm audit --json > audit-report.json
node scripts/generate-patch-plan.js --priority=$PRIORITY > patch-plan.json
npm audit --json > audit-report.json
node scripts/generate-patch-plan.js --priority=$PRIORITY > patch-plan.json
Apply patches
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"
undefinedwhile 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"
undefinedCI Vulnerability Gating
CI漏洞门禁
yaml
undefinedyaml
undefined.github/workflows/security-audit.yml
.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
});undefinedname: 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
});undefinedPatch Rollback Strategy
补丁回滚策略
markdown
undefinedmarkdown
undefinedVulnerability Patch Rollback Plan
漏洞补丁回滚计划
Before Patching
补丁前准备
- Create rollback tag
bash
git tag -a pre-security-patch-$(date +%Y%m%d) -m "Pre-patch checkpoint"
2. **Document current versions**
```bash
npm list --depth=0 > versions-before.txt
```
3. **Run baseline tests**
```bash
npm test > test-results-before.txt
```- 创建回滚标签
bash
git tag -a pre-security-patch-$(date +%Y%m%d) -m "Pre-patch checkpoint"
2. **记录当前版本**
```bash
npm list --depth=0 > versions-before.txt
```
3. **运行基准测试**
```bash
npm test > test-results-before.txt
```Rollback Steps
回滚步骤
If issues detected after patching:
-
Immediate revertbash
git revert HEAD git push origin main -
Redeploy previous versionbash
git checkout pre-security-patch-$(date +%Y%m%d) npm ci npm run build npm run deploy -
Verify rollbackbash
npm test npm run smoke-tests -
Incident report
- Document what failed
- Update patch plan with new risk factors
- Schedule retry with additional testing
undefined如果补丁后检测到问题:
-
立即回退bash
git revert HEAD git push origin main -
重新部署旧版本bash
git checkout pre-security-patch-$(date +%Y%m%d) npm ci npm run build npm run deploy -
验证回滚结果bash
npm test npm run smoke-tests -
事件报告
- 记录失败内容
- 用新的风险因素更新补丁计划
- 安排额外测试后重试
undefinedBest 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
undefined- 已定义严重性矩阵
- 已实现审计解析器
- 已生成补丁计划
- 已确定安全升级顺序
- 已完成风险评估
- 已识别破坏性变更
- 已编写自动化补丁脚本
- 已配置CI漏洞门禁
- 已记录回滚策略
- 已通知团队关键补丁信息
undefined