ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

SkillSpector MCP 最小权限静态分析(LP1–LP4)实战解析:从 SKILL.md 声明到代码真实能力的交叉验证

SkillSpector MCP 最小权限静态分析(LP1–LP4)实战解析:从 SKILL.md 声明到代码真实能力的交叉验证 SkillSpector MCP 最小权限静态分析LP1–LP4实战解析从 SKILL.md 声明到代码真实能力的交叉验证【免费下载链接】SkillSpectorSecurity scanner for AI agent skills. Detect vulnerabilities, malicious patterns, security risks, prompt injection, data exfiltration, and supply-chain risks in Claude Code, Codex, and MCP skills before you install them.项目地址: https://gitcode.com/GitHub_Trending/sk/SkillSpector本篇技术指南围绕 SkillSpector 中编号为B.3.1 的 MCP Least-Privilege 分析器设计文档展开讲解它是如何把SKILL.md/ MCP server 清单中声明的权限与可执行代码中实际使用的能力做交叉比对并输出 LP1–LP4 四类发现Finding。读完本文你将掌握六类能力检测正则与关键字映射表的构成、四条规则的触发条件与置信度体系、规则之间的抑制关系以及如何借助仓库内置测试夹具验证分析器行为。1. 背景为什么 AI Skill 需要最小权限审计MCPModel Context Protocol类型的 Skill 会在清单文件SKILL.md或 MCP server 的permissions字段中声明自己打算使用的权限。一个行为良好的 Skill 应当只请求自己真正用到的能力——这就是最小权限原则Principle of Least Privilege。然而在实践中Skill 经常出现四类偏差实际使用但从不声明代码偷偷调用了网络或 shell却不在清单里披露掩盖真实意图声明宽泛的通配符用*、all等代替具体权限清单完全省略权限字段清单里没有任何权限声明但代码仍在执行敏感操作声明了早已不存在的权限权限列表与当前代码脱节。这些偏差对用户和调用该 Skill 的 AI Agent 都是不可见的。B.3.1 分析器的作用就是补上清单声明了什么与代码实际做了什么之间的信息鸿沟。它是 SkillSpector 针对 MCP 生态安全扫描的一部分与mcp_rug_pull、mcp_tool_poisoning等分析器并列见 分析器注册相关测试。2. 架构总览一个纯静态的 LangGraph 节点B.3.1 分析器在 SkillSpector 的 LangGraph 流水线中以一个节点的形式运行。其内部流程可抽象为三步┌─────────────┐ │ SKILL.md │ │ (manifest) │ └──────┬──────┘ │ permissions[] ▼ ┌───────────────────────────────┐ │ mcp_least_privilege (node) │ │ │ │ 1. Map permissions → caps │ │ 2. Detect code capabilities │ │ 3. Cross-reference emit │ └───────────────────────────────┘ │ ┌───────┴───────┐ │ Findings │ │ LP1 -- LP4 │ └───────────────┘节点运行需要依赖 Graph State 中携带的三类数据State key类型说明manifestdict解析后的SKILL.mdYAML frontmatterfile_cachedict[str, str]文件路径 → 文件内容的映射覆盖 Skill 的所有文件component_metadatalist[dict]每个文件的元数据其中executable标志决定该文件是否参与能力扫描从源码实现看src/skillspector/nodes/analyzers/mcp_least_privilege.py节点入口node(state)会先从manifest提取声明、从local_file_cache/file_cache取内容、从component_metadata取可执行标志。整个分析过程不调用任何 LLM、不发任何网络请求属于纯静态分析无论 Skill 规模大小都能在毫秒级完成。测试断言中对该节点产出的category字段固定为MCP Least Privilege、tags固定为[ASI02]这两者与 pattern_defaults.py 中的PatternCategory.MCP_LEAST_PRIVILEGE及规则描述一一对应。两个主动跳过条件源码与测试均确认见 tests/test_mcp_least_privilege.py没有 manifest节点返回空发现并在analyzer_status_events中标记not_applicable原因码manifest_absent只有文档类文件、没有任何可执行文件executableFalse同样跳过原因码no_applicable_files。3. 能力检测六类能力 × 正则模式分析器对所有可执行文件做一次扫描把文件内容按六类能力分类进行正则匹配。下表是设计文档给出的分类示例类别示例检测模式shellsubprocess、Popen、curl、wget、chmodnetworkhttpx、requests、urllib、aiohttp、socket.connectfile_readopen(..., r)、.read_text()、os.listdir、os.walk、globfile_writeopen(..., w)、.write_text()、shutil.copy、os.renameenvos.environ、os.getenv、process.env、dotenvmcpcreate_session、MCPClient、mcp.client实际落地到源码中这六类模式被定义在_CAPABILITY_PATTERNS字典里mcp_least_privilege.py比文档表格更细shellsubprocess、Popen、os.system、os.popen、os.exec、\bcurl\b、\bwget\b、\bchmod\bnetwork\bhttpx\b、\brequests\b、\burllib\b、\baiohttp\b、socket\.connect、fetch(、XMLHttpRequestfile_readopen(后跟读模式r带正则长度约束的滑动匹配、.read_text(、.read_bytes(、os.listdir、os.walk、glob.globfile_writeopen(后跟写/追加模式w/a、.write_text(、.write_bytes(、shutil.copy、os.rename、os.mkdirenvos.environ、os.getenv、process.env、\bdotenv\bmcpcreate_session、MCPClient、mcp.client。所有模式均以re.IGNORECASE编译大小写不敏感。每个文件只扫一遍某个类别只要命中任意一条模式该类别即被记录为该文件的能力。测试文件的特殊处理符合test_*.py或*_test.py命名规则的文件_is_test_file判定见 mcp_least_privilege.py会被单独跟踪。如果一个能力只出现在测试文件里LP1 的置信度会下调详见第 5 节。能力扫描的窗口化设计源码级细节为防御超大文件的资源消耗扫描按 64 KiB 的滑动窗口进行窗口之间保留 4096 字符的重叠单条匹配正则的最大匹配跨度被限制为 4096 字符声明值的展示上限为 512 字符_bounded_evidence。这些常量为_CAPABILITY_WINDOW_CHARS、_CAPABILITY_WINDOW_OVERLAP_CHARS、_MAX_CAPABILITY_MATCH_CHARS、_MAX_EVIDENCE_CHARSmcp_least_privilege.py。4. 权限声明映射permissions 与 allowed-tools 双通道4.1permissions关键字的类别映射manifest.permissions[]中的声明字符串通过关键字匹配映射到同样的六类能力类别权限关键字映射到类别bash、shell、terminal、commandshellnetwork、http、fetch、apinetworkread、fs_read、file_readfile_readwrite、fs_write、file_writefile_writeenv、environmentenvmcp、tools、tool_usemcp源码中的_PERM_TO_CAPABILITY字典与该表一致且匹配方式为词边界大小写不敏感的\b{keyword}\b正则mcp_least_privilege.py。也就是说声明BASH、Network这种混合大小写同样能正确匹配——这一点有专门的测试用例test_permission_matching_case_insensitive覆盖tests/test_mcp_least_privilege.py。4.2allowed-tools工具名的类别映射Claude Code / Agent Skills 通道与 MCP server 清单使用permissions不同Claude Code / Agent Skills 类型的SKILL.md使用 frontmatter 中的allowed-tools字段声明可调用的工具。分析器为工具名单独维护了一张映射表_TOOL_TO_CAPABILITYmcp_least_privilege.py工具名能力类别bash、execute、terminalshellread、glob、lsfile_readwrite、edit、multiedit、notebookeditfile_writewebfetch、websearch、fetchnetworkenvenvallowed-tools字段支持三种书写形式_normalize_allowed_tools都会归一化为工具名列表mcp_least_privilege.py# 形式一YAML 列表 allowed-tools: - Bash - Read # 形式二逗号分隔字符串 allowed-tools: Bash, Read # 形式三空格分隔字符串 allowed-tools: Bash Read测试TestLP3AllowedTools逐个验证了这三种形式都能满足已声明判定不会触发 LP3而把permissions置空后仅靠allowed-tools也能覆盖对应能力类别tests/test_mcp_least_privilege.py。4.3 通配符的独立检测*、all、full、any被单独识别为通配符值源码中的_WILDCARD_PERMS集合不参与普通关键字映射直接触发第 5 节的 LP2 规则。通配符检测还有长度防护只有长度 ≤ 16 的字符串才会被比较避免把攻击者构造的超长字符串复制并做大小写折叠mcp_least_privilege.py。5. 四条规则LP1–LP4 的触发条件、置信度与修复5.1 LP1 —— 能力未声明Underdeclared Capability字段值严重级别HIGH置信度0.75代码文件/ 0.55仅测试文件标签ASI02触发条件可执行文件中检测到某类能力但没有任何已声明权限能映射到该类别。为什么重要一个使用网络却不声明网络的 Skill 是在隐藏真实行为。这是 LP 系列规则中最强的欺骗意图信号——Skill 正在执行它声称不需要的操作。示例Agent Skills 的SKILL.md声明allowed-tools: Read但代码里出现httpx.post(...)。此时未声明的network能力触发 LP1。修复建议源码中根据声明通道自动生成不同措辞见 mcp_least_privilege.pyAgent Skills 类型向allowed-toolsfrontmatter 字段补充覆盖缺失能力的工具MCP server 清单向permissions列表补充缺失能力否则删除需要该能力的代码。LP1 置信度有两个档位能力出现在普通代码文件中为 0.75仅出现在测试文件中降为 0.55源码_clamp(0.55 if capability in test_only_caps else 0.75)mcp_least_privilege.py。测试test_lp1_test_files_reduced_confidence验证了这一行为test_helper.py中的网络调用产生的 LP1 置信度必须小于 0.75tests/test_mcp_least_privilege.py。5.2 LP2 —— 通配符权限Wildcard Permission字段值严重级别MEDIUM置信度0.90标签ASI02触发条件权限列表中出现*、all、full或any中的任意一个。为什么重要通配符权限会完全关闭基于权限的安全控制。Skill 用的任何能力在技术上都算已声明但用户对 Skill 实际做什么毫无可见性。这是权限体系中的chmod 777。示例permissions: - *修复建议把通配符替换为明确的权限列表只请求所需的最小访问范围。5.3 LP3 —— 缺少权限声明Missing Permission Declaration字段值严重级别MEDIUM置信度0.70标签ASI02触发条件manifest 中既没有permissions字段或为空列表也没有allowed-toolsfrontmatter同时可执行文件中检测到了代码能力。为什么重要没有权限声明Skill 的意图就完全不可见用户和 Agent 都无法评估其访问级别是否合适。LP3 比 LP1 可疑程度低更可能是疏忽而非欺骗但仍是显著的透明度缺口。示例SKILL.md既没有permissions:键也没有allowed-tools:键但代码调用了os.environ[API_KEY]和subprocess.run(...)。修复建议在你编写的清单类型中声明工具范围Claude Code / Agent Skills 的SKILL.md在allowed-toolsfrontmatter 字段中列出可调用的工具注意permissions不在 SKILL.md schema 中会被忽略MCP server 清单添加permissions列表命名所需能力。5.4 LP4 —— 过度声明权限Overdeclared Permission字段值严重级别LOW置信度0.65标签ASI02触发条件manifest 声明了某权限但在任何文件中都检测不到对应的代码能力。为什么重要过度声明可能意味着已移除的功能没有清理干净良性但草率为未来的滥用预埋——现在先声明权限将来加入恶意代码时就不会触发 LP1从其他 Skill 的清单复制粘贴而来。由于它不构成正在进行的实际利用因此是 LOW 级别但确实违反了最小权限原则值得人工复核。示例permissions: [shell, network]但代码只用了httpxnetwork。此时shell权限属于过度声明。修复建议如果对应能力已不再使用删除该权限声明。6. 规则之间的交互与抑制关系四条规则在设计上刻意避免重复或矛盾的发现。判定顺序在源码中是先做通配符检查LP2再判断声明缺失LP3最后在存在声明的前提下做交叉比对LP1 / LP4。整体行为可归纳为条件触发的规则被抑制的规则通配符 未声明能力LP2LP1被抑制无权限 检测到能力LP3LP1、LP4无列表可比对正常列表 缺口LP1 和/或 LP4——纯文档 Skill无可执行文件无分析器跳过全部无 manifest无分析器跳过全部源码中的两个关键判定变量与之一致wildcard_present通配符存在则直接发 LP2 并抑制后续 LP1 比对、permissions_absentpermissions为 None/空且无allowed-tools时走 LP3 分支。测试用例TestLP3AllowedTools.test_allowed_tools_underdeclared_fires_lp1专门验证了allowed-tools: [Read] 出现subprocess代码时 LP3 被抑制、而 LP1 以 HIGH 触发 shell 能力tests/test_mcp_least_privilege.py。7. 测试夹具与行为验证仓库在 tests/fixtures/ 下提供了三个与本文档直接对应的 Skill 夹具测试文件 tests/test_mcp_least_privilege.py 通过解析其 frontmatter、构建file_cache与component_metadata来驱动mcp_least_privilege.node(state)夹具目录预期发现用途tests/fixtures/mcp_clean_skill/无负向测试——所有能力均已声明tests/fixtures/mcp_underdeclared_skill/LP3缺权限声明 检测到能力tests/fixtures/mcp_overprivileged_skill/LP2、LP4通配符 过度声明逐一对照夹具内容可以直观理解规则cleanSKILL.md声明permissions: [bash, read, write]scripts/format.py 恰好使用subprocessshell、read_textfile_read、write_textfile_write声明与能力完全闭合 → 零发现underdeclaredSKILL.md完全没有permissions与allowed-tools键而 scripts/agent.py 使用了os.environenv、subprocess.runshell、httpx.postnetwork→ LP3overprivilegedSKILL.md声明permissions: [bash, network, read, write, *]其中*触发 LP2而 scripts/helper.py 实际只用open(path)读取file_read声明中的network、write、shell均无对应能力 → LP4。除三条主规则外测试还覆盖了 LP1 在高严重级别下的置信度门槛≥ 0.70、LP4 的置信度门槛≥ 0.60、allowed-tools三种书写形式、大小写不敏感匹配、无 manifest/纯文档 Skill 的跳过以及资源上限触发时检查账本inspection ledger事件被标记为partial、状态降级为degradedtests/test_mcp_least_privilege.py。8. 资源边界保障任意 Skill 输入下的可控性从源码结构看B.3.1 分析器不仅关心检出能力还内置了一套完整的资源预算机制防止恶意构造的超大 Skill 拖垮扫描流水线运行时间预算_LeastPrivilegeBudget.check_runtime依赖 state 中的transitive_remaining_seconds在每个文件、每个窗口、每条权限的粒度上做协作式检查超时即抛出_LeastPrivilegeResourceLimitError并把所有未完成作用域在 inspection ledger 中标记为partial原因码runtime_limit输出数量上限单文件与单分析器的发现数分别受MAX_FINDINGS_PER_ARTIFACT与MAX_FINDINGS_PER_ANALYZER约束来自 static_runner.py超限则触发output_limit并保留已生成发现的有界前缀声明条目上限_MAX_DECLARATION_VALUES限制权限/工具声明条目的处理数量防止逗号密集型声明产生超大的临时列表证据截断超长权限值在 LP4 消息中只展示前 512 字符。测试test_many_matches_stop_at_construction_cap_and_bound_evidence和test_deadline_during_component_marks_every_omitted_scope_partial分别验证了这两种边界行为tests/test_mcp_least_privilege.py。这保证了毫秒级纯静态分析的前提在对抗性输入下依然成立。9. 已知限制文档明确列出了分析器当前的边界使用时需留意基于正则而非语义分析能力检测是模式匹配。位于永远不会执行的代码路径中的能力也会被检出反过来通过getattr、importlib动态分发调用的能力可能被漏检无传递分析如果 Skill 调用了一个内部使用subprocess的库函数只要 Skill 自身代码没有直接出现这些模式shell能力就不会被检出权限关键字仅限英文关键字到类别的映射假定权限名是英文非英文权限名无法映射到能力类别。10. 如何在实际扫描中启用与查看结果B.3.1 分析器是 SkillSpector 内置静态分析流水线的一员无需额外配置即可随扫描运行它作为mcp_least_privilege槽位被登记在常量与模型槽位表中constants.py并与其他静态分析器一样受统一的最大发现数约束。运行一次完整扫描即可在结果中找到LP1–LP4规则规则的默认文案与修复建议汇总在 pattern_defaults.py例如 LP1 的文案为代码使用了声明权限未覆盖的能力network、shell、file write 等Skill 做了超出其声明的事情可能暗示欺骗意图每条 Finding 携带category MCP Least Privilege、tags [ASI02]可据此在报告中过滤。如需本地复现本文的验证过程可直接运行测试python -m pytest tests/test_mcp_least_privilege.py -v。该测试不依赖任何外部服务或 LLM是理解 LP1–LP4 触发条件最直接的可执行证据。【免费下载链接】SkillSpectorSecurity scanner for AI agent skills. Detect vulnerabilities, malicious patterns, security risks, prompt injection, data exfiltration, and supply-chain risks in Claude Code, Codex, and MCP skills before you install them.项目地址: https://gitcode.com/GitHub_Trending/sk/SkillSpector创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表