Cisco mcp-scanner: 零 key 零配置,一条命令扫完所有 MCP
MCP 服务器装不装,先让它过一遍安检。 mcp-scanner 把 MCP 协议里所有能藏东西的地方——工具描述、prompts、resources、server instructions——逐个拉出来扫,九个分析器各管一段。它跟同类工具最大的区别不在规则多,而在一条接口约定:扫描不完整的时候,is_safe 返回 None,而不是 True。
它扫的是什么
MCP 服务器能投毒的面不止代码。工具的 name / description / inputSchema 是模型直接读的文本,prompts 和 resources 是服务器塞进上下文的文本,连握手时 InitializeResult 里的 server instructions 也算。mcp-scanner 给每一面都开了入口:remote、stdio、config、known-configs(直接扫本机 Cursor / Claude / VS Code 的配置文件)、prompts、resources、instructions,外加离线模式的 static、扫包的 pypi-scan / npm-scan、读源码的 supplychain。
默认跑三个分析器:--analyzers api,yara,llm。api 是 Cisco AI Defense 的商业 inspect 接口,要 key;yara 零依赖,装上就能跑;llm 要一个模型 key,走 LiteLLM,OpenAI / Bedrock / Azure / 本地 Ollama 都行。
九个分析器,各管一段
YARA 在这里不是扫二进制的。 10 个规则文件全是跑在工具描述文本上的正则。tool_poisoning.yara 抓的是「藏着第二个动作」的句式:also / while processing / for caching purposes 后面跟 collect|send|upload|log 这一类组合。规则注释里还留着误报修复记录——邮件类工具的正常描述就是「search emails」,宽动词表把它全打中了,于是把 search|find 从那条规则的动词里摘出去。这套规则的价值和局限是同一件事:它读的是说明书,改个句式、换种语言就绕过去了。
LLM 分析器的 system prompt 只有一句话,「你是安全专家,按给出的框架分析」,判定逻辑全在 user prompt 的分析框架里。值得注意的是失败路径:重试之后仍然拿到空回复,它不会当干净放过,而是记一条 INFO 级的 infrastructure finding,把「这一路没扫成」写进报告。
behavioral 是唯一真读代码的。 tree-sitter 建 CFG、做数据流和跨文件调用图,支持 Python / TypeScript / Go / Java / Rust 等 10 种语言,比的是 docstring 声称什么、实现实际做了什么,再交给 LLM 做 alignment 复核。自带 evals:141 个自造恶意样本、14 个类别,azure/gpt-4.1 下检出 135 个,95.7%。但良性侧只有 3 个文件,误报长什么样,仓库没给答案。
readiness 管的是另一件事:能不能上生产。 20 条 HEUR 规则查超时、重试、错误 schema、描述质量,外加 3 个 Rego 策略和一轮 LLM judge。Rego 那路要本机装着 opa 二进制,没装就静默跳过。
prompt_defense 是反着来的。 它不找描述里有什么攻击,而是找描述里缺哪些防御话术:12 个攻击向量逐个查有没有对应的防护声明,纯正则,模式里带繁体中文(不要假裝、跨語言、不可見字元)。README 写它「always runs by default」,代码里其实要显式 --analyzers prompt_defense 才跑(scanner.py:1440)——文档和代码对不上,用的时候以代码为准。
剩下三个:virustotal 拿 SHA256 去查服务器里捆绑的二进制文件;vulnerable-package 包了一层 pip-audit 查依赖 CVE;meta 要 --enable-meta 显式开,只做一件事——把别的分析器的假阳性删掉,只删不加,而且删了会在结果里留一块 meta_analysis 审计,写清楚删了哪几条、理由是什么。
is_safe 是个三值属性(result.py:88):有 finding 返回 False;status == "completed" 且 analyzer_errors 为空才返回 True;其余情况返回 None。汇总处把结果分成 safe / unsafe / incomplete 三桶,incomplete 单独列出来。任何一个分析器抛异常都会进 analyzer_errors,status 降为 partial,而异常消息只保留类名——provider 的报错里可能带着 URL、header 甚至被扫的输入,一律不回显。
同一条纪律还出现在两个地方。REST API 只放行 api / yara / llm / meta 四个分析器,其余的请求边界直接 422 拒掉,注释写得很直白:API 的响应形状会静默丢掉那几个分析器的 finding,不如拒掉,「不让运维以为扫描跑过了」。Docker 沙箱里的 pypi / npm 扫描也一样,扫描状态不是 completed 时 is_safe 一律 None,注释原话是 a degraded scan can’t masquerade as is_safe=True。
短板
零依赖路径(yara + readiness + prompt_defense)读的全是描述文本和元数据,不碰实现;真读代码的 behavioral 必须给 LLM key,而它的检出率数字来自自家合成样本,换个模型就是另一个数。readiness 这一路的纪律比主扫描松:opa 没装静默跳过、LLM judge 失败只记 warning,分数照算,status 也不会降成 partial。meta 每跑一次多一轮 LLM 调用,默认关着。
仓库 2025-09-24 建仓,Cisco 官方博客同年 10 月 23 日发文介绍,现在版本 v4.8.6,Apache-2.0。它和 Cisco AI Defense 的关系是开源前端加商业引擎:不订 AI Defense,三个默认引擎里就少一个,剩下两个照样能用。
装 MCP 服务器之前跑一遍 mcp-scanner --analyzers yara,readiness,零 key 零成本,能挡掉描述里明写的投毒和一批生产隐患;要语义判定再补 LLM key。但这个项目真正该被抄走的是那套报告约定:「没扫完」和「扫了没问题」必须是两个不同的值。 扫描器这个行业里,敢把 incomplete 单独列成一桶的,还不多。
- 仓库:cisco-ai-defense/mcp-scanner,Apache-2.0,2025-09-24 建仓,现版 v4.8.6
- 分析器:9 个(api / yara / llm / behavioral / virustotal / readiness / prompt_defense / vulnerable_package / meta),默认 api,yara,llm
- YARA:10 个规则文件,作用于工具描述文本;behavioral 支持 10 种语言
- 判定:
is_safe三值(False / True / None),汇总分 safe / unsafe / incomplete 三桶 - evals:141 个合成恶意样本检出 95.7%(azure/gpt-4.1),良性样本 3 个
- 入口:CLI、Python SDK、FastAPI REST、Claude Code 插件、PyInstaller 单文件 spec