内容
<div align="center">
# 技能守护
扫描第三方 AI 代理技能文件(`SKILL.md` 清单、钩子和捆绑脚本)以查找已知攻击模式,然后再运行它们,并且是同类中唯一一个可以从另一个代理内部作为 MCP 工具调用的扫描器。
[](https://github.com/RudrenduPaul/skillguard/actions/workflows/ci.yml)
[](https://www.npmjs.com/package/skillguard-cli)
[](https://pypi.org/project/skillguard-cli/)
[](./LICENSE)
[](./CHANGELOG.md)
</div>

## 为什么存在
第三方代理技能在安装后立即具有真实的工具、文件和网络权限,几乎没有什么检查它们。 [Snyk 的 ToxicSkills 研究](https://snyk.io/blog/toxicskills-malicious-ai-agent-skills-clawhub/) 在 2026 年 2 月扫描了 3,984 个公开列出的技能,发现 36% 的技能存在安全漏洞,包括 76 个技能带有确认的恶意有效负载:凭证盗窃、反弹 shell、数据泄露。大多数市场和代理框架在“某人发布技能”和“用户的代理运行它”之间没有扫描步骤。技能守护是这一步骤:一个 CLI、一个库、一个 MCP 服务器和一个 GitHub Action,都读取相同的十个捆绑规则包。
## 安装
```bash
# npm -- JavaScript/TypeScript CLI + 库
npm install --save-dev skillguard-cli
# PyPI -- Python CLI + 库,真正的端口,不是 Node 二进制的包装
pip install skillguard-cli
```
两个安装在扫描时都不会获取任何内容:所有十个规则包和模式匹配引擎都包含在 npm tarball 和 Python wheel 中。
## 现在试试
```bash
npx skillguard-cli scan ./examples/known-bad-skill
```
这将针对存储库中捆绑的固定装置运行,该装置是安全的、非功能的,并且故意可以模式匹配,返回 11 个真实的发现,带有文件:行引用,耗时约 0.15 秒。每次扫描都会在 stderr 上打印“加载技能守护规则包...”,这是预期的,而不是挂起。
与干净的固定装置进行比较:
```bash
npx skillguard-cli scan ./examples/clean-skill
```
返回零发现和退出代码 0。
## 目录
- [为什么存在](#为什么存在)
- [安装](#安装)
- [现在试试](#现在试试)
- [功能](#功能)
- [快速入门](#快速入门)
- [命令](#命令)
- [API 参考](#api-参考)
- [MCP 服务器](#mcp-服务器-代理原生工具调用)
- [GitHub Action](#github-action)
- [规则包](#规则包)
- [技能守护的比较](#技能守护的比较)
- [什么是技能守护,为什么存在](#什么是技能守护,为什么存在)
- [抑制发现](#抑制发现)
- [已知限制](#已知限制)
- [开发](#开发)
- [FAQ](#faq)
- [许可证](#许可证)
## 功能
- **十个专门的规则类别(SG01-SG10)**,涵盖网络不匹配、远程代码执行、文件范围提升、钩子供应链风险、混淆有效负载、凭证收集、前端范围伪造、技能自身指令文本中的提示注入、跨技能特权链接和市场域名抢注。每个类别都在 [CHANGELOG.md](./CHANGELOG.md) 中按规则记录。
- **跨技能特权链接(`scan-set`)** 捕获单个技能扫描器在结构上遗漏的威胁:技能 A 读取敏感文件,技能 B 具有网络出口权限,它们都没有声明彼此之间的沙盒隔离,并且它们组合后可以泄露技能 A 单独无法读取的内容。 `npx skillguard-cli scan-set ./my-skills-dir` 扫描目录中的每个技能,并标记组合。Snyk Agent Scan 的维护者 [在问题 #301](https://github.com/snyk/agent-scan/issues/301) 中确认,他们的 `--skills` 标志一次只识别一个 `SKILL.md`,并且不能递归发现目录中的技能。
- **零身份验证,零配置。** `npx skillguard-cli scan <path>` 不需要帐户、不需要 API 令牌、不需要注册。与 Snyk Agent Scan (`SNYK_TOKEN` 和 Snyk 帐户) 或 Socket CLI(需要 `SOCKET_CLI_API_TOKEN` 才能具有完整功能)相比。
- **可作为 MCP 工具调用,而不仅仅是 CLI。** `npx skillguard-cli mcp` 启动一个 stdio MCP 服务器,暴露一个 `scan_skill` 工具,以便 Claude Code、Cursor 或任何编排代理可以直接作为工具调用,在安装或运行之前扫描下载的技能,无需子进程,无需 stdout 解析。
- **两个独立的、同样维护的发行版。** npm 包和 PyPI 包读取相同的规则包合同,并针对相同的目标产生匹配的发现;两者都不是对方的包装。
- **扫描时不会从网络获取任何内容。** 所有规则包都包含在包本身中,因此对不受信任内容的扫描永远不会延伸到内容控制的任何内容。
## 快速入门
```bash
npx skillguard-cli scan ./my-skill --format json --severity-threshold MEDIUM
```
```ts
import { scanSkill } from 'skillguard-cli';
const result = await scanSkill('./my-skill', { severityThreshold: 'HIGH' });
if (result.exitCode === 1) {
console.log(`${result.findings.length} 个发现,严重程度为 HIGH 及以上`);
}
```
```python
from skillguard import scan_skill, ScanOptions
result = scan_skill("./my-skill", ScanOptions(severity_threshold="HIGH"))
if result.exit_code == 1:
print(f"{len(result.findings)} 个发现,严重程度为 HIGH 及以上")
```
## 命令
```bash
npx skillguard-cli <command> [options]
# Python:skillguard <command> [options]
```
| 命令 | 执行操作 |
| --- | --- |
| `scan <path>` | 扫描一个技能目录(一个 `SKILL.md` 及其钩子/脚本)以查找已知的攻击模式。 |
| `scan-set <dir>` | 扫描一个目录,其直接子目录都是技能目录,对每个目录运行 `scan`,并额外检查跨技能特权链接(SG09)。 |
| `mcp` | 以 stdio MCP 服务器的形式启动 SkillGuard,暴露 `scan_skill` 作为可调用工具,供另一个代理使用。参见 [MCP 服务器](#mcp-服务器-代理原生工具调用)。 |

`scan` 和 `scan-set` 共享的标志(验证自 `src/cli.ts` 和 `skillguard/cli.py`):
| 标志 | 默认值 | 描述 |
| --- | --- | --- |
| `--format <human\|json\|sarif>` | `human` | 输出格式。 |
| `--severity-threshold <HIGH\|MEDIUM\|LOW>` | `HIGH` | 扫描失败的最低严重程度(退出代码 1)。 |
| `--timeout <ms>` | `10000` | 毫秒为单位的文件扫描超时。 |
| `--skillguardignore <path>` | 无,需显式传递 | 抑制文件路径。永远不会从扫描目标内部自动加载;参见 [抑制发现](#抑制发现)。 |
| `--allow-inline-suppression` | `false` | 允许扫描文件中找到的内联 `# skillguard-ignore: SGxx` 注释。默认关闭。 |
**退出代码**:`0` 表示扫描干净。`1` 表示发现严重程度为阈值或以上的发现。`2` 表示目标路径不存在,或者(对于 `scan`)未找到技能文件。
## API 参考
**TypeScript**(`src/index.ts`),从 npm 包的根导入重新导出:
```ts
import { scanSkill, scanSkillSet, discoverSkillDirs, computeCrossSkillFindings } from 'skillguard-cli';
scanSkill(path: string, options?: ScanOptions): Promise<ScanResult>
scanSkillSet(dir: string, options?: ScanOptions): Promise<SkillSetScanResult>
```
`scanSkill()` 运行与 `scan` 相同的扫描逻辑;`scanSkillSet()` 运行与 `scan-set` 相同的逻辑。两者都返回一个结构化结果(`findings`、`timeouts`、`warnings`、`exitCode`),而不是抛出异常,因此调用者嵌入 SkillGuard 时无需将其包装在 try/catch 中以获取判决。 `Finding`、`ScanOptions`、`ScanResult`、`ScanWarning`、`Severity`、`OutputFormat`、`RuleCategory`、`SkillEntry` 和 `SkillSetScanResult` 类型随包一起提供。
**Python**(`python/src/skillguard/__init__.py`):
```python
from skillguard import scan_skill, scan_skill_set, discover_skill_dirs, compute_cross_skill_findings, ScanOptions
scan_skill(path: str, options: ScanOptions | None = None) -> ScanResult
scan_skill_set(dir: str, options: ScanOptions | None = None) -> SkillSetScanResult
```
形状相同,命名采用蛇形命名。 一个诚实的差距值得明确说明:Python 包的 `scan_skill_set()` 运行与 npm 包的 `scanSkillSet()` 相同的跨技能链接编排,但 npm 包还运行第二个、更窄的 SG09 检查(`src/ast/cross-skill-chaining.ts`),而 Python 尚未提供此功能。如果您依赖于单个 `scan` 调用中的跨技能检测,而不是 `scan-set`,则此路径仅限于 TypeScript。
## MCP 服务器(代理原生、工具调用)
```bash
npx skillguard-cli mcp
```
以 stdio MCP 服务器的形式启动 SkillGuard,暴露一个工具 `scan_skill`(`{ path, severityThreshold?, timeoutMs? }`),以便另一个代理(如 Claude Code、Cursor 或编排器)可以直接作为工具调用,在安装或运行之前扫描第三方技能。 在此更新中通过真实的握手验证:`initialize` 返回真实的服务器信息,`tools/list` 返回真实的 `scan_skill` 模式,`tools/call` 针对 `./examples/known-bad-skill` 返回与 CLI 报告的相同的 11 个发现。 客户端配置示例:
```json
{ "mcpServers": { "skillguard": { "command": "npx", "args": ["skillguard-cli", "mcp"] } } }
```
完整的设置、工具的输入/输出模式以及此路径保留的安全保证(与 CLI 相同的不忽略路径/内联抑制默认值,无法通过工具传递忽略路径)都在 [docs/integrations/mcp.md](./docs/integrations/mcp.md) 中。
## GitHub Action
```yaml
- name: SkillGuard
uses: RudrenduPaul/skillguard@main
with:
path: ./my-skill
severity-threshold: HIGH
```
Action 默认使用 `--format sarif` 并直接将结果上传到 GitHub 代码扫描。 裸 CLI 保留人类可读的默认值,因为终端和 CI 日志需要不同的内容。
## 规则包
每个规则包都位于 `rulepacks/`(npm)和 `python/src/skillguard/rulepacks/data/`(Python)下,由人工保持同步,并受 [CONTRIBUTING.md](./CONTRIBUTING.md) 的同等性规则约束。 扫描时不会从网络获取任何内容。
| 类别 | 名称 | 严重程度 | 它能捕获什么 |
| --- | --- | --- | --- |
| SG01 | 网络不匹配 | 中等 | 原始套接字、netcat、`/dev/tcp`、绕过典型仅 HTTP 范围的网络原语。 |
| SG02 | 远程代码执行 | 高 | `curl \| bash`、`eval()`/ `exec()` 获取的响应、远程影响的内容外壳执行。 |
| SG03 | 文件范围提升 | 中等 | 在技能自己的目录之外写入/删除/ `chmod`,以及用于逃逸的路径遍历(`../../..`)。 |
| SG04 | 钩子供应链 | 高 | 安装时钩子或依赖项获取并运行远程代码,或固定到可变的分支。 |
| SG05 | 混淆有效负载 | 低 | base64/字符串连接/动态 `Function` 习惯用于隐藏有效负载。 参见下面的已知限制。 |
| SG06 | 凭证收集 | 高 | 读取凭证形状的环境变量或凭证文件,特别是在网络调用附近。 |
| SG07 | 前端范围伪造 | 中等 | SKILL.md 声明的网络/文件范围与钩子/脚本实际执行的操作。 |
| SG08 | 通过技能内容注入提示 | 高 | SKILL.md 自己的指令文本尝试覆盖主机代理的系统提示或劫持其工具路由(7 个规则 ID:`sg08-ignore-prior-instructions`、`sg08-disregard-system-prompt`、`sg08-fake-mode-switch`、`sg08-reveal-system-prompt`、`sg08-hide-action-from-user`、`sg08-hidden-unicode-characters`、`sg08-encoded-block-in-instructions`)。 |
| SG09 | 跨技能特权链接 | 高 | 在 `scan-set` 运行中组合两个技能的能力,一个具有敏感文件系统读取权限,一个具有网络出口权限,两者之间没有沙盒隔离。 TypeScript 还运行此检查的更窄的单一技能兄弟路径变体;Python 目前仅提供 `scan-set` 版本。 |
| SG10 | 市场域名抢注 | 高 | SKILL.md 声明的名称与 51 个流行的 npm/PyPI 包名称列表的 Levenshtein 编辑距离为 1-2,尝试冒充知名工具。 |
**SG05 已知限制**:纯静态分析下的混淆检测比其他类别具有更高的假阴性率。 SG05 捕获已知、常见的编码/求值习惯;将其视为尽力而为的覆盖,而不是对混淆的完整答案。
## 技能守护的比较
技能守护专门用于代理技能威胁模型、前端声明范围与实际行为、钩子级供应链风险、跨技能特权组合以及针对技能打包和安装方式量身定制的凭证收集模式。 这是一个比通用扫描器更狭窄的任务,本表明确说明了这一点,而不是暗示技能守护优于为不同任务构建的工具。
| | 技能守护 | Snyk Agent Scan | Semgrep (CE) | Socket CLI |
| --- | --- | --- | --- | --- |
| 扫描内容 | 代理技能文件:SKILL.md、钩子、脚本 | 代理技能和 MCP 服务器配置 | 通用源代码,30+ 语言 | npm/PyPI/etc. 包安装 |
| 代理技能特定规则集 | 是,所有 10 个类别专门为此威胁模型构建 | 是,其全部关注点(提示注入、工具中毒、有毒流、技能恶意软件) | 否,仅通用 SAST 规则 | 否,供应链关注,不关注技能文件 |
| 扫描多个技能以获取组合风险 | 是,`scan-set`(SG09 跨技能特权链接) | 否,已通过 [问题 #301](https://github.com/snyk/agent-scan/issues/301) 确认单文件/非递归 | 不适用于此威胁模型 | 不适用于此威胁模型 |
| 可作为另一个代理的 MCP 工具调用 | 是,`skillguard-cli mcp` | 未公开记录 | 否 | 否 |
| 基本扫描所需的身份验证 | 无(`npx skillguard-cli scan <path>`) | 是,需要 Snyk 帐户和 `SNYK_TOKEN` | 社区版本地扫描无需身份验证 | 是,需要 `SOCKET_CLI_API_TOKEN` 才能具有完整功能 |
| 运行时 | Node.js,无外部二进制(也提供 Python 端口) | 通过 `uvx` 使用 Python | 自己的二进制(基于 Python 的 CLI) | Node.js |
| 许可证 | Apache 2.0 | Apache 2.0 | LGPL-2.1 | MIT(根据 [`socket` npm 包列表](https://www.npmjs.com/package/socket);存储库本身不包含 LICENSE 文件) |
| GitHub 星数(2026-07-18 检查) | 0,一个星期,启动于 2026-07-11 | 2,790 星,250 个分叉 | 15,945 星,992 个分叉 | 299 星,54 个分叉 |
一些诚实的备注,由于安全工具的可信度取决于说出不利的一面至关重要:
- **Snyk Agent Scan 是最接近的真正竞争对手**,更成熟(2,790 个星 vs. SkillGuard 的 0 个),并且覆盖面更广(15+ 检测类别,以及 MCP 服务器配置扫描,而 SkillGuard 不执行这些操作)。真正的区别在于身份验证故事(SkillGuard 无需注册或令牌)和跨技能分析(Snyk 的维护者确认他们的工具目前是单文件的)。如果您已经拥有 Snyk 帐户,并希望同时获得 MCP 服务器覆盖,则 Agent Scan 涵盖的范围更广;如果您想要一个无需身份验证的检查,也可以考虑整个目录的技能,则这是 SkillGuard 的用途。
- **SkillGuard 不包装 Semgrep。** 它配备了自己的小型进程内模式引擎(Semgrep 启发,但不是 Semgrep),因此 `npx` 安装永远不会依赖 Python/PyPI 工具链。如果您需要对 30 多种语言进行真正的跨语言静态分析,Semgrep 是更成熟的工具。
- **Socket CLI 属于不同的类别,不是直接竞争对手。** 它用于 npm/PyPI 包的域名抢注和恶意安装脚本,而不是技能自身的 SKILL.md 前端和声明的行为。
- **SkillGuard 本身非常早期**:零 GitHub 星,仅在 2026-07-11 提交了第一个提交。那是一个真正的“早期但真实”的故事,而不是值得粉饰的:本 README 中显示的每个命令和发现都是在编写时针对实际代码运行的,而不是从旧草稿中继承的。
## 抑制发现结果
**信任模型:** SkillGuard 的任务是审查您没有编写的目录。下面两种抑制机制都可以使发现结果保持沉默,因此在扫描目标内部永远不会自动信任任何一种机制。每种机制都需要运行扫描的人员进行明确、故意的选择。
`.skillguardignore` 文件通过 glob 抑制整个文件,与 `.gitignore` 的思维模型相同:
```
# .skillguardignore
vendor/**
*.generated.js
```
**只有在您传递 `--skillguardignore <path>`** (或 `ignoreFilePath` 库选项)时,才会遵循此文件。SkillGuard 永远不会在扫描目标内部自动读取 `.skillguardignore` 文件。恶意的技能提交如果附带自己的 `.skillguardignore` 文件,除非您明确指出 SkillGuard 读取该文件,否则无法消除关于它自己的发现结果。如果您自己维护技能并希望自我抑制,请保留您的 `.skillguardignore` 文件并显式传递其路径:
```bash
npx skillguard-cli scan ./my-skill --skillguardignore ./my-skill/.skillguardignore
```
内联 `# skillguard-ignore: SG02` 评论(在匹配行上或直接在上一行)会抑制单个发现结果。此功能**默认关闭**,需要 `--allow-inline-suppression` 参数,原因相同:评论位于正在审查的精确不受信任内容内部,因此默认情况下,扫描目标内部的任何内容都无法消除关于它自己的发现结果。只有在您已经信任目标时才启用它,例如在发布之前扫描自己的技能。
## 已知限制
SkillGuard 有意在公开文档中说明其不足之处。除了上面 SG05 的假阴性注释外,还有两个值得注意的限制:符号链接的扫描目标不会被扫描,而是保持原样,Python 包的跨技能检测目前仅存在于 `scan-set` 中,而 TypeScript 则具有单一技能的兄弟路径检查。完整的列表,包括残留的单个模式 ReDoS 风险,位于 [CHANGELOG.md](./CHANGELOG.md) 中。在将 SkillGuard 集成到 CI 门控之前,请先阅读它。
## 开发
```bash
# TypeScript
npm install
npm run build
npm test
# Python
cd python
python3 -m venv .venv && source .venv/bin/activate
pip install -e ".[dev,mcp]"
pytest
```
参见 [CONTRIBUTING.md](./CONTRIBUTING.md):规则包的更改必须同时在 `rulepacks/` 和 `python/src/skillguard/rulepacks/data/` 中进行,并具有等效的测试覆盖率,因为如果一个规则仅存在于一种语言中,则在两个 CLI 之间存在静默行为差距。
## 常见问题
**什么是 SkillGuard,它与通用安全扫描器有什么不同?**
SkillGuard 是一种静态分析扫描器,专门用于一种威胁模型:第三方 AI 代理技能文件、`SKILL.md` 清单以及它们捆绑的钩子和脚本。它提供了十种规则类别(SG01-SG10),针对通用扫描器默认不检查的内容、前端声明的作用域与实际行为、安装时钩子供应链风险、嵌入在技能自身指令文本中的提示注入,以及同一目录中多个技能的综合风险。像 Semgrep 这样的通用 SAST 工具可以发现一些相同的代码级模式,但它没有 `SKILL.md` 清单或跨技能特权关系的概念。
**SkillGuard 是否需要帐户或 API 令牌来运行扫描?**
否。`npx skillguard-cli scan <path>` 和 `pip install skillguard-cli && skillguard scan <path>` 都可以在无需注册、令牌和扫描时联网的情况下工作。
**SkillGuard 支持哪些平台和语言运行时?**
npm 包需要 Node.js >=20(根据 `package.json` 的 `engines` 字段),并且没有本机/编译依赖项。PyPI 包需要 Python >=3.9 作为基本安装,或者如果您安装可选的 `mcp` 额外功能以启用 MCP 服务器模式,则需要 >=3.10(根据 `pyproject.toml` 的 `requires-python` 及其 `mcp` 可选依赖项注释)。两者都是纯语言包,无需 OS 特定构建步骤,Python 包的分类器声明 `Operating System :: OS Independent`。
**SG08、SG09 和 SG10 检查什么,它们也可以通过 MCP 服务器运行吗?**
SG08(高风险)查找技能自身指令文本中的提示注入,尝试覆盖主机代理的系统或劫持其工具路由,涵盖 7 个不同的规则 ID。SG09(高风险)是跨技能特权链:`scan-set` 在目录中的一个技能具有敏感文件系统读取能力,另一个具有网络出口能力,但两者之间没有声明沙盒隔离时,会标记为高风险发现;TypeScript 的普通 `scan` 也运行一个更窄的兄弟路径变体检查。SG10(高风险)是市场_typosquatting:技能的声明名称与 51 个流行 npm/PyPI 包名称列表的 Levenshtein 编辑距离为 1-2。所有这三个检查都通过 `npx skillguard-cli mcp` 的 `scan_skill` 工具运行,与通过 CLI 运行的结果完全相同,因为 MCP 服务器调用相同的底层扫描逻辑。
**AI 代理可以直接调用 SkillGuard 而不是通过 CLI 调用并解析输出吗?**
可以。`npx skillguard-cli mcp` (或 Python 端的 `skillguard mcp`)启动了一个 stdio MCP 服务器,暴露了一个 `scan_skill` 工具,以便编排器或编码代理可以在安装或运行下载的技能之前,作为正常工具调用来扫描它。
**SkillGuard 是否能检测到引用了更特权技能的技能?**
`scan-set` 可以:将其指向一个技能子目录列表,如果一个技能具有敏感文件系统读取能力,另一个具有网络出口能力,但两者之间没有声明沙盒隔离,则会标记为高风险发现。这是 Snyk Agent Scan 的问题跟踪器确认其工具尚未涵盖的特定差距(单文件,非递归)。
**npm 包和 PyPI 包有什么区别?**
两者都是独立的、等效维护的端口,它们读取相同的规则包合同,并针对相同的目标生成匹配的发现结果;PyPI 包是真正的 Python 实现,而不是 Node 二进制文件的包装器。目前存在一个差距:TypeScript 的 `scan` 命令运行了一个额外的兄弟路径跨技能启发式检查,而 Python 尚未实现(Python 的跨技能检测完全存在于 `scan-set` 中)。如果您依赖于单个 `scan` 调用而不是 `scan-set` 的跨技能检测,那么目前只有 TypeScript 支持此功能,请在 Python 端运行 `scan-set` 以获得等效的覆盖率。
**SkillGuard 是否取代 Semgrep 或 Snyk?**
否。Semgrep 是一个成熟的、通用的静态分析引擎,支持 30 多种语言;Snyk 广泛涵盖依赖和代码漏洞,Snyk Agent Scan 涵盖的代理技能和 MCP 配置威胁比 SkillGuard 更广泛。SkillGuard 的任务更狭窄:特定的 SKILL.md/钩子/前端威胁模型,无需身份验证,且具有跨技能检查,而其他工具目前尚不支持。
**SkillGuard 是否已准备好投入生产?**
还处于早期阶段:仅一周历史,版本低于 1.0,编写时 GitHub 星标数为零。测试套件(157/157 TypeScript,110/110 Python)在干净安装和本 README 中的每个命令都独立重新运行后通过,但尚未针对大型现实世界语料库运行,因此将其假阳性/假阴性率视为未经大规模验证。
**我可以商业使用 SkillGuard 吗?许可证是否需要付费?**
可以,不需要。SkillGuard 采用 Apache License 2.0(参见 [LICENSE](./LICENSE)),允许商业使用、修改和重新分发,包括在专有软件中,无需支付任何费用,但需保留版权和许可证声明。Apache 2.0 还授予了明确的专利许可,而 plain MIT 许可证不提供此许可。
## 许可证
Apache 2.0,参见 [LICENSE](./LICENSE)。
MCP 配置
以下是此 MCP Server 的配置信息,可直接复制到 Cursor 或其他 MCP 客户端中使用。
mcp.json
连接信息
猜你喜欢
暂无推荐服务器