
MCP stdio サーバー向け多層防御バンドル: そのまま使える guardExec/guardSpawn ラッパー、AST 監査 CLI、リファレンス MCP サーバー。Ox-Security の 20 万サーバー規模の stdio-RCE クラス(LiteLLM CVE-2025-69256)を解消。MIT ライセンス、TypeScript、Node >= 20。
StudioMeyer MCP Stack の一部 — マヨルカ島でビルド 🌴 · ⭐ お使いの際はスターを
許可リスト + サンドボックス + リプレイ検出でラップし、さらに MCP サーバーソースをスキャンして未サニタイズのシェル呼び出しを検出する AST 監査 CLI(mcp-shellguard-audit)
を提供します。Ox-Security の
MCP stdio-RCE クラス(2026年5月開示、20万の脆弱なサーバー)を閉じます。
@modelcontextprotocol/sdk ^1.29.0npm install mcp-stdio-shellguard
または、インストールせずに監査 CLI を直接実行:
npx -y -p mcp-stdio-shellguard mcp-shellguard-audit scan ./src
3つのレイヤーで、個別にオプトインできます:
guardExec / guardSpawn。デフォルト拒否の許可リスト、サンドボックスプロファイル、リプレイウィンドウ。mcp-shellguard-audit scan <path> が AST を走査し、LOW(no timeout)から CRITICAL(exec(\...${userInput}...`)`)までの12のアンチパターンを報告します。mcp-stdio-shellguard-demo は8つのツールを公開し、MCP Inspector / Claude Desktop がバンドルを直接操作できます。呼び出し側は呼び出しごとに timeoutMs / fdBudget で制限を厳しくできます。プロファイルを超えて緩めることはできません。
| ティア | 条件 |
|---|---|
| LOW | ツールが未登録(デフォルト拒否) |
| MEDIUM | 登録済みだが argsPatterns が空(任意の引数が許可される) |
| HIGH | argsPatterns が設定されているが、サンドボックスまたはリプレイトラッカーが無効 |
ツールの登録 + 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: true } だけでなく、文字列シェル({ shell: "/bin/sh" })や動的なシェル値でも発火します。child_process 以外の関数の promisify、別モジュールからの分割代入、{ shell: false } はクリーンのままです(誤検知なし)。
// shellguard:ignore-next-line — 1つの検出結果を抑制// shellguard:ignore-file — ファイル全体を抑制(稀;行単位を推奨)Ox-Security は(2026-05)、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 | 破壊的 | 保護された child_process.exec。args[] ベクターを強制し、許可リスト + サンドボックス + リプレイを適用。stdout、stderr、exitCode、canonicalHash、isReplay、trustTier を返します。 |
guard_spawn | 破壊的 | 保護された child_process.spawn。完全な本文の代わりに stdout/stderr の SHA-256 ハッシュを返します。shell:true をハード拒否します。 |
register_allowlist | 変更あり | ツール名を実行ファイル + args 正規表現とともに登録します。登録がない場合はデフォルト拒否が適用されます。 |
audit_source | 読み取り専用 | TS/JS パスをシェルインジェクションのアンチパターンについてスキャンします。AuditFinding[] + サマリーを返します。 |
audit_report | 読み取り専用 | 監査結果を markdown / json / SARIF 2.1.0 としてフォーマットします。 |
replay_check | 読み取り専用 | 呼び出しの正規 SHA-256 ハッシュを計算し、それがすでにリプレイウィンドウ内にあるかどうかを報告します。 |
sandbox_status | 読み取り専用 | アクティブなサンドボックスプロファイル + 具体的な制限 + cgroup-v2 アクティブフラグを報告します。 |
trust_tier | 読み取り専用 | 登録済みツールの LOW/MEDIUM/HIGH/CRITICAL ティアを導出し、改善のヒントも提供します。 |
| プロファイル | タイムアウト | 最大 stdout | 最大 stderr | FD バジェット | cgroup-v2 |
|---|
strict | 5 s | 1 MB | 256 KB | 32 | あり(cpu/memory) |
standard(デフォルト) | 30 s | 10 MB | 1 MB | 256 | あり |
permissive | 5 min | 100 MB | 10 MB | 1024 | なし |
| 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 |