MCP stdio 服务器的纵深防御包:即插即用的 guardExec/guardSpawn 包装器、AST 审计 CLI、参考 MCP 服务器。修复了 Ox-Security 20 万服务器 stdio-RCE 类漏洞(LiteLLM CVE-2025-69256)。MIT 许可,TypeScript,Node >= 20。
** StudioMeyer MCP Stack 的一部分** — 在马略卡岛构建 🌴 · 如果使用请⭐
针对 MCP stdio 服务器的深度防御捆绑包。使用白名单 + 沙箱 + 重放检测包装 child_process.exec/spawn,外加一个 AST 审计 CLI (mcp-shellguard-audit),用于扫描 MCP 服务器源中未清理的 shell 调用。封堵了 Ox-Security MCP stdio-RCE 漏洞类别(200k 台易受攻击服务器,2026 年 5 月披露)。
@modelcontextprotocol/sdk ^1.29.0npm install mcp-stdio-shellguard
或者直接运行审计 CLI 而无需安装:
npx -y -p mcp-stdio-shellguard mcp-shellguard-audit scan ./src
三个层次,可逐层选择使用:
guardExec / guardSpawn,您可以从自己的 MCP 服务器中调用。默认拒绝白名单、沙箱配置文件、重放窗口。mcp-shellguard-audit scan <path> 遍历 AST,报告 12 种反模式,从 LOW(no timeout)到 CRITICAL(exec(\...${userInput}...`)`)。mcp-stdio-shellguard-demo 公开了 8 个工具,以便 MCP Inspector / Claude Desktop 可以直接驱动这个捆绑包。调用者可以通过每次调用的 timeoutMs / fdBudget 来收紧。调用者不能放宽超出配置文件的限制。
| 等级 | 条件 |
|---|---|
| LOW | 工具未注册(默认拒绝) |
| MEDIUM | 已注册但 argsPatterns 为空(允许任何参数) |
| HIGH | 设置了 但沙箱或重放跟踪器未激活 |
通过注册工具 + 设置 argsPatterns + 通过 guardExec/guardSpawn 运行(始终激活沙箱 + 重放)来将 LOW 提升为 CRITICAL。
import {
AllowlistRegistry,
ReplayWindow,
guardExec,
} from "mcp-stdio-shellguard";
const registry = new AllowlistRegistry();
const replay = new ReplayWindow();
registry.register({
toolName: "git-log",
executable: "/usr/bin/git",
argsPatterns: ["^log$", "^--oneline$", "^-n$", "^\\d+$"],
sandboxProfile: "strict",
});
const result = await guardExec(
{
toolName: "git-log",
command: "/usr/bin/git",
args: ["log", "--oneline", "-n", "10"],
},
{ registry, replay },
);
console.log(result.stdout); // → commit lines
console.log(result.trustTier); // → "CRITICAL"
console.log(result.canonicalHash); // → 64-char SHA-256
mcp-shellguard-audit scan ./src
mcp-shellguard-audit scan ./src --format sarif --output audit.sarif
mcp-shellguard-audit scan ./src --severity-floor HIGH # CI gate
退出代码:
0 干净(找不到达到或高于下限的问题)1 存在问题2 解析 / IO 错误扫描器在匹配前解析了重命名的 child_process 绑定,因此即使调用通过别名而非字面量的 child_process.exec,以下危险形式也会被捕获:
const execAsync = promisify(exec); execAsync(...${x})import cp from "node:child_process"; cp.exec(...${x})const { exec: sh } = require("child_process"); sh(...${x})import { exec as run } from "node:child_process"; run(...)同步变体(spawnSync、execFileSync)共享其异步规则,并且 shell_true_option 也会在字符串 shell({ shell: "/bin/sh" })或动态 shell 值上触发——而不仅仅是字面量 { shell: true }。对非 child_process 函数的 promisify、从其他模块解构、以及 { shell: false } 保持干净(无误报)。
// shellguard:ignore-next-line — 抑制一条结果// shellguard:ignore-file — 抑制整个文件(很少使用;建议逐行抑制)Ox-Security 披露(2026 年 5 月)超过 20 万个 MCP stdio 服务器将 child_process.exec 与包含直接来自 LLM 工具参数的用户输入的模板字面量包装在一起。LiteLLM v1.83.6 是典型示例(CVE 在 1.83.7 中修补)。此捆绑包是防御安全对应物:一个可直接使用的防护 + 扫描器,封堵了该漏洞类别。灵感来自 AWS Linux seccomp + Chromium 沙箱等级。
HOOK_RECIPES.md — Claude Code 钩子配方,自动阻止危险工具调用CHANGELOG.md — 发布历史MIT — Copyright (c) 2026 Matthias Meyer (StudioMeyer)
| 工具 | 类型 | 用途 |
|---|
guard_exec | destructive | 受保护的 child_process.exec。强制使用 args[] 向量、白名单 + 沙箱 + 重放。返回 stdout、stderr、exitCode、canonicalHash、isReplay、trustTier。 |
guard_spawn | destructive | 受保护的 child_process.spawn。返回 stdout/stderr 的 SHA-256 哈希值而非完整内容。硬性拒绝 shell:true。 |
register_allowlist | mutating | 使用可执行文件 + 参数正则表达式注册工具名称。未注册时应用默认拒绝。 |
audit_source | read-only | 扫描 TS/JS 路径以查找 shell 注入反模式。返回 AuditFinding[] + 摘要。 |
audit_report | read-only | 将审计结果格式化为 markdown / json / SARIF 2.1.0。 |
replay_check | read-only | 计算调用的规范 SHA-256 哈希值,并报告它是否已在重放窗口中。 |
sandbox_status | read-only | 报告活动的沙箱配置文件 + 具体限制 + cgroup-v2 活动标志。 |
trust_tier | read-only | 为已注册的工具推导出 LOW/MEDIUM/HIGH/CRITICAL 等级,以及改进提示。 |
| 配置文件 | 超时时间 | 最大 stdout | 最大 stderr | 文件描述符预算 | cgroup-v2 |
|---|
strict | 5 s | 1 MB | 256 KB | 32 | yes (cpu/memory) |
standard (default) | 30 s | 10 MB | 1 MB | 256 | yes |
permissive | 5 min | 100 MB | 10 MB | 1024 | no |
argsPatterns| CRITICAL | argsPatterns + 沙箱 + 重放全部激活 |
| ID | 严重性 | 触发条件 |
|---|
exec_template_literal_with_input | CRITICAL | child_process.exec(\ls ${x}`)` |
exec_dynamic_string | CRITICAL | child_process.exec(cmd) |
exec_sync_dynamic_string | CRITICAL | child_process.execSync(cmd) |
eval_near_child_process | CRITICAL | eval(...) |
function_constructor_near_child_process | CRITICAL | new Function(...) |
spawn_dynamic_file_args | HIGH | spawn(bin, userArgs) |
exec_file_dynamic | HIGH | execFile(bin, ...) |
shell_true_option | HIGH | { shell: true } |
os_system_equivalent | HIGH | Deno.run / Bun.spawn |
spawn_literal_dynamic_args | MEDIUM | spawn('git', userArgs) |
unbounded_buffer | LOW | exec without maxBuffer |
missing_timeout | LOW | exec/spawn without timeout |