Skip to content

实战 · 依赖审计

🎯 学习目标

  • 从 package.json / requirements.txt 解析依赖清单
  • 对接 OSV 或 npm audit 等 CVE 数据源
  • 通过 MCP Tool 暴露 audit_dependencies 能力
  • 输出按 CVSS 分级的 JSON/Markdown 审计报告

引言

本节把 MCP 落到真实安全场景:对项目依赖做 CVE 审计。你会实现「解析 → 查询 → 分级 → 报告」流水线,并封装为 MCP Tool,供 Cursor 或自研 Agent 调用。

章节正文

第 1 步:项目目标与 Tool 契约设计

对外暴露单一入口 Tool:

json
{
  "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

javascript
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

javascript
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

typescript
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://latest

Prompt 模板 security-audit-report 可固定 Markdown 章节:概览、Critical 列表、修复建议、误报说明。

第 5 步:平台接入与权限

同一 Server 经 stdio 接入 Cursor,经 HTTP 接入内部 Agent 平台。注意:

  • 路径白名单:仅允许扫描 workspace 内目录
  • 只读:Tool 不得修改 lockfile
  • 敏感输出:CVE 详情可能暴露内部版本策略,日志需脱敏

CI 中可非交互调用 MCP Client,critical_count > 0 则 fail pipeline。

动手练习

  1. 完成 audit_dependencies 最小实现,对一个小型 npm 项目返回 JSON 报告。
  2. 增加 severityMin 过滤,验证 HIGH 以上过滤逻辑正确。
  3. 添加 Resource sbom://latest,返回上次扫描的包数量与最高严重级别。
  4. 编写 Prompt 模板 security-audit-report,让模型把 JSON 转成给管理层的一页 Markdown。
  5. 模拟 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 定报告格式;路径白名单与只读权限是上线底线。