SkillSpector 多语言批量扫描模块贡献指南:搭建环境、测试体系与核心设计原理
SkillSpector 多语言批量扫描模块贡献指南搭建环境、测试体系与核心设计原理【免费下载链接】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 的Multilingual Batch Scanner多语言批量扫描模块位于contrib/batch_scan/贡献代码的开发者完整讲解该模块的环境搭建、项目结构、164 项测试的运行方式、编码规范与提交风格并深入剖析其三大核心设计要点——双补丁池接线dual-patch pool wiring、实例属性注入instance-attribute injection与应用前守护guard before apply。阅读完本文你将能够独立搭建开发环境、验证所有测试套件并理解该模块在不修改 SkillSpector 上游核心代码的前提下如何以 7 个 monkey-patch 实现对 DeepSeek 等非 OpenAI 结构化输出提供商的兼容。关联文档contrib/batch_scan/CONTRIBUTING.md本文骨架 · 用户指南contrib/batch_scan/docs/README.md · 架构设计contrib/batch_scan/docs/DESIGN.md快速开始从零搭建开发环境Multilingual Batch Scanner 是一个以目录为单位的批量扫描扩展它会递归发现输入根目录下所有包含SKILL.md的子目录对每个 skill 并行跑完 SkillSpector 的完整 LangGraph 流水线再汇总输出终端 / JSON / Markdown 报告。对环境变量的要求是.env必须早于任何skillspector导入加载因为src/skillspector/constants.py在导入时就会读取SKILLSPECTOR_MODEL与SKILLSPECTOR_PROVIDER参见 batch_scan.py 顶部的 dotenv 加载逻辑。安装步骤python3 -m venv .venv source .venv/bin/activate pip install -e . cp contrib/batch_scan/.env.example .env # edit with your API keys.env模板位于 contrib/batch_scan/.env.example包含三种关键配置环境变量作用SKILLSPECTOR_API_KEYS多 Key 池配置格式为key\|base_url\|model以分号分隔每行一个批量扫描推荐SKILLSPECTOR_PROVIDER强制使用openai兼容模式适用于 DeepSeek 等兼容端点OPENAI_API_KEY/OPENAI_BASE_URL单 Key 回退模式当SKILLSPECTOR_API_KEYS未设置时生效SKILLSPECTOR_MODEL默认模型例如deepseek-chat多 Key 池的完整格式示例如下注意只有各 Key 不共享账户级限流时才真正有效SKILLSPECTOR_API_KEYSsk-or-xxx1|https://api.deepseek.com|deepseek-chat;sk-or-xxx2|https://api.deepseek.com|deepseek-chat;sk-or-xxx3|https://api.openai.com/v1|gpt-5.4验证环境是否可用安装完成后用内置 fixture 套件跑一次端到端扫描即可验证一切正常python -m contrib.batch_scan.batch_scan ./tests/fixtures/ -f terminal --workers 8该命令会扫描tests/fixtures/下的全部测试 skill 目录输出每个 skill 的风险评分、严重级别与问题数量。23 个内置 fixture 被特意设计用来覆盖每一条检测规则。项目地图模块文件职责速览contrib/batch_scan/目录下的每个 Python 文件职责单一这是理解与扩展模块的起点contrib/batch_scan/ ├── batch_scan.py # CLI 入口 ThreadPoolExecutor从这里开始读 ├── runner.py # graph.invoke() 封装 7 patches 池接线核心 ├── gap_fill.py # GapFillAnalyzer —— 针对 8 条未覆盖规则的 LLM 补充扫描 ├── api_pool.py # ApiKeyPool —— 多 Key 调度 429 退避 ├── detection.py # Unicode 脚本比例语言检测 ├── annotation.py # Finding 语言兼容性标注 ├── discovery.py # 递归 SKILL.md 查找 ├── reports.py # Terminal / JSON / Markdown 格式化器 ├── CONTRIBUTING.md # 本文档 │ ├── docs/ │ ├── README.md # 用户指南 —— 全部命令、测试命令、评审索引 │ ├── DESIGN.md # 架构 —— 并发、patches、双补丁机制 │ ├── REVIEW_RESPONSE.md # PR #100 评审回复 │ └── archive/ # 深度剖析、历史、未来工作、踩坑记录 │ └── tests/ ├── test_pool_wiring.py # smoke —— 3 路径池接线验证 ├── test_monkeypatch_invasiveness.py # 线程隔离、作用域14 项测试 ├── test_monkeypatch_fragility.py # 守护验证、深层依赖26 项测试 ├── docs/ │ ├── TEST_DESIGN.md # 每个测试套件为何如此设计 │ ├── TEST_GUIDE.md # 每个文件覆盖什么 运行命令 │ └── BUGS_FOUND.md # 发现并修复的 16 个 bug └── tests-pro/ ├── test_api_pool.py # 45 项 —— acquire/release/backoff ├── test_gap_fill.py # 41 项 —— JSON 解析、提示词构建 ├── test_runner_patches.py # 24 项 —— 上下文管理器、patches ├── test_annotation.py # 10 项 —— 语言兼容性 ├── random_numbered.py # 主测试入口seed42 └── mutation_max.py # 30 处 bug 注入框架从源码结构看测试被刻意拆成两个层次tests/tests-pro/存放与核心逻辑一一对应的 120 项单元测试按seed42随机排序执行见 random_numbered.pytests/顶层存放面向主题的 44 项测试14 项线程隔离 26 项守护验证外加 4 项池接线的 smoke 测试。运行测试164 项测试的完整清单该模块共维护164 项测试分为四个独立入口。任何代码改动都建议至少跑一遍下面四组命令# 全部 164 项测试 python contrib/batch_scan/tests/tests-pro/random_numbered.py # 120 项单元测试seed42 python contrib/batch_scan/tests/test_pool_wiring.py # 4 项 smoke 检查 python contrib/batch_scan/tests/test_monkeypatch_invasiveness.py # 14 项主题测试 python contrib/batch_scan/tests/test_monkeypatch_fragility.py # 26 项主题测试其中random_numbered.py会加载test_api_pool、test_gap_fill、test_runner_patches、test_annotation四个模块用random.seed(42)打乱顺序并逐条编号打印进度——固定种子保证每次运行顺序一致便于复现失败。仅跑评审相关测试python -m unittest \ contrib.batch_scan.tests.test_monkeypatch_invasiveness \ contrib.batch_scan.tests.test_monkeypatch_fragility -v python contrib.batch_scan/tests/test_pool_wiring.py变异测试Mutation Testpython contrib.batch_scan/tests/tests-pro/mutation_max.pymutation_max.py 是一个 30 处 bug 注入框架它向被测代码注入故意破坏性修改验证测试套件能否把这些变异全部抓出来——这是衡量测试质量的手段。端到端测试fixture 套件python -m contrib.batch_scan.batch_scan ./tests/fixtures/ -f terminal --workers 8 python -m contrib.batch_scan.batch_scan ./tests/fixtures/ -f terminal --workers 8 --no-llm第一行带 LLM 完整扫描第二行--no-llm仅跑静态模式不需要任何 API Key。回归三连捕获大多数回归的三条命令CONTRIBUTING 明确推荐这三条命令作为改动后的最小回归集python contrib/batch_scan/tests/tests-pro/random_numbered.py python contrib/batch_scan/tests/test_pool_wiring.py python -m contrib.batch_scan.batch_scan ./tests/fixtures/ -f terminal --workers 8编码规范与上游 SkillSpector 完全对齐模块要求与 SkillSpector 上游风格逐条对齐新增任何.py文件都必须遵守SPDX 头每个.py文件顶部保留 SPDX 版权与许可证声明Apache-2.0from __future__ import annotations作为第一个导入导入分组顺序标准库 → 第三方 →skillspector.*→ 相对导入.类型注解使用| None语法而非Optional[X]模块级常量用frozenset/Final声明命名使用UPPER_SNAKE_CASE私有辅助函数_lower_snake_case前缀日志每个模块都应有logger get_logger(__name__)注释解释why为什么而非 what做了什么文档字符串所有公开函数与类都必须有 docstring。以 runner.py 中的_SKIP_DIRS为例它用frozenset[str]声明且全大写正是上述约定的直接体现。提交风格约定式提交 NVIDIA DCO提交信息遵循约定式提交规范使用现在时祈使语气fix: wire ApiKeyPool into llm_analyzer_base graph path feat: add multilingual batch scanner with parallel execution docs: document dual-patch pool wiring fix并附加两条硬性要求Signed-off-bytrailer 必填NVIDIA DCO 要求联合开发需附加Co-authored-bytrailer。核心设计要点改代码前必须先理解的三件事CONTRIBUTING 明确警告任何修改前必须先理解以下三个设计决策。它们是整个模块的承重墙。1. 双补丁池接线Dual-patch pool wiringset_api_pool()在 runner.py 中实现它同时替换两个模块的get_chat_modelskillspector.llm_utils.get_chat_modelskillspector.llm_analyzer_base.get_chat_model第二个补丁是必须的llm_analyzer_base通过from ... import方式导入该函数在自身模块内建立了局部引用。如果只补llm_utilsllm_analyzer_base内部仍然拿着旧的函数引用单模块补丁就会漏掉约 95% 的 LLM 调用20 个图内 analyzer 都经由LLMAnalyzerBase.__init__调用它。这一坑的完整记录见 contrib/batch_scan/docs/archive/PITFALLS.md。补丁后的_pooled_get_chat_model返回PooledChatModel从而让图内 analyzer、meta-analyzer 与 gap-fill 的每一次LLM 调用都流经ApiKeyPool。这一接线行为被 test_pool_wiring.py 以三条路径逐一断言llm_utils.get_chat_model()直接调用、LLMAnalyzerBase.__init__构建的_llm、以及GapFillAnalyzer.chat_model并验证上下文管理器退出与set_api_pool(None)后补丁全部还原。2. 实例属性注入而非类属性注入Instance-attribute injectionPatch 1 在LLMAnalyzerBase.__init__的原始逻辑执行之前把self.response_schema None写入实例的__dict__而不是类的__dict__见 runner.py。其正确性依赖 Python 语言级保证MRO 查找属性时永远先查instance.__dict__再查类属性。这带来两个关键收益线程安全每个实例各自持有response_schema互不干扰patch 可以安全地运行在ThreadPoolExecutor的多线程环境下抗上游重构即使上游类层次结构未来变化实例字典优先的语义不变。CONTRIBUTING 特别强调V1 版本曾因修改类属性而导致跨线程竞态这是被历史验证过的失败教训见 PITFALLS.md。3. 应用前守护Guard before apply_verify_patch_targets()runner.py在_apply_patches()真正执行前逐一校验全部 7 个 patch 的前置假设表层签名检查如LLMAnalyzerBase.__init__必须保留关键字专用keyword-only的node参数parse_response必须仍接受(self, response, batch)深层依赖检查patch 内部 try/except 中调用的方法如LLMAnalysisResult.model_validate、LLMFinding.to_finding、Batch.file_path字段、MetaAnalyzerResult.findings字段一旦缺失会静默降级守护逻辑同样逐一校验。守护的设计目标是如果上游改了签名或删了依赖patch 立即抛RuntimeError失败关闭fail closed绝不静默地以错误状态继续运行。守护通过后_apply_patches使用嵌套计数器而非布尔标志保证幂等且可安全嵌套使用。并发模型与 patch 清单三层并行 7 个兼容补丁三层并行架构批量扫描的并发建立在内置两层并行之上共三层参见 batch_scan.py层级机制粒度Layer 1LangGraph 内部 20 个 analyzer fan-out单个 skill 内Layer 2LLMAnalyzerBase.arun_batchesSemaphore(10)单个 analyzer 内Layer 3ThreadPoolExecutor(max_workers)本模块skill 之间每个 skill 在独立线程中执行完整graph.invoke(state)--workers控制并行度默认 4。90 秒的 per-skill 超时防止卡死的 worker 阻塞整个批次——超时的 skill 被跳过且不重试重试会占用另一个槽位。另外worker 数量应结合实际 API Key 数量调整免费档 Key 建议降到 1企业档可调高详见 docs/README.md 中的说明。ApiKeyPool多 Key 调度与 429 退避api_pool.py 中的ApiKeyPool是限流保护的核心每 Key 并发槽位默认max_concurrent5一个 Key 可同时服务最多 5 个调用方最闲调度least-loadedacquire()优先选择活跃请求最少的可用 Key多个调用方可共享同一 Key只要槽位充足429 退避释放时若标记successFalse该 Key 进入冷却退避时长为30 × 2^n秒上限 300 秒到期后自动恢复轮换PooledChatModel是 LangChain 兼容包装每次invoke从池中取 Key、即时构建ChatOpenAI实例、用毕释放遇 429 自动换 Key 重试最多 5 次。旧设计是每 Key 一把互斥锁一旦每个 Key 有一个活跃请求就整体阻塞导致 worker 数与 Key 数强耦合新设计让吞吐量只依赖 worker 数与总槽位与 Key 数量解耦。7 个 DeepSeek 兼容补丁deepseek_compat()上下文管理器runner.py遵循Save → Patch → Yield → Restorefinally 保证模式即使发生异常也会还原现场#补丁目标作用1LLMAnalyzerBase.__init__注入response_schemaNone实例属性2LLMAnalyzerBase.parse_response手动 JSON 解析 Pydantic 校验3LLMMetaAnalyzer.parse_response手动 JSON 解析 字段清洗如impact非法值归为low4LLMAnalyzerBase.build_prompt追加 JSON 输出指令5LLMMetaAnalyzer.build_prompt追加 meta JSON 输出指令6ChatOpenAI.__init__强制 HTTP 超时请求 30s / 连接 8s7asyncio.run抑制 httpx 清理阶段的 Event loop is closed 噪音补丁不在导入时自动生效必须显式调用setup_deepseek_compat()是一次性永久生效的便捷包装而上下文管理器是可逆的推荐用法。这些补丁解决的核心问题是DeepSeek 等提供商不支持response_format结构化输出因此必须关闭response_schema并手动解析 JSON。多语言增强与 Gap-Fill 补充扫描批量扫描模块的另一大核心特性是非英语 skill 的检测增强。当 skill 被检测为非英语zh/ja/ko时25 条依赖英文关键词的静态规则会丢失召回率其中 17 条已有 SSD / SDI / SQP 语义 analyzer 兜底剩余 8 条——P5、P6-P8、MP1-MP3、RA1-RA2——没有任何 LLM 发现规则对应由GapFillAnalyzer针对每个 skill 补齐见 gap_fill.py 与 annotation.py规则漏洞类别P5有害内容投毒、伤害、武器制造等伪装成配方/教程P6-P8系统提示词泄漏直接 / 间接 / 工具型MP1-MP3记忆投毒持久上下文注入、上下文窗口填塞、记忆/状态篡改RA1-RA2流氓 Agent自修改代码、未经授权的持久化如 cron / .bashrc / systemdGapFillAnalyzer继承LLMAnalyzerBase因此自动获得 token 预算感知的分批get_batches与并行执行arun_batchesresponse_schema保持NoneJSON 由parse_response手动解析并以 Pydantic 校验只保留confidence 0.7的发现。gap-fill 的发现经annotate_findings打上language_compatible标注后追加进该 skill 的 issues 列表并在enhancements.gap_fill_applied/gap_fill_findings中记录应用情况供报告展示见 batch_scan.py。语言检测零依赖的 Unicode 脚本比例算法detection.py 只依赖标准库unicodedata与主项目mcp_tool_poisoning.py相同通过统计 CJK / 平假名 / 片假名 / 谚文字符在总字母字符中的占比判定语言判定条件阈值结果平假名片假名占比 0.05ja谚文音节占比 0.10koCJK 统一表意文字含扩展 A占比 0.10zh其余情况—endetect_skill_language对 skill 内所有文件逐文件判定后按多数投票确定整个 skill 的语言。在batch_scan.py中所有 skill 的语言在进入 worker 线程之前就预先解析完毕lang_map避免多线程在文件 I/O 上竞争。--lang参数可强制指定auto/en/zh/ja/ko绕过自动检测。CLI 参数速查主入口命令的完整参数如下实现见 batch_scan.py参数默认值说明input_dir必填包含 skill 子目录每个含SKILL.md的根目录-f/--formatterminal输出格式terminal/json/markdown-o/--outputstdout报告写入文件默认标准输出--no-llm关跳过 LLM 分析仅静态模式--workers N4并行 worker 数-V/--verbose关开启 DEBUG 级日志--langauto期望语言auto/en/zh/ja/ko--require-llm/--no-require-llm开是否要求非英语 skill 必须有 LLM--no-llm时给出警告报告按风险评分降序排列。退出码有约定扫描出错时退出 2存在高风险 skill评分 50时退出 1否则退出 0——这一约定使-f json -o report.json可以直接接入 CI 管道做门禁判断。如何贡献高价值方向详细的 12 个未来方向与工作量估算见 contrib/batch_scan/docs/archive/FUTURE_WORK.md。CONTRIBUTING 特别点名的高影响力方向Checkpoint / 断点续扫防止大扫描中途数据丢失语言检测扩展覆盖 9 种以上语言SARIF 输出格式非英语 ground-truth fixtures当前测试数据以英语为主。推荐的继续阅读路径contrib/batch_scan/docs/README.md —— 用户指南全部命令、测试命令、评审索引contrib/batch_scan/docs/DESIGN.md —— 架构并发、补丁、双补丁机制contrib/batch_scan/docs/REVIEW_RESPONSE.md —— PR #100 评审回复contrib/batch_scan/docs/archive/PITFALLS.md —— 全部踩坑记录V1 类属性竞态、单模块补丁漏接等contrib/batch_scan/tests/docs/TEST_DESIGN.md 与 TEST_GUIDE.md —— 每个测试套件的设计动机与覆盖说明contrib/batch_scan/tests/docs/BUGS_FOUND.md —— 测试驱动发现的 16 个已修复 bug在动手修改任何代码之前请务必重读第三节核心设计要点双补丁接线、实例属性注入与守护先行这三点共同保证了模块在 8 worker 并发、多 Key 池与 DeepSeek 兼容补丁同时生效的场景下依然线程安全、失败闭环且不静默降级。【免费下载链接】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),仅供参考