Retour aux mises à jour
New releaseAug 21, 2026

mcp-stdio-shellguard v0.1.2

Bundle de défense en profondeur pour serveurs MCP stdio : wrappers guardExec/guardSpawn prêts à l'emploi, CLI d'audit AST, serveur MCP de référence. Corrige la classe de vulnérabilité stdio-RCE Ox-Security 200k-server (LiteLLM CVE-2025-69256). MIT, TypeScript, Node >= 20.

Partager

Partie de la pile MCP StudioMeyer — Construit à Majorque 🌴 · ⭐ si vous l'utilisez

mcp-stdio-shellguard

npm version npm downloads License Last commit GitHub stars

Bundle de défense en profondeur pour les serveurs MCP stdio. Enveloppe `child_process.exec/spawn`

avec allowlist + sandbox + détection de rejeu, plus un CLI d'audit AST (mcp-shellguard-audit) qui analyse les sources des serveurs MCP pour les appels shell non nettoyés. Ferme la classe Ox-Security MCP stdio-RCE (200 000 serveurs vulnérables, divulgation de mai 2026).

  • MCP spec : 2025-06-18
  • SDK : @modelcontextprotocol/sdk ^1.29.0
  • Node : >= 20
  • License : MIT
  • Author : Matthias Meyer (StudioMeyer)

Installer

npm install mcp-stdio-shellguard

Ou exécutez le CLI d'audit directement sans installer :

npx -y -p mcp-stdio-shellguard mcp-shellguard-audit scan ./src

Ce qu'il vous apporte

Trois couches, opt-in par morceaux :

  1. API de bibliothèqueguardExec / guardSpawn prêts à l'emploi que vous appelez depuis votre propre serveur MCP. Allowlist par défaut de refus, profils de sandbox, fenêtre de rejeu.
  2. CLI d'auditmcp-shellguard-audit scan <path> parcourt l'AST, rapporte 12 anti-patrons de LOW (no timeout) à CRITICAL (exec(\...${userInput}...`)`).
  3. Serveur MCP de référencemcp-stdio-shellguard-demo expose 8 outils pour que l'Inspecteur MCP / Claude Desktop puisse piloter le bundle directement.

Outils (serveur de référence)

OutilTypeObjectif
guard_execdestructifExécution protégée de child_process.exec. Force le vecteur args[], allowlist + sandbox + rejeu. Retourne stdout, stderr, exitCode, canonicalHash, isReplay, trustTier.
guard_spawndestructifExécution protégée de child_process.spawn. Retourne les hachages SHA-256 de stdout/stderr au lieu des corps complets. Rejette strictement shell:true.
register_allowlistmutateurEnregistrer un nom d'outil avec exécutable + regex d'args. Sans enregistrement, le refus par défaut s'applique.
audit_sourcelecture seuleAnalyser un chemin TS/JS pour les anti-patrons d'injection shell. Retourne AuditFinding[] + résumé.
audit_reportlecture seuleFormater un résultat d'audit en markdown / json / SARIF 2.1.0.
replay_checklecture seuleCalculer le hachage SHA-256 canonique pour une invocation et signaler s'il est déjà dans la fenêtre de rejeu.
sandbox_statuslecture seuleSignaler le profil de sandbox actif + limites concrètes + indicateur cgroup-v2 actif.
trust_tierlecture seuleDériver le niveau LOW/MEDIUM/HIGH/CRITICAL pour un outil enregistré plus des conseils d'amélioration.

Profils de sandbox

ProfilTimeoutMax stdoutMax stderrBudget FDcgroup-v2
strict5 s1 MB256 KB32oui (cpu/memory)
standard (par défaut)30 s10 MB1 MB256oui
permissif5 min100 MB10 MB1024non

L'appelant peut resserrer via timeoutMs / fdBudget par appel. L'appelant ne peut pas élargir au-delà du profil.

Niveaux de confiance

NiveauCondition
LOWoutil non enregistré (refus par défaut)
MEDIUMenregistré mais argsPatterns vide (tous les args autorisés)
HIGHargsPatterns défini mais sandbox ou suivi de rejeu inactif
CRITICALargsPatterns + sandbox + rejeu tous actifs

Passez de LOW à CRITICAL en enregistrant l'outil + en définissant argsPatterns + en passant par guardExec/guardSpawn (qui activent toujours sandbox + rejeu).

Guide rapide de la bibliothèque

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

CLI d'audit

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

Codes de sortie :

  • 0 propre (aucun résultat au niveau ou au-dessus du seuil)
  • 1 résultats présents
  • 2 erreurs d'analyse / E/S

Bibliothèque d'anti-patrons (12 règles)

IDSévéritéDéclenché sur
exec_template_literal_with_inputCRITICALchild_process.exec(\ls ${x}`)`
exec_dynamic_stringCRITICALchild_process.exec(cmd)
exec_sync_dynamic_stringCRITICALchild_process.execSync(cmd)
eval_near_child_processCRITICALeval(...)
function_constructor_near_child_processCRITICALnew Function(...)
spawn_dynamic_file_argsHIGHspawn(bin, userArgs)
exec_file_dynamicHIGHexecFile(bin, ...)
shell_true_optionHIGH{ shell: true }
os_system_equivalentHIGHDeno.run / Bun.spawn
spawn_literal_dynamic_argsMEDIUMspawn('git', userArgs)
unbounded_bufferLOWexec sans maxBuffer
missing_timeoutLOWexec/spawn sans timeout

Le scanner résout les liaisons child_process renommées avant la correspondance, de sorte que les formes dangereuses ci-dessous sont détectées même lorsque l'appel passe par un alias plutôt que par un child_process.exec littéral :

  • 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(...)

Les variantes synchrones (spawnSync, execFileSync) partagent leurs règles asynchrones, et shell_true_option se déclenche également sur un shell chaîne ({ shell: "/bin/sh" }) ou une valeur de shell dynamique — pas seulement sur le littéral { shell: true }. Un promisify d'une fonction non-child_process, une déstructuration d'un autre module et { shell: false } restent propres (pas de faux positifs).

Pragmas

  • // shellguard:ignore-next-line — supprimer un résultat
  • // shellguard:ignore-file — supprimer tout le fichier (rare ; préférer par ligne)

Pourquoi cela existe

Ox-Security a divulgué (2026-05) que plus de 200 000 serveurs MCP stdio enveloppent child_process.exec avec des littéraux de gabarit transportant des entrées utilisateur directement depuis les arguments d'outils LLM. LiteLLM v1.83.6 était l'exemple canonique (CVE corrigé dans 1.83.7). Ce bundle est le pendant défensif de sécurité : un garde-fou prêt à l'emploi + un scanner qui ferme la classe. Inspiré par AWS Linux seccomp + les niveaux de sandbox Chromium.

Voir aussi

Licence

MIT — Copyright (c) 2026 Matthias Meyer (StudioMeyer)

Catégories