实战 · 依赖审计
🎯 学习目标
- 从 package.json / requirements.txt 解析依赖清单
- 对接 OSV 或 npm audit 等 CVE 数据源
- 通过 MCP Tool 暴露 audit_dependencies 能力
- 输出按 CVSS 分级的 JSON/Markdown 审计报告
引言
本节把 MCP 落到真实安全场景:对项目依赖做 CVE 审计。你会实现「解析 → 查询 → 分级 → 报告」流水线,并封装为 MCP Tool,供 Cursor 或自研 Agent 调用。
章节正文
第 1 步:项目目标与 Tool 契约设计
对外暴露单一入口 Tool:
{
"name": "audit_dependencies",
"description": "扫描项目路径下的依赖锁文件,返回已知 CVE 列表与修复建议",
"inputSchema": {
"type": "object",
"properties": {
"projectPath": { "type": "string", "description": "项目根目录绝对路径" },
"severityMin": { "type": "string", "enum": ["LOW", "MEDIUM", "HIGH", "CRITICAL"] }
},
"required": ["projectPath"]
}
}输出统一 JSON,便于模型汇总,也便于 CI 门禁解析 critical_count。
第 2 步:解析依赖与 lockfile
import { readFileSync, existsSync } from 'node:fs'
import path from 'node:path'
function detectEcosystem(projectPath) {
if (existsSync(path.join(projectPath, 'package-lock.json'))) return 'npm'
if (existsSync(path.join(projectPath, 'pnpm-lock.yaml'))) return 'pnpm'
if (existsSync(path.join(projectPath, 'requirements.txt'))) return 'pip'
return null
}
function parseNpmLock(projectPath) {
const lock = JSON.parse(readFileSync(path.join(projectPath, 'package-lock.json'), 'utf8'))
const packages = lock.packages || {}
return Object.entries(packages)
.filter(([name]) => name)
.map(([name, meta]) => ({ name: name.replace('node_modules/', ''), version: meta.version }))
}若无 lockfile,应提示运行 npm install 生成,而不是只读 package.json 里宽泛的版本范围。
第 3 步:查询 OSV API
async function queryOsv(packageName, version, ecosystem = 'npm') {
const res = await fetch('https://api.osv.dev/v1/query', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
package: { name: packageName, ecosystem },
version,
}),
})
const data = await res.json()
return (data.vulns || []).map(v => ({
id: v.id,
summary: v.summary,
severity: v.severity?.[0]?.score || 'UNKNOWN',
}))
}生产环境应批量查询、限速、缓存结果,避免对大 monorepo 触发数千次 HTTP。
第 4 步:封装 MCP Server 与 Resource
server.tool('audit_dependencies', 'CVE 依赖审计', schema, async ({ projectPath, severityMin }) => {
const deps = parseNpmLock(projectPath)
const findings = []
for (const dep of deps.slice(0, 50)) {
findings.push(...await queryOsv(dep.name, dep.version))
}
const report = { scanned: deps.length, findings, generatedAt: new Date().toISOString() }
return { content: [{ type: 'text', text: JSON.stringify(report, null, 2) }] }
})
// Resource: 暴露最近一次 SBOM 摘要 URI sbom://latestPrompt 模板 security-audit-report 可固定 Markdown 章节:概览、Critical 列表、修复建议、误报说明。
第 5 步:平台接入与权限
同一 Server 经 stdio 接入 Cursor,经 HTTP 接入内部 Agent 平台。注意:
- 路径白名单:仅允许扫描 workspace 内目录
- 只读:Tool 不得修改 lockfile
- 敏感输出:CVE 详情可能暴露内部版本策略,日志需脱敏
CI 中可非交互调用 MCP Client,critical_count > 0 则 fail pipeline。
动手练习
- 完成 audit_dependencies 最小实现,对一个小型 npm 项目返回 JSON 报告。
- 增加 severityMin 过滤,验证 HIGH 以上过滤逻辑正确。
- 添加 Resource sbom://latest,返回上次扫描的包数量与最高严重级别。
- 编写 Prompt 模板 security-audit-report,让模型把 JSON 转成给管理层的一页 Markdown。
- 模拟 OSV 超时:单包查询失败时报告里应标记 partial: true 而非静默丢弃。
常见问题
Q:npm audit 和 OSV 该选哪个?
npm audit 与 npm 生态集成深、含部分 npm 专有建议;OSV 跨生态、适合统一 MCP Server 支持多语言。生产可抽象 VulnerabilityProvider 接口,按 ecosystem 路由。
Q:扫描 monorepo 太慢怎么办?
增量扫描(仅 changed lockfile)、并行批请求、结果缓存 TTL、上限保护(先扫 direct deps)。报告注明 truncated: true。
Q:模型会把 CVE 描述夸大吗?
会。应要求模型「仅引用报告 JSON 中的 id 与 summary」,并在 Prompt 中禁止臆测影响范围。高 stakes 场景由人审阅 Critical 项。
本节小结
依赖审计 MCP 是「解析 + 外部情报 + 结构化报告」的范本。Tool 做扫描,Resource 存摘要,Prompt 定报告格式;路径白名单与只读权限是上线底线。