
حزمة دفاع متعدد الطبقات لخوادم MCP stdio: أغلفة guardExec/guardSpawn جاهزة للتبديل المباشر (drop-in)، وأداة CLI لتدقيق AST، وخادم MCP مرجعي. تسد فئة ثغرة stdio-RCE الخاصة بـ Ox-Security التي تمس 200 ألف خادم (LiteLLM CVE-2025-69256). MIT، TypeScript، Node >= 20.
<!-- studiomeyer-mcp-stack-banner:start -->
> **جزء من [StudioMeyer MCP Stack](https://studiomeyer.io)** — صُنع في مايوركا 🌴 · ⭐ إذا استخدمته
<!-- studiomeyer-mcp-stack-banner:end -->
# mcp-stdio-shellguard
<!-- badges -->
[](https://www.npmjs.com/package/mcp-stdio-shellguard)
[](https://www.npmjs.com/package/mcp-stdio-shellguard)



<!-- /badges -->حزمة دفاع متعدد الطبقات لخوادم MCP stdio. تُغلِّف `child_process.exec/spawn`
بقائمة سماح + صندوق رمل + كشف إعادة التشغيل، إضافةً إلى أداة تدقيق AST عبر سطر الأوامر (`mcp-shellguard-audit`)
تفحص مصادر خادم MCP بحثًا عن استدعاءات shell غير المعقَّمة. تُغلق فئة Ox-Security
MCP stdio-RCE (200 ألف خادم معرَّض للخطر، إفصاح مايو 2026).
- **مواصفة MCP**: 2025-06-18
- **SDK**: `@modelcontextprotocol/sdk` ^1.29.0
- **Node**: >= 20
- **الترخيص**: MIT
- **المؤلف**: Matthias Meyer (StudioMeyer)
## التثبيت
```bash
npm install mcp-stdio-shellguard
```
أو شغّل واجهة سطر أوامر التدقيق مباشرةً دون تثبيت:
```bash
npx -y -p mcp-stdio-shellguard mcp-shellguard-audit scan ./src
```
## ما الذي يوفره لك
ثلاث طبقات، اختيارية بشكل تدريجي:
1. **واجهة برمجة المكتبة** — `guardExec` / `guardSpawn` جاهزة للاستخدام تستدعيها من
خادم MCP الخاص بك. قائمة سماح بالرفض الافتراضي، ملفات تعريف العزل، نافذة إعادة التشغيل.
2. **أداة التدقيق CLI** — `mcp-shellguard-audit scan <path>` يتجول في شجرة AST، ويبلّغ
عن 12 نمطًا مضادًا من LOW (`no timeout`) إلى CRITICAL (`exec(\`...${userInput}...\`)`).
3. **خادم MCP المرجعي** — `mcp-stdio-shellguard-demo` يعرض 8 أدوات
حتى يتمكن MCP Inspector / Claude Desktop من تشغيل الحزمة مباشرة.
## الأدوات (الخادم المرجعي)
| الأداة | النوع | الغرض |
|------|------|---------|
| `guard_exec` | مدمّرة | `child_process.exec` محمية. تفرض متجه args[]، قائمة السماح + العزل + إعادة التشغيل. تُرجع `stdout`، `stderr`، `exitCode`، `canonicalHash`، `isReplay`، `trustTier`. |
| `guard_spawn` | مدمّرة | `child_process.spawn` محمية. تُرجع تجزئات SHA-256 لـ stdout/stderr بدلاً من المحتوى الكامل. ترفض `shell:true` بشكل صارم. |
| `register_allowlist` | معدّلة | تسجيل اسم أداة مع ملف تنفيذي + تعبير نمطي للوسائط. بدون التسجيل يُطبّق الرفض الافتراضي. |
| `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 ثوانٍ | 1 ميغابايت | 256 كيلوبايت | 32 | نعم (cpu/memory) |
| `standard` (افتراضي) | 30 ثانية | 10 ميغابايت | 1 ميغابايت | 256 | نعم |
| `permissive` | 5 دقائق | 100 ميغابايت | 10 ميغابايت | 1024 | لا |
يمكن للمستدعي التشديد عبر `timeoutMs` / `fdBudget` لكل استدعاء. لا يمكن للمستدعي التوسيع
بما يتجاوز الملف التعريفي.
## مستويات الثقة
| المستوى | الشرط |
|------|-----------|
| LOW | الأداة غير مسجّلة (الرفض الافتراضي) |
| MEDIUM | مسجّلة لكن `argsPatterns` فارغة (أي وسائط مسموح بها) |
| HIGH | `argsPatterns` محددة لكن العزل أو متتبع إعادة التشغيل غير نشط |
| CRITICAL | argsPatterns + العزل + إعادة التشغيل جميعها نشطة |
ارفع من LOW → CRITICAL بتسجيل الأداة + تعيين argsPatterns + التشغيل
عبر `guardExec`/`guardSpawn` (التي تفعّل دائمًا العزل + إعادة التشغيل).
## البدء السريع للمكتبة
```ts
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
```
## واجهة سطر أوامر التدقيق
```bash
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` أخطاء تحليل / إدخال وإخراج
تقبل `ignoreGlobs` المخصصة التي تُمرَّر إلى `scanPath` أو أداة MCP `audit_source`
128 سلسلة نصية كحد أقصى. كل سلسلة محدودة بـ 1024 وحدة من وحدات UTF-16 و16
حرف تجميع افتتاحي غير مهرَّب (`(`, `{`, `[`). المدخلات التي تتجاوز هذه
الميزانيات تُطلق خطأً خاضعاً للتحكم قبل المطابقة.
## مكتبة الأنماط المضادة (12 قاعدة)
| المعرّف | الخطورة | يُفعَّل عند |
|----|----------|-------------|
| `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 بدون `maxBuffer` |
| `missing_timeout` | LOW | exec/spawn بدون `timeout` |
يحل الماسح *الارتباطات المُعاد تسميتها* لـ `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 }` الحرفي. يبقى
`promisify` لدالة ليست من child_process، والتفكيك من وحدة أخرى،
و`{ shell: false }` نظيفاً (دون نتائج إيجابية خاطئة).
## التوجيهات (Pragmas)
- `// shellguard:ignore-next-line` — كتم نتيجة واحدة
- `// shellguard:ignore-file` — كتم الملف بأكمله (نادر؛ يُفضَّل لكل سطر)
## سبب وجود هذا
أفصحت Ox-Security (2026-05) أن أكثر من 200 ألف خادم MCP stdio يغلّفون
`child_process.exec` بقوالب نصية تحمل مدخلات المستخدم مباشرة من
وسائط أدوات LLM. كان LiteLLM v1.83.6 المثال النموذجي (تم ترقيع CVE في 1.83.7).
هذه الحزمة هي النظير الأمني الدفاعي: حارس + ماسح جاهز للاستخدام
يسدّ هذه الفئة. مستوحى من `seccomp` في AWS Linux + مستويات sandbox في Chromium.
## انظر أيضاً
- `HOOK_RECIPES.md` — وصفات خطافات Claude Code التي تحظر تلقائياً استدعاءات الأدوات الخطرة
- `CHANGELOG.md` — سجل الإصدارات
- تدقيق Ox-Security MCP: <https://venturebeat.com/security/200000-mcp-stdio-servers/>
- LiteLLM CVE-2026-XXXX: <https://github.com/BerriAI/litellm/security/advisories>
## الترخيص
MIT — حقوق النشر (c) 2026 Matthias Meyer (StudioMeyer)