Skip to content

CVE catalog

This page is auto-generated from the regression tests in tests/cves/.

Every CVE listed here has a corresponding test that reproduces the vulnerable tool-call pattern and asserts an agent-airlock primitive blocks it, except a row whose fit is None: triaged in scope and not refused yet. That row's test is a strict xfail, so the build fails the day the refusal starts working, and the row has to change with it.

The suite is a second defence: agent-airlock's job is to catch the same class of bug when a vulnerable server is still running, or when a new tool ships with the same shape. Most of these CVEs have an upstream fix; where none existed when a row was added, its Vulnerability section says so.

See tests/cves/README.md for the classification rules and a list of CVEs we deliberately chose NOT to cover (transport-layer and web-framework bugs that sit outside the airlock execution seam).

To regenerate this page:

python3 scripts/gen_cve_catalog.py --write

CI runs python3 scripts/gen_cve_catalog.py --check on every PR, so the catalog and the tests stay in lockstep.

Summary

CVE Component / title CVSS Airlock fit
CVE-2025-59528 Flowise CustomMCP RCE via JS Function() constructor — —
CVE-2025-59536 Claude Code hooks RCE + MCP consent bypass (exfil leg) 8.7 (High) Partial
CVE-2025-68143 Anthropic mcp-server-git git_init path traversal 8.2 (High) Strong
CVE-2025-68144 Anthropic mcp-server-git argument injection 8.1 (High) Strongest
CVE-2025-68145 mcp-server-git --repository root not enforced 7.1 (High) Strong
CVE-2026-102911 pi-llm-wiki wiki_capture_source splices its url argument into sh -c 9.9 (CRITICAL) — CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:H, CWE-77 + CWE-78 Partial
CVE-2026-104120 modelcontextprotocol mcp-server-fetch fetch_url SSRF 7.3 (HIGH) — CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:L/A:L, CWE-918 Strong
CVE-2026-105697 Langflow MCP stdio transport spawns an unvalidated command 9.9 (CRITICAL) — CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:H, CWE-78 Partial
CVE-2026-105740 Langflow MCP stdio command and env injection 9.9 (CRITICAL) — CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:H, CWE-78 Partial
CVE-2026-105788 Microsoft UFO mobile MCP type_text and launch_app injection 8.8 (HIGH) — CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H, CWE-78, CWE-88 Partial
CVE-2026-105793 Microsoft UFO mobile MCP press_key argument injection 9.1 (CRITICAL) — CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:L/I:H/A:L, CWE-78 Partial
CVE-2026-105797 SimpleChat personal MCP plugin spawns an attacker-selected stdio process 8.8 (HIGH) — CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H, CWE-78, CWE-863 Partial
CVE-2026-108953 mac-shell-mcp bypasses its command whitelist via shell metachars in args 7.8 (HIGH) — CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H Partial
CVE-2026-111451 Wheels CLI MCP tool arguments reach deploy shells, file paths and generated CFML 7.8 (HIGH) — CVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H Partial
CVE-2026-11393 AgentCore CLI triple-quote codegen RCE — —
CVE-2026-11624 MCP HTTP-transport Origin/Host DNS-rebinding 9.4 —
CVE-2026-117558 ai-sdlc plugin MCP server: repo interpolated into gh issue view and gh pr view 7.8 (HIGH) — CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H, CWE-78 Strong
CVE-2026-117559 DroidMind: shell command allowlist bypassed with $(...) or backtick substitution 7.8 (HIGH) — CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H, CWE-78 Strong
CVE-2026-117560 ATXP Gemini CLI extension: $() or backticks in message_id, subject or query reach runAtxp() 7.8 (HIGH) — CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H, CWE-78 Strong
CVE-2026-117561 Codefriar sf-mcp: Salesforce CLI flag values reach execSync past escapeValue() 7.8 (HIGH) — CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H, CWE-78 Strong
CVE-2026-117567 esp-mcp: target, port, project_path and project_name reach asyncio.create_subprocess_shell 7.8 (HIGH) — CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H, CWE-78 Strong
CVE-2026-117568 mcp-ffmpeg: videoPath and outputDir break out of the child_process.exec string 7.0 (HIGH) — CVSS:3.1/AV:L/AC:H/PR:L/UI:N/S:U/C:H/I:H/A:H, CWE-78 Partial
CVE-2026-117569 Kali-GPT: scanner parameters of execute_tool() run through subprocess.run with shell=True 7.8 (HIGH) — CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H, CWE-78 Strong
CVE-2026-117573 hagent: args of hagent_arduino and hagent_esp32 interpolated into arduino-cli and idf.py under shell=True 7.8 (HIGH) — CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H, CWE-78 Strong
CVE-2026-118378 Tabularis SQLite driver: stacked statements through describe_table's table_name 7.8 (HIGH) — CVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H, CWE-89 Partial
CVE-2026-119025 open on Windows and WSL: a discovery-metadata authorization_endpoint with $(...) run through PowerShell 7.5 (HIGH) — CVSS:3.1/AV:N/AC:H/PR:N/UI:R/S:U/C:H/I:H/A:H, CWE-78 Partial
CVE-2026-119050 Agno PandasTools: a model-chosen function name dispatched through getattr(pandas, ...) 8.1 (HIGH) — CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:H, CWE-470 (CVSS 4.0: 9.2 CRITICAL) Partial
CVE-2026-123271 ChatCLI MCP OAuth: a server-supplied resource used as the resource indicator 8.0 (HIGH) — AV:N/AC:H/PR:N/UI:R/S:C/C:H/I:H/A:N, CWE-346 Partial
CVE-2026-123272 mcp-go OAuth: protected resource metadata names another server's resource 8.0 (HIGH) — AV:N/AC:H/PR:N/UI:R/S:C/C:H/I:H/A:N, CWE-346 Partial
CVE-2026-123783 Skyvern script sandbox: functions rebuilt from raw bytecode with types.CodeType 8.8 (HIGH) — AV:N/AC:L/PR:L/UI:N, CWE-184 Partial
CVE-2026-19591 Codex command-safety parser disagreed with PowerShell about --% 8.8 (HIGH) — CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H, CWE-150 Strong
CVE-2026-19753 mcp-rdf-explorer explore_url takes an attacker-supplied URL into SSRF 7.3 (HIGH) — CWE-918 Strong
CVE-2026-21520 Capsule ShareLeak / PipeLeak 7.5 (HIGH) —
CVE-2026-23744 MCPJam Inspector unauthenticated public bind 9.8 —
CVE-2026-25874 HuggingFace LeRobot pickle-deserialization RCE 9.3 —
CVE-2026-26118 Microsoft Azure MCP Server SSRF (IMDS token theft) 8.8 (High) Strong
CVE-2026-27825 mcp-atlassian arbitrary file write via download_path 9.1 (Critical) Strong
CVE-2026-27826 mcp-atlassian SSRF via X-Atlassian-*-Url headers 7.5 (High, AV:A/PR:N/UI:N, C:H) Partial
CVE-2026-30615 (Windsurf zero-click MCP config) — spawn-time config pin — —
CVE-2026-30616 MCP STDIO transport command-injection (Ox Security class) 9.8 (Critical) Strongest
CVE-2026-32211 Azure MCP Server SSE token-echo 8.6 (High) —
CVE-2026-32625 (LibreChat MCP server-URL env-interpolation secret leak) 9.6 —
CVE-2026-33032 "MCPwn" — nginx-ui missing /mcp_message auth middleware 9.8 —
CVE-2026-39884 flux159/mcp-server-kubernetes argv injection — —
CVE-2026-40933 Flowise MCP-stdio adapter RCE regression 9.9 —
CVE-2026-41349 OpenClaw agentic consent-bypass 8.8 —
CVE-2026-41361 OpenClaw IPv6 SSRF guard bypass 7.1 —
CVE-2026-42271 CISA KEV regression fixture (LiteLLM MCP command injection) 3.1 —
CVE-2026-44211 Cline Kanban cross-origin WebSocket hijack 9.7 —
CVE-2026-47390 SSRF-protection bypass via alternate IP encodings — —
CVE-2026-48782 SafeURL IPv6-transition cloud-metadata SSRF bypass — —
CVE-2026-5023 codebase-mcp RepoMix OS command injection — —
CVE-2026-53710 ContextForge python_sandbox_server: RestrictedPython escape via raw getattr 10.0 (CRITICAL) — CWE-94 + CWE-693 Partial
CVE-2026-53820 OpenClaw exec-denylist bypass at MCP loopback spawn 6.9 —
CVE-2026-57124 PraisonAI UI /api/mcp/connect spawns caller-chosen local commands 9.8 (CRITICAL) — CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H, CWE-306 + CWE-78 Partial
CVE-2026-57496 NetLicensing MCP splices product_number into a REST path, no segment check 9.6 (CRITICAL) — CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:N Partial
CVE-2026-59176 functype-mcp-server: set_functype_version's version installs any package as functype through pnpm add 7.8 (HIGH) — CVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H, CWE-829 Strong
CVE-2026-6980 GitPilot-MCP repo_path injection — —
CVE-2026-75062 Google langfun lf.query evaluates model-generated Python unsandboxed 9.2 (CRITICAL) — CWE-95, CWE-1188 Partial
CVE-2026-75130 Upstash Context7 "ContextCrush" MCP instruction injection 9.0 (Critical, CVSS v3.1; NVD also records 6.4 Medium under v4.0) Strongest
CVE-2026-77521 MaxKB SandboxShellBackend exposes an unapproved execute shell tool 10.0 (CRITICAL) — CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:H Partial
CVE-2026-78575 IBM Langflow MCP stdio server config takes unvalidated command-line arguments 8.8 (HIGH) — CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H, CWE-78 Partial
CVE-2026-79538 MetaMCP inspector proxy spawns the stdio command its query string names 9.8 (CRITICAL) — CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H, CWE-94 Partial
CVE-2026-79748 MCPHub server-config endpoints spawn attacker-supplied stdio commands 9.9 (CRITICAL) — CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:H, CWE-862 Partial
CVE-2026-90898 Bifrost MCP client registration spawns an unauthenticated stdio command 9.8 (CRITICAL) — CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H, CWE-284 + CWE-306 Partial

Covered inside umbrella modules

These CVEs have no row above: a module that replays several at once names them in its docstring rather than in a header of its own, so there is no per-CVE CVSS or fit to show. Each one's pattern is replayed by the module listed.

CVE NVD Regression test
CVE-2026-26015 NVD tests/cves/test_ox_supply_chain_2026_04.py
CVE-2026-30617 NVD tests/cves/test_ox_supply_chain_2026_04.py
CVE-2026-30618 NVD tests/cves/test_ox_supply_chain_2026_04.py
CVE-2026-30623 NVD tests/cves/test_ox_supply_chain_2026_04.py
CVE-2026-30624 NVD tests/cves/test_ox_supply_chain_2026_04.py
CVE-2026-30625 NVD tests/cves/test_ox_supply_chain_2026_04.py
CVE-2026-33224 NVD tests/cves/test_ox_supply_chain_2026_04.py
CVE-2026-44717 NVD tests/cves/test_metis_inspired_corpus_2026_05_18.py

Details

CVE-2025-59528

Flowise CustomMCP RCE via JS Function() constructor

Vulnerability

Flowise's /api/v1/node-load-method/customMCP passed user-supplied strings directly into JavaScript Function() and eval. CVSS 10.0. Patched in v3.0.6 (Sept 2025) but CSA documented active exploitation in April 2026 — ~12-15K instances still exposed. This regression codifies the offending token set (Function(, new Function, eval(, Deno.eval, vm.runInNewContext) so

CVE-2025-59536

Claude Code hooks RCE + MCP consent bypass (exfil leg)

Vulnerability

Claude Code (< 1.0.111) executes repository-controlled configuration — project hooks, registered MCP servers, and environment variables including ANTHROPIC_BASE_URL — BEFORE showing the user the trust dialog. Opening a malicious repository is therefore enough to (1) run arbitrary shell commands via hooks, and (2) redirect the agent's base URL to an attacker-controlled host that exfiltrates the API key on the first request.

Airlock mitigation

The hook-execution leg runs on the Claude Code client before any tool call exists, so runtime middleware has no seam. That half is out-of-scope for agent-airlock and is fixed by upgrading Claude Code.

The exfiltration leg — sending the API key to an attacker-controlled ANTHROPIC_BASE_URL — IS blockable. EndpointPolicy rejects any hostname not in the caller's allow-list, and SafeURL applies the same guard at the tool-signature level. If the agent's outbound requests are routed through an airlock-wrapped HTTP tool, the attempt to post to https://evil.example.com/... never leaves the process.

CVE-2025-68143

Anthropic mcp-server-git git_init path traversal

Vulnerability

The git_init tool of anthropics/mcp-server-git (< 2025.9.25 / 2025.12.18) accepts an arbitrary filesystem path as its repo_path argument without validating it against the configured repository root. An attacker who can prompt-inject the agent can therefore initialise a .git directory anywhere the server process can write, and — chained with a filesystem MCP — drop a malicious .git/config that achieves RCE on the next git invocation.

Airlock mitigation

This is the canonical SafePath / FilesystemPolicy defense. The fix upstream and the fix here are the same: reject any repo_path that escapes the configured repo root via os.path.commonpath().

We assert both: 1. SafePathValidator with the bare defaults rejects traversal strings (the pre-normalisation defense). 2. FilesystemPolicy.validate_path rejects paths outside the allowed root even when the path is syntactically clean (the post-resolution defense).

CVE-2025-68144

Anthropic mcp-server-git argument injection

Vulnerability

git_diff / git_checkout in anthropics/mcp-server-git (< 2025.12.18) pass user-controlled refs directly to the git CLI. A ref value starting with a hyphen (for example --output=/etc/profile.d/rce.sh) is interpreted by git as an OPTION rather than a ref, allowing arbitrary file overwrite through the resulting git subprocess call.

Airlock mitigation

The ghost/strict argument validator is exactly the primitive for "LLM passes a string that looks like a flag into a typed parameter." A Pydantic-strict model with a custom validator that rejects any ref beginning with - is a one-liner at the tool-decoration layer.

CVE-2025-68145

mcp-server-git --repository root not enforced

Vulnerability

When anthropics/mcp-server-git (< 2025.12.18) is started with the --repository flag to declare an allowed repo root, the server fails to verify on each subsequent tool call that the repo_path argument stays inside that root. A crafted argument such as /var/lib/otheruser/.git lets the server operate on any repo the process user can read.

Airlock mitigation

FilesystemPolicy with an allowed_roots list is the canonical mitigation. validate_path uses os.path.commonpath() (not string prefix) so it catches the three common escape variants:

  • absolute path outside the root
  • relative path with .. that would normalise outside the root
  • a symlink that points outside the root

CVE-2026-102911

pi-llm-wiki wiki_capture_source splices its url argument into sh -c

Vulnerability

pi-llm-wiki up to 0.11.7 exposes an MCP tool, wiki_capture_source, whose url argument reaches extractWithMarkItDown in extensions/llm-wiki/lib/source-extractors.ts. That function ran sh with these arguments:

["-c", `uvx --from 'markitdown[docx,pdf]' markitdown "${source}" 2>/dev/null || echo ""`]

The value sits inside double quotes, so $(...) and backticks run where they stand, and a " closes the quoting so that ;, | or && start a command of the caller's choosing. The published proof of concept sends url as ";open -a Calculator;#. Fixed in 0.11.8 (PR #186), which passes source as its own argv element with no shell.

Airlock mitigation

A url typed SafeURL refuses every payload here that runs a command through the double-quoted splice. Upstream's own payloads are not https URLs, so the scheme check refuses them. Put inside one, each still carries a " that ends the quoting, a backtick, a | or $(, and since 0.10.22 SafeURL refuses a URL carrying a character RFC 3986 requires to be percent-encoded, and $(. Until then it checked only where a URL points, and these four cases were held here as strict xfail (issue #256).

What it cannot refuse: $NAME inside the double quotes still expands, so a URL can carry an environment variable into the fetch. $name is ordinary URL syntax (OData's $select), and refusing it would break real URLs. The complete fix is the upstream one: pass the URL as its own argv element, with no shell.

CVE-2026-104120

modelcontextprotocol mcp-server-fetch fetch_url SSRF

Vulnerability

modelcontextprotocol mcp-server-fetch and mcp-server-everything up to 2026.6.4 expose a fetch_url tool (mcp_server_fetch/server.py) that takes a caller-supplied url / path and issues the request without restricting the destination. Manipulating that argument to point at a cloud metadata endpoint, loopback, or an RFC1918 host reaches internal resources — server-side request forgery, remotely, with a publicly disclosed exploit. No fixed release existed when this was catalogued: the fix pull request (modelcontextprotocol/servers#4890) awaited acceptance as of 2026-10-09, so for this CVE the second defence is the only one there is.

Airlock mitigation

SSRF via a url argument is the documented in-scope shape (docs/cve-triage.md, "SSRF via a URL argument", anchored on CVE-2026-26118). The fetch tool's whole input is the destination, so the contract is a destination filter: :class:~agent_airlock.ssrf_egress_guard.SSRFEgressGuard refuses the dangerous ones — cloud metadata, loopback, link-local, RFC1918, and a public name that resolves or rebinds to any of them — on the resolved IP, while allowing a genuine public fetch, which is the tool's legitimate purpose.

An existing guard already refuses the shape, so this is a second-defence regression fixture against that guard, not a new guard — the CVE-2026-47390 pattern, one layer up on the tool's url argument. The preset's cves tuple is deliberately not extended to name this CVE: it claims the CVE it was built for (CVE-2026-47390) and refuses this one's payload without claiming it.

CVE-2026-105697

Langflow MCP stdio transport spawns an unvalidated command

Vulnerability

Before Langflow 1.10.3, the MCP stdio transport launched whatever command / args a user put in an MCP server configuration, with no allowlist and wrapped in bash -c "exec {command} ...". Any user able to reach the MCP server settings (POST/PATCH /api/v2/mcp/servers/{name}) or to build a flow with the MCP Tools component could add a "server" whose command is an arbitrary OS command; it runs on the Langflow host as the Langflow process user the moment Langflow tries to connect — listing servers, loading tools or running the flow — even when the UI then reports that the stdio server failed to start. With the default LANGFLOW_AUTO_LOGIN=true this is reachable without an account. Fixed in Langflow 1.10.3 (langflow-base 0.10.3, lfx 1.10.3).

Airlock mitigation

This is the "both" case docs/cve-triage.md describes, and it splits the same way as CVE-2026-78575 (the earlier IBM Langflow stdio bulletin) and CVE-2026-79748 (MCPHub).

Out of scope: the settings endpoint. That any user — or, with AUTO_LOGIN, an anonymous one — may POST an MCP server at all is Langflow's own authorization. It is a missing check on an HTTP route that never calls into a decorated tool, and a contract layer for tool-call arguments cannot add auth to someone else's endpoint. Langflow 1.10.3 is where that belongs.

In scope: the spawn primitive. The thing the missing check hands over is a caller-supplied stdio command / args heading for a shell spawn — the documented in-scope shape (docs/cve-triage.md, "Command / argument injection into a spawn", anchored on CVE-2026-42271), which McpSubprocessArgInjectionGuard already refuses deny-by-default. So this is a second-defence regression fixture against an existing guard, not a new guard — the CVE-2026-90898 / CVE-2026-57124 / CVE-2026-77521 pattern. The env-injection half disclosed as the sibling CVE-2026-105740 is covered in test_cve_2026_105740_langflow_stdio_env.py.

CVE-2026-105740

Langflow MCP stdio command and env injection

Vulnerability

Prior to Langflow 1.9.0, any authenticated Langflow user can achieve Remote Code Execution by adding an MCP server with the "Stdio" transport. The user-supplied command field is passed directly to bash -c "exec {command}" with zero validation, no allowlisting and no sandboxing, and executes immediately when the server list is fetched. Additionally, the env field allows arbitrary environment variable injection (e.g. LD_PRELOAD, PATH override), which turns even an otherwise-benign launcher into an execution primitive. Fixed in Langflow 1.9.0.

Airlock mitigation

The sibling of CVE-2026-105697, on the same stdio transport. That one is covered in test_cve_2026_105697_langflow_stdio_spawn.py; this file exists for the half CVE-2026-105740 adds, the env field.

Out of scope: the authenticated-add primitive. That any authenticated user may add a stdio server is Langflow's own authorization, the same route check CVE-2026-105697 turns on, and not expressible at the tool-call boundary. Langflow 1.9.0 is where it belongs.

In scope: the env-injection primitive. McpSubprocessArgInjectionGuard refuses an env mapping carrying a known code-loading variable (LD_PRELOAD / PATH / PYTHONPATH / ...) regardless of whether the command was allowlisted, because env turns any binary into an execution primitive. This is a second-defence fixture against the existing guard, not a new guard — the guard already carries LD_PRELOAD and PATH in its default dangerous-env set, so no env handling needed changing for this CVE.

The command half (bash -c "exec {command}") is the same shape the CVE-2026-105697 module pins; here it is asserted only enough to show it is the env leg, not the command leg, doing the refusing once a launcher is allowlisted.

CVE-2026-105788

Microsoft UFO mobile MCP type_text and launch_app injection

Vulnerability

Prior to UFO 3.0.10, the type_text and launch_app tools in ufo/client/mcp/http_servers/mobile_mcp_server.py pass the authenticated caller-controlled text and package_name parameters into adb shell command argument positions without comprehensive validation. The adb client joins those arguments into a remote command string that the Android shell reparses, letting shell metacharacters run additional commands as the Android shell user on an authorized connected device. Exploitation needs a valid Mobile MCP API key and a reachable authorized device; it does not reach the host OS, Android root, or beyond the shell user. Fixed in 3.0.10 (by quoting the adb arguments in the callee).

Airlock mitigation

Argument-shaped and authenticated, with no authorization half. The two parameters split on their type, and the sibling CVE-2026-105793 (press_key) is covered in test_cve_2026_105793_ufo_press_key.py.

package_name has a narrow shape — a dotted Android package name. A tool that declares it gets the separator refused by agent-airlock's strict validation, no new guard, the CVE-2025-68144 primitive. This fixture pins it.

text is free text by design. No contract on the caller's side can make free text safe for a shell that reparses it: the field's whole purpose is to carry arbitrary characters, so a str accepts them, separator and all. That is not a gap in validation, it is the honest boundary of an argument contract, and the fix is the callee's quoting (UFO 3.0.10). The test below documents that boundary rather than asserting a refusal that would be wrong to expect.

So the fit is partial: strict typing reaches package_name, not text.

CVE-2026-105793

Microsoft UFO mobile MCP press_key argument injection

Vulnerability

Prior to UFO 3.0.9, the press_key tool in ufo/client/mcp/http_servers/mobile_mcp_server.py accepts a free-form key_code parameter and passes it to adb shell input keyevent. The adb client joins the arguments into a remote command string that the Android shell reparses, letting an authenticated Mobile MCP caller run additional commands as the Android shell user on an authorized connected device. Exploitation needs a valid UFO_MCP_API_KEY, adb on the host, and a reachable authorized device; it does not reach the host OS, Android root, or beyond the shell user. Fixed in 3.0.9 (by quoting the adb arguments in the callee).

Airlock mitigation

Argument-shaped and authenticated, with no authorization half: the key_code value is the whole defect. What decides the outcome is the argument's type.

Out of scope: the reparsing shell. That adb shell rejoins its arguments and the Android shell reparses them is a property of the callee, and the fix is to quote there, which UFO shipped in 3.0.9. A contract layer on the caller's side cannot change how a remote shell splits a string it is handed.

In scope: the argument's shape. key_code has a narrow legal form, an Android keycode name or number. A tool that declares that shape gets the separator rejected by agent-airlock's strict validation with no new guard and no preset, which is the CVE-2025-68144 (git flag-shaped ref) primitive. This fixture pins that: a decorated press_key whose key_code is int | KEYCODE_... accepts a real keycode and refuses one carrying a shell separator. It is a second defence contingent on the schema the vulnerable tool did not declare, which is why the fit is partial rather than strong.

CVE-2026-105797

SimpleChat personal MCP plugin spawns an attacker-selected stdio process

Vulnerability

In SimpleChat 0.261.003 and 0.261.027, an authorization ordering flaw in POST /api/user/plugins lets an authenticated low-privileged user omit the top-level MCP type so that _reject_non_admin_mcp_stdio skips inspection before the type is restored from metadata. The stored personal action then reaches McpPluginFactory.create_connector, and MCPStdioPlugin.connect starts the attacker-selected operating-system process under the application service identity when the action tool is invoked. Exploitation requires personal plugins enabled and governance permitting MCP actions. Fixed in 0.261.031 (which restores the type before the admin check runs).

Airlock mitigation

The "both" case from docs/cve-triage.md, splitting the same way as CVE-2026-79748 (MCPHub).

Out of scope: the authorization ordering (CWE-863). That _reject_non_admin_mcp_stdio runs before the type is restored, so omitting the top-level type skips the admin gate, is "incorrect authorization logic inside a router or hub" (docs/cve-triage.md out-of-scope table): the bug is which principal may store the action, not the call's arguments. Only SimpleChat's own route can order that check correctly, and 0.261.031 does.

In scope: the spawn primitive the ordering flaw hands over. The stored action carries a stdio command / args naming a caller-chosen process that MCPStdioPlugin.connect starts. That is the shape :class:~agent_airlock.mcp_spec.subprocess_arg_guard.McpSubprocessArgInjectionGuard already refuses deny-by-default for CVE-2026-42271, so this is a second-defence regression fixture against an existing guard, not a new guard. Crucially, the guard resolves the program from command / args and never consults a top-level type discriminator, so the exact move this CVE uses — omit the type to skip inspection — does not skip the guard.

CVE-2026-108953

mac-shell-mcp bypasses its command whitelist via shell metachars in args

Vulnerability

mac-shell-mcp before 2.0.0 exposes an execute_command MCP tool guarded by a command whitelist. The whitelist vets the command but not its arguments, so a client calls an allowed command such as pwd and smuggles a forbidden command in the arguments behind a shell metacharacter — pwd ; …, pwd && …, a pipe, $( ) or a backtick — which the server then runs through a shell as the server user, without approval. NVD, verbatim: "mac-shell-mcp before 2.0.0 contains an OS command injection vulnerability that allows MCP clients to bypass the command whitelist by injecting shell metacharacters into execute_command arguments. Attackers or malicious prompts can call a safe command such as pwd with arguments like ';' to run forbidden commands as the server user without approval." Fixed in 2.0.0, whose commit stops building a shell string from the arguments.

Airlock mitigation

Out of scope: the whitelist and the approval gate. Whether execute_command is offered, what its command whitelist contains, and whether a human approves a run are decisions in mac-shell-mcp's own server, downstream of the tool call. A contract layer cannot impose them on someone else's server, and 2.0.0 is the right place for the fix.

In scope: the primitive. Shell metacharacters riding in a caller-controlled argument is the documented in-scope shape (docs/cve-triage.md, "Metacharacter / escape neutralisation mismatch", anchored on CVE-2026-19591), and StdioCommandInjectionGuard already refuses it. So this is a second-defence regression fixture against an existing guard, not a new guard — the CVE-2026-77521 pattern. The guard runs on the argument, before the server ever builds a shell string, so it still fires when the whitelist is the only thing the server relied on.

One honest boundary: a bare $VAR (no $() is not in the default metachar set and passes, because $ appears in ordinary arguments and a blanket refusal would break them — the same line SafeURL draws for $name in CVE-2026-102911. Once the server still splices the value into a shell, $VAR would expand; the complete fix is the upstream one — 2.0.0 passes arguments as argv with no shell.

CVE-2026-111451

Wheels CLI MCP tool arguments reach deploy shells, file paths and generated CFML

Vulnerability

Wheels CLI 4.0.0 through 4.1.1 uses caller-supplied values without validating or escaping them. The advisory lists eight defects. Three of them reach the CLI as the arguments of an MCP tool call, the seam this library sits on:

  • wheels deploy interpolates its release, version or rollback value into remote shell commands, container names and image tags without escaping, so a value that carries a shell metacharacter runs commands on every deploy host as the SSH user. Through 4.1.0 deploy was listed as an MCP tool; 4.1.1 hid it. Its advertised input schema was empty, but LuCLI's tools/call forwards every argument key to the module as key=value regardless, and Wheels 4.1.0 reads release=<value> exactly as it reads --release=<value>, the version its deploy parser uses.
  • packages remove and generate take a name with no validation, so .. or a ../ prefix deletes the project's contents or writes .cfm files outside it.
  • Model, property, action and app names are interpolated into generated CFML, so a # expression or a quote in a name becomes executable code.

Fixed in 4.1.2, which validates names and versions strictly and shell-escapes every interpolated value.

Airlock mitigation

In scope: the three argument shapes. Each is refused by a primitive that already exists, so this is a second-defence fixture, not a new guard or preset:

  • The deploy value, in the argv LuCLI builds from the call, is refused by StdioCommandInjectionGuard when it carries a shell metacharacter: the CVE-2026-108953 pattern. _lucli_argv repeats LuCLI's lines, as _spawn_config does for CVE-2026-79538.
  • A traversal name is refused by SafePathValidator: the CVE-2026-57496 pattern.
  • A name carrying a CFML # or a quote is refused by strict validation when the tool declares the identifier shape a Wheels name has: the CVE-2026-105793 pattern. No guard reads CFML, so the declared shape is the whole defence for that half.

Out of reach. A deploy value that comes from a CI variable or a branch name never crosses a tool call. Package archive contents, destroy following a symlink on disk, unencoded output in generated views and a stale RustCFML process id are not arguments either. Two boundaries are pinned below: a bare $VAR passes the stdio guard, as in CVE-2026-108953, and a name declared as a bare str, the shape the Wheels MCP schema advertised, admits the CFML delimiter. The complete fix is the upstream validation and escaping in 4.1.2.

CVE-2026-11393

AgentCore CLI triple-quote codegen RCE

Vulnerability

AWS AgentCore CLI < 0.14.2 (CVSS 9, CWE-94, published 2026-06-08) generates Python source by interpolating a model-/user-controlled collaborationInstruction into a code string without neutralising triple-quote characters. A crafted instruction containing """ closes the generated literal and injects statements that execute when another account user imports the agent — RCE on the AgentCore Runtime

CVE-2026-11624

MCP HTTP-transport Origin/Host DNS-rebinding

Vulnerability

Google MCP Toolbox for Databases < 0.25.0 (CWE-346 Origin Validation Error, CVSS 9.4): the MCP server exposed an HTTP/SSE transport that did not validate the Origin (or Host) header, so a browser the developer visits can DNS-rebind to 127.0.0.1 and script MCP tool calls at the local server (file reads, command execution, database access). Fixed in 0.25.0 with an --allowed-hosts flag alongside --allowed-origins, warning when

CVE-2026-117558

ai-sdlc plugin MCP server: repo interpolated into gh issue view and gh pr view

Vulnerability

"The ai-sdlc plugin MCP server through 0.31.2 contains an OS command injection vulnerability that allows attackers to execute commands by supplying shell metacharacters in the repo argument. The unvalidated repo value is interpolated into gh issue view or gh pr view commands run via execSync(), letting prompt-injected agents execute commands as the MCP server user." NVD names no fixed release (its range is "through 0.31.2"), and this module cites no upstream fix.

Airlock mitigation

In scope: shell syntax in a string argument a server splices into a command line, the "Command / argument injection into a spawn" shape in docs/cve-triage.md. ToolArgSinkGuard(shell_args={"repo"}) reads repo at step 2.75 of @Airlock and refuses every token of DEFAULT_SHELL_METACHARS before the tool runs, and so does tool_arg_sink_defaults(mode="deny"), which claims this CVE. Out of scope: the execSync string ai-sdlc builds, which is upstream's to fix: the guard reads the argument, not the command line. The guard is off until a policy sets it, and the preset starts in warn mode, which logs the finding and runs the call.

An existing guard (0.10.28) already refuses the shape, so this is a second-defence fixture, not a new guard. TestDocumentedLimit pins what the guard's own docstring says passes: a value starting with - (option injection) and a bare $NAME. Triage: #384.

CVE-2026-117559

DroidMind: shell command allowlist bypassed with $(...) or backtick substitution

Vulnerability

"DroidMind through 0.4.0 contains a command injection vulnerability that allows MCP clients to bypass the shell command allowlist by using $(...) or backtick substitution. Attackers, including prompt-injected agents, can inject commands through parameters interpolated into Device.run_shell() or unvalidated Device.list_directory() to run blocked commands such as reboot on connected Android devices." NVD names no fixed release (its range is "through 0.4.0"), and this module cites no upstream fix.

Airlock mitigation

In scope: substitution syntax in a string argument that reaches a device shell, the "Command / argument injection into a spawn" shape in docs/cve-triage.md. NVD names no argument; android-shell's command and list_directory's path are the server's own (docs/guide/shell-and-installer-arguments.md). ToolArgSinkGuard(shell_args={"command", "path"}) refuses $(, a backtick and every other DEFAULT_SHELL_METACHARS token at step 2.75, before the tool runs, and so does tool_arg_sink_defaults(mode="deny"), which claims this CVE. Out of scope: which commands DroidMind's allowlist admits. A blocked command sent on its own, reboot, carries no shell syntax and passes this guard; refusing it is the allowlist's job (or a ShellCommandPolicy rule's), and the substitution that smuggled it past the allowlist is what this guard refuses.

An existing guard (0.10.28) already refuses the shape, so this is a second-defence fixture, not a new guard. Triage: #385.

CVE-2026-117560

ATXP Gemini CLI extension: $() or backticks in message_id, subject or query reach runAtxp()

Vulnerability

"ATXP Gemini CLI extension through 1.0.0 contains an OS command injection vulnerability in runAtxp() that allows attackers to execute shell commands through unsanitized MCP tool parameters. Attackers can plant instructions in email or SMS content to inject $() or backtick substitutions into parameters like message_id, subject, or query, running commands as the user." NVD names no fixed release (its range is "through 1.0.0"), and this module cites no upstream fix.

Airlock mitigation

In scope: substitution syntax in the string arguments NVD names, which reach a shell through runAtxp(): the "Command / argument injection into a spawn" shape in docs/cve-triage.md. ToolArgSinkGuard(shell_args={"message_id", "subject", "query"}) refuses $(, a backtick and the other DEFAULT_SHELL_METACHARS tokens at step 2.75, before the tool runs, and so does tool_arg_sink_defaults(mode="deny"), which claims this CVE. NVD names the parameters, not the tools, so atxp_tool here is a placeholder. Out of scope: the email or SMS content that steers the agent, which no tool argument carries until the agent writes it into one, and runAtxp()'s command string, which is upstream's to fix.

subject and query look like free text, and with screen_all_str_args an operator opts free text out by name. TestDocumentedLimit pins that an opted-out subject admits the payload: on this server it must be screened, not exempted. An existing guard (0.10.28) already refuses the shape, so this is a second-defence fixture, not a new guard. Triage: #386.

CVE-2026-117561

Codefriar sf-mcp: Salesforce CLI flag values reach execSync past escapeValue()

Vulnerability

"Codefriar sf-mcp through 1.3.2 contains an OS command injection vulnerability that allows attackers to execute shell commands by supplying crafted Salesforce CLI tool flag values. Because escapeValue() in src/utils.ts fails to neutralize backticks, $() substitution, and other shell metacharacters, these values reach execSync in executeSfCommand and run as the MCP server user." NVD names no fixed release (its range is "through 1.3.2"), and this module cites no upstream fix.

Airlock mitigation

In scope: shell syntax in flag values a server splices into a command line, the "Command / argument injection into a spawn" shape in docs/cve-triage.md. sf-mcp's tools take the CLI's own flag names (target-org, query, ...) and build --<flag>=<value>, so the set of arguments is open-ended: ToolArgSinkGuard(screen_all_str_args=True) screens every value, and every **kwargs name too, for DEFAULT_SHELL_METACHARS at step 2.75, before the tool runs. tool_arg_sink_defaults(mode="deny") is that configuration and claims this CVE. Out of scope: escapeValue() and the execSync string, which are upstream's to fix. sf_data_query is a placeholder tool.

The default set leaves out quotes and $NAME expansion (the guard's Honest scope), so a double-quoted SOQL literal and $SF_ORG pass; TestDocumentedLimit pins both. An existing guard (0.10.28) already refuses the shape, so this is a second-defence fixture, not a new guard. Triage: #387.

CVE-2026-117567

esp-mcp: target, port, project_path and project_name reach asyncio.create_subprocess_shell

Vulnerability

"esp-mcp through commit 28afddf contains an OS command injection vulnerability that allows attackers to run arbitrary commands by supplying shell metacharacters in MCP tool parameters. Attackers influencing agent tool calls through prompt injection can inject metacharacters into target, port, project_path, or project_name values passed to asyncio.create_subprocess_shell, executing commands as the MCP server user." NVD names no fixed release (its range is "through commit 28afddf"), and this module cites no upstream fix.

Airlock mitigation

In scope: shell syntax in the four string arguments NVD names, which reach a shell through asyncio.create_subprocess_shell: the "Command / argument injection into a spawn" shape in docs/cve-triage.md. ToolArgSinkGuard(shell_args={"target", "port", "project_path", "project_name"}) refuses every DEFAULT_SHELL_METACHARS token at step 2.75, before the tool runs, and so does tool_arg_sink_defaults(mode="deny"), which claims this CVE. NVD names the parameters, not the tools, so build_project is a placeholder. Out of scope: the shell command line esp-mcp builds, which is upstream's to fix: a call through create_subprocess_exec with an argv list would not be exposed to these characters at all.

TestDocumentedLimit pins what the guard's Honest scope says passes: a value starting with - (option injection) and a bare $NAME. An existing guard (0.10.28) already refuses the shape, so this is a second-defence fixture, not a new guard. Triage: #388.

CVE-2026-117568

mcp-ffmpeg: videoPath and outputDir break out of the child_process.exec string

Vulnerability

"mcp-ffmpeg through 1.0.5 contains an OS command injection vulnerability that allows attackers to run shell commands by supplying crafted videoPath or outputDir values to the resize-video, extract-audio and get-video-info tools. Attackers can embed $(), backticks or a closing double quote in an existing file or directory name to break out of the child_process.exec command string and execute commands as the MCP server user." NVD names no fixed release (its range is "through 1.0.5"), and this module cites no upstream fix. No triage issue exists for this record, although the watcher reads it as a candidate (pinned in this module).

Airlock mitigation

In scope: shell syntax in the two path arguments NVD names, the "Command / argument injection into a spawn" shape in docs/cve-triage.md. ToolArgSinkGuard(shell_args={"videoPath", "outputDir"}) refuses $( and a backtick at step 2.75, before any of the three tools runs, and so does tool_arg_sink_defaults(mode="deny"), which claims this CVE.

Why partial: the third form NVD names, a closing double quote, is not in DEFAULT_SHELL_METACHARS. The guard's Honest scope says a quote that only closes the server's own quoting injects arguments (here -y and a second output path) rather than commands, and leaves quotes to the operator: extra_metachars={'"'} refuses it, and TestTheClosingQuote pins both halves. The preset takes no extra_metachars, so in deny mode it still admits the quote. Out of scope: the child_process.exec string, which is upstream's to fix.

An existing guard (0.10.28) already refuses the substitution forms, so this is a second-defence fixture, not a new guard.

CVE-2026-117569

Kali-GPT: scanner parameters of execute_tool() run through subprocess.run with shell=True

Vulnerability

"Kali-GPT through commit a608c74 contains an OS command injection vulnerability in execute_tool() in kali_gpt/tools/mcp_server.py that allows MCP clients to run commands via unsanitized scanner parameters. Attackers or prompt-injected agents can embed shell metacharacters in parameters like target, ports, domain, wordlist, or url, executed via subprocess.run with shell=True as the MCP server user." NVD names no fixed release (its range is "through commit a608c74"), and this module cites no upstream fix.

Airlock mitigation

In scope: shell syntax in the five scanner parameters NVD names, the "Command / argument injection into a spawn" shape in docs/cve-triage.md. ToolArgSinkGuard(shell_args={"target", "ports", "domain", "wordlist", "url"}) refuses every DEFAULT_SHELL_METACHARS token at step 2.75, before execute_tool runs, and so does tool_arg_sink_defaults(mode="deny"), which claims this CVE. Out of scope: the shell=True command line Kali-GPT builds, which is upstream's to fix, and which scanner a call may select.

Under shell=True an unquoted & is a separator, so a well-formed URL with a query string is refused too (TestTheUrlParameter): the cost of screening a URL that reaches a shell. A target starting with - is option injection (CWE-88), not shell syntax, and passes (TestDocumentedLimit). An existing guard (0.10.28) already refuses the shape, so this is a second-defence fixture, not a new guard. Triage: #390.

CVE-2026-117573

hagent: args of hagent_arduino and hagent_esp32 interpolated into arduino-cli and idf.py under shell=True

Vulnerability

"hagent through commit 761eec6 contains an OS command injection vulnerability that allows MCP clients to execute shell commands by injecting metacharacters into the args parameter of the hagent_arduino and hagent_esp32 tools. Attackers or prompt-injected agents can supply crafted args interpolated into arduino-cli and idf.py commands run with subprocess.run(shell=True) to execute commands as the MCP server user." NVD names no fixed release (its range is "through commit 761eec6"), and this module cites no upstream fix.

Airlock mitigation

In scope: shell syntax in the args string of both tools NVD names, the "Command / argument injection into a spawn" shape in docs/cve-triage.md. ToolArgSinkGuard(shell_args={"args"}) refuses every DEFAULT_SHELL_METACHARS token at step 2.75, before either tool runs, and so does tool_arg_sink_defaults(mode="deny"), which claims this CVE. Out of scope: the shell=True command line hagent builds, which is upstream's to fix.

args holds command-line options by design, so another option is not shell syntax and passes; which options arduino-cli or idf.py may take is the tool's contract, not this guard's (TestDocumentedLimit). An existing guard (0.10.28) already refuses the shape, so this is a second-defence fixture, not a new guard. Triage: #391.

CVE-2026-118378

Tabularis SQLite driver: stacked statements through describe_table's table_name

Vulnerability

"Tabularis before 0.28.0 contains an SQL injection vulnerability in its SQLite driver that allows MCP clients to execute stacked statements by supplying a crafted table_name to the describe_table tool. Attackers, including AI agents steered by untrusted content, can close the quoted PRAGMA argument and append statements to read, modify, or drop SQLite data, bypassing read-only mode and write approval." Fixed in 0.28.0.

Airlock mitigation

In scope: a stacked SQL statement carried in the table_name argument of an MCP tool, the "carried in an argument" shape of docs/cve-triage.md. agent-airlock has no SQL parser, so the defence is the strict-validation contract, the CVE-2025-68144 and CVE-2026-105793 primitive: a tool that declares its identifier argument as a constrained type, here Annotated[str, StringConstraints(pattern=r"^[A-Za-z_][A-Za-z0-9_]{0,62}$")], has every payload in the fixture refused under @Airlock as validation_error before the tool runs, while users is admitted. No guard and no preset.

Not covered. A tool that declares table_name: str gets no protection: there is no SQL grammar here, and the payload passes strict validation as the string it is (TestABareStrIsOutOfReach). The declared shape is the tool author's, and it refuses legal SQLite names outside it, such as order items. Out of scope: the read-only mode and write approval the stacked statement bypasses are Tabularis's own server logic, and the quoting of the PRAGMA argument is upstream's to fix, which 0.28.0 does. Triage: #393.

CVE-2026-119025

open on Windows and WSL: a discovery-metadata authorization_endpoint with $(...) run through PowerShell

Vulnerability

"open 7.2.0 before 11.0.0 contains an OS command injection vulnerability that allows attackers to execute PowerShell code by supplying crafted URLs on Windows and WSL. Attackers can embed $(...) subexpressions in URLs, such as OAuth authorization_endpoint values from malicious MCP servers, which execute when index.js passes them unescaped to powershell -EncodedCommand." Fixed in open 11.0.0. NVD's references include the MCP Inspector advisory GHSA-q978-78c7-phqw, the MCP client that opened such an endpoint.

Airlock mitigation

In scope: the URL a malicious MCP server hands a client in its discovery metadata, before the client opens it. AuthorizationServerMetadata screens issuer and the authorization, token and registration endpoints as it parses them, and ProtectedResourceMetadata screens each of its authorization_servers (mcp_spec/oauth.py, _screen_metadata_url, 0.10.29): $( and every character RFC 3986 never allows unencoded (a double quote, a backtick, a space, ...) are refused with a ValidationError that names the field and the character's class, never the URL. from_discovery parses through the same screen. On the @Airlock call path, a tool that takes the URL as an argument typed SafeURL refuses it at strict validation (validation_error).

Why partial, and what is out of scope: open is a Node package and MCP Inspector a TypeScript client; neither calls airlock, and the fix is open 11.0.0. The screen covers only a document parsed through these models, and only the fields above: an extra field the model keeps (extra="allow"), such as RFC 8414's service_documentation, is not screened (TestDocumentedLimit). No preset claims this CVE: the screen needs none, it runs on every parse. An existing primitive already refuses the shape, so this is a second-defence fixture, not a new guard. Triage: #399.

CVE-2026-119050

Agno PandasTools: a model-chosen function name dispatched through getattr(pandas, ...)

Vulnerability

"Agno through 3.1.11 contains an unsafe reflection vulnerability in PandasTools that allows attackers to invoke arbitrary pandas functions by supplying model-controlled function names to getattr(). Attackers can use prompt injection to make the agent call pandas.read_pickle on attacker-controlled paths for code execution, or call other readers to read local files." NVD names no fixed release (its range is "through 3.1.11"), and none existed when docs/guide/dispatch-arguments.md was written (agno-agi/agno#8699). No triage issue exists: the watcher drops this record, since its text never names the MCP ecosystem (pinned in this module).

Airlock mitigation

In scope: an argument that names the function a tool calls, which reaches getattr: create_using_function in create_pandas_dataframe and operation in run_dataframe_operation. read_pickle is a plain identifier, so neither the shell screen nor the eval guards see it. ToolArgSinkGuard(dispatch_args=...) reads each as one name at step 2.75, before the tool runs: with an allowlist only the names it lists pass, and with an empty one DEFAULT_DISPATCH_DENY refuses private names, *pickle*, read_*, to_* and the rest. Both configurations refuse every name in the fixture as deny_dispatch_name. reflection_sink_defaults claims this CVE.

Why partial: the default denylist is a backstop, not a contract. It cannot see what an admitted method does with its parameters: DataFrame.query evaluates an expression string and is admitted (TestDocumentedLimit); an allowlist of the operations a tool supports is the control. The guard reads a dispatch argument at the top level only, and never the reader's path in function_parameters. Out of scope: Agno's own getattr call, which is upstream's to fix. An existing guard (0.10.29) already refuses the shape, so this is a second-defence fixture, not a new guard.

CVE-2026-123271

ChatCLI MCP OAuth: a server-supplied resource used as the resource indicator

Vulnerability

"ChatCLI before v1.215.2 contains an improper validation vulnerability in the MCP OAuth login flow that uses server-supplied Protected Resource Metadata as the resource indicator without checking it matches the configured server URL. A malicious MCP server can declare another resource on the same authorization server, obtain the user's bearer token for it, and replay that token against the other resource." Fixed in v1.215.2 by commit 951fe24b, which refuses metadata that names another resource and always requests the token for the configured server.

Airlock mitigation

The same class as CVE-2026-123272 in mcp-go, which has its own module. RFC 9728 section 3.3 says a protected resource metadata document whose resource is not the identifier the client built the metadata URL from must not be used, and ProtectedResourceMetadata.from_discovery refuses it. Out of reach: ChatCLI's own login flow, which does not call airlock, and the client's choice of RFC 8707 resource parameter. ChatCLI's fix sends the configured server URL whatever the metadata says, and a client using airlock should do the same.

Observed against v0.10.31 while writing this module: ProtectedResourceMetadata parsed the malicious document with no error, and there was no from_discovery.

CVE-2026-123272

mcp-go OAuth: protected resource metadata names another server's resource

Vulnerability

"Mark3labs mcp-go before v1.2.1 contains an improper validation vulnerability in OAuthHandler.fetchServerMetadata that allows malicious MCP servers to obtain tokens for other resources by declaring arbitrary resources in well-known metadata. Attackers can declare a different MCP server as the RFC 8707 resource indicator, causing the authorization server to mint tokens the attacker replays against that resource." Fixed in v1.2.1 by commit 90d247ed (mark3labs/mcp-go#1053), which checks the declared resource against the server the client addressed. On the well-known path it also accepts a resource that is a segment-aware prefix of that URL; the check below accepts only the same server.

Airlock mitigation

RFC 9728 section 3.3: the resource a protected resource metadata document returns must be the resource identifier the client built the metadata URL from, or the document must not be used. ProtectedResourceMetadata.from_discovery and validate_protected_resource are that check, so a client that reads the document through them refuses this one. Out of reach: mcp-go's own OAuth handler, which does not call airlock, and the client's choice of RFC 8707 resource parameter: send the MCP server's own URL there whatever the document says. The plain constructor still parses any resource, which TestDocumentedLimit pins.

Observed against v0.10.31 while writing this module: ProtectedResourceMetadata parsed the malicious document with no error, and there was no from_discovery.

CVE-2026-123783

Skyvern script sandbox: functions rebuilt from raw bytecode with types.CodeType

Vulnerability

"Skyvern through 1.0.56 contains a sandbox bypass vulnerability that allows authenticated users to execute code by reconstructing functions from raw bytecode with types.CodeType. Attackers can deploy scripts that evade the is_safe_script_code AST denylist and import blocked modules such as subprocess or socket to run code in the Skyvern server process." Fixed in 1.0.57 by commit 31a64c1a (Skyvern-AI/skyvern#8937), which adds CodeType, FunctionType and LambdaType to the blocked attributes, and sys and types to the blocked imports.

Airlock mitigation

In scope: a code-tool argument escaping the restriction the tool declares, the documented shape in docs/cve-triage.md ("Code injection into an interpreter sink"), and the same split as CVE-2026-53710. Out of scope: Skyvern's own AST denylist, which is upstream's to fix, and the authenticated-user precondition: this guard's whole input is the call's arguments, so it cannot see who sent them.

EvalRCEGuard had the shape of the bug: it refused a string argument only when it saw an eval-family call. These payloads call none, so on 0.10.31 routes a to p were all allowed. Observed against v0.10.31 while writing this module: allowed=True for every payload in the fixture, and for exec in fullwidth letters. The guard now screens for the code object itself, on by default. Skyvern's fix names three of the routes; the fixture covers the rest of the class, in its own routes g to p.

CVE-2026-19591

Codex command-safety parser disagreed with PowerShell about --%

Vulnerability

The OpenAI Codex CLI and Codex Desktop misclassified certain PowerShell commands as safe because their command-safety parser interpreted PowerShell's stop-parsing token (--%) differently than PowerShell itself. A user opening an attacker-prepared repository could have Codex run a file-writing Git command without requesting approval. If the write lands, it can modify Codex's own configuration; if Codex later loads that configuration it launches an attacker-controlled MCP server and executes code as the user. Upstream's fix does not add --% to a denylist of characters — it treats stop-parsing forms as unsupported in the AST-backed command flattener and routes them to the conservative path.

Airlock mitigation

This is not a new guard. It is the failure mode :class:~agent_airlock.mcp_spec.stdio_command_injection_guard.StdioCommandInjectionGuard already exists to prevent — a safety decision taken over an argv model that does not match what the shell will actually build — and the guard was missing the token that breaks the model. v0.8.89 adds it.

The interesting part is why a metachar list was the wrong shape for the fix, and these tests pin that:

  1. Whole-element, never substring. --% is PowerShell's stop-parsing token only when it stands alone. Folding it into DEFAULT_SHELL_METACHARS would have used substring matching and denied ordinary arguments like date +--%Y — noise, with no attack blocked in exchange.
  2. Checked before the metachar walk. After the token, the rest of the argv goes to the native command verbatim. A metachar verdict over those elements is an answer about a command line PowerShell will not construct. The token has to short-circuit, and the negative control below asserts it does: a payload carrying both a stop-parsing token and a shell metachar must report the token, not the metachar.

The guard does not and cannot fix Codex. What it does is refuse the same class of argv at the tool-call boundary, so an agent routed through airlock does not inherit its own parser's disagreement with the shell.

CVE-2026-19753

mcp-rdf-explorer explore_url takes an attacker-supplied URL into SSRF

Vulnerability

"A vulnerability was detected in Model Context Protocol mcp-rdf-explorer 1.0.0. Affected is the function explore_url of the file src/index.ts. Executing manipulation of the argument url can lead to server-side request forgery."

Airlock mitigation

The defect is carried in a tool argument the caller supplies, named in the advisory, reaching an outbound fetch. That is the seam SSRFEgressGuard sits on, and the guard refuses cloud metadata, loopback, link-local and IPv4-mapped-IPv6 targets while admitting a real SPARQL endpoint.

CVE-2026-21520

Capsule ShareLeak / PipeLeak

Vulnerability

Pins the deny-by-default + denied-exfil-sinks + reauth-on-untrusted posture of :func:capsule_indirect_injection_cve_2026_21520_defaults end-to-end: - Eagerly-constructed defaults are byte-identical to a fresh factory call. - Empty allowed_tools denies any read-side call (default_deny). - Every canonical exfil sink in the bundle is denied by name AND by glob.

CVE-2026-23744

MCPJam Inspector unauthenticated public bind

Vulnerability

Primary source (cited per v0.5.1+ convention): - GHSA-232v-j27c-5pp6 / CVE-2026-23744 (CVSS 9.8, fixed 1.4.3): https://github.com/advisories/GHSA-232v-j27c-5pp6

CVE-2026-25874

HuggingFace LeRobot pickle-deserialization RCE

Vulnerability

LeRobot's async-inference PolicyServer / robot-client call pickle.loads() on payloads received over an unauthenticated, non-TLS gRPC channel (SendObservations / SendPolicyInstructions / GetActions). An unauthenticated, network-reachable attacker reaches arbitrary OS command execution by sending a crafted pickle blob (CVSS 9.3, published 2026-04-23, unpatched as of disclosure).

CVE-2026-26118

Microsoft Azure MCP Server SSRF (IMDS token theft)

Vulnerability

Azure MCP Server Tools (< 2.0.0-beta.17) fetch URLs passed through tool arguments without validating the destination. A crafted argument of http://169.254.169.254/metadata/identity/oauth2/token?... causes the server process to hit the Azure Instance Metadata Service and return the managed-identity access token to the caller — trivially escalating any prompt-injection bug into full Azure resource takeover.

Airlock mitigation

validate_endpoint(...) rejects: - all four cloud-metadata hosts in _METADATA_HOSTS (169.254.169.254 / 253 / fd00:ec2::254 / metadata.google.internal), - any hostname that resolves to a private / loopback / link-local IP when allow_private_ips=False (the default), - any hostname matching a caller-supplied blocklist pattern.

CVE-2026-27825

mcp-atlassian arbitrary file write via download_path

Vulnerability

mcp-atlassian (< 0.17.0) exposes a confluence_download_attachment tool with a download_path argument. The tool writes the downloaded file to the provided path without boundary enforcement. An attacker who can prompt-inject the argument can therefore overwrite ~/.ssh/authorized_keys, ~/.bashrc, or any other file the server process can reach — and on the exposed HTTP transport deployment this requires no authentication.

Airlock mitigation

SafePath + FilesystemPolicy.allowed_roots is the textbook mitigation. The upstream fix (in 0.17.0) introduces a validate_safe_path function — agent-airlock has had this since v0.3.0.

CVE-2026-27826

mcp-atlassian SSRF via X-Atlassian-*-Url headers

Vulnerability

mcp-atlassian (< 0.17.0) uses unvalidated X-Atlassian-Jira-Url and X-Atlassian-Confluence-Url request headers to decide where to send upstream API calls. An attacker can redirect outbound requests to the IMDS endpoint or to an internal host to steal credentials or fingerprint the internal network.

Airlock mitigation

The vulnerability is at the HTTP-transport layer — headers aren't tool-call arguments. Runtime middleware cannot validate a header on an incoming request that never invokes a decorated tool.

BUT: when an MCP server is fronted by agent-airlock and the base URL is surfaced as a tool parameter (the common operator pattern these days — per-call URL selection instead of a static config), the same SafeURL + EndpointPolicy primitives that block CVE-2026-26118 block this too. That narrower case is what we assert here.

For the transport-header path, operators should (a) upgrade mcp-atlassian to ≥ 0.17.0 and (b) front their MCP server with an HTTP reverse proxy that strips or validates these headers before they reach application code.

CVE-2026-30615

(Windsurf zero-click MCP config) — spawn-time config pin

Vulnerability

Companion to test_cve_2026_30615_zero_click.py (which covers the config-file diff guard). This suite covers the spawn-time half: the mcp_config_pin preset / :class:McpConfigPinSet, which fingerprints the resolved STDIO spawn config at invocation time and fails closed (raises, never warns) on an injected or mutated server — catching the zero-click pattern even when the mutation never touched a watched config file.

CVE-2026-30616

MCP STDIO transport command-injection (Ox Security class)

Vulnerability

The MCP STDIO transport, implemented in the official Anthropic MCP SDKs across Python, TypeScript, Java, and Rust, passes the command and args fields of a client's STDIO server entry directly to a subprocess without validation, sanitisation, or sandboxing. The subprocess is spawned BEFORE the MCP handshake completes — so if the attacker controls the payload, the OS-level command runs whether or not the "server" ever returns a valid handshake. Ox catalogued four attack classes:

1. Unauthenticated command injection via a poisoned
   ``mcp.json`` / ``claude_desktop_config.json`` / ``.cursor``
   entry.
2. Authenticated command injection via a trusted-but-vulnerable
   MCP server that forwards user-controlled strings into a new
   STDIO invocation.
3. Zero-click prompt-injection chains across Claude Code,
   Cursor, Gemini-CLI, Windsurf, and GitHub Copilot — the agent
   writes a config entry on the attacker's behalf.
4. Config-file takeover — an attacker who can write to
   ``~/.cursor`` or the Claude Desktop config directory owns
   the machine on next launch.

Tenable has CVE-2026-30616 live against Jaaz 1.0.30 as one instance of this class. Ox documents 30+ affected open-source projects (LangChain-ChatChat, Agent Zero, LibreChat, MaxKB, WeKnora, Flowise, MCPJam Inspector, and more), and estimates ~200,000 vulnerable server instances across the ecosystem.

Airlock mitigation

The root cause is "the STDIO transport runs arbitrary OS commands with no policy layer in front of it." That is precisely the seam agent-airlock was designed to fill.

Anthropic's public position (per The Register, 2026-04-16) is that input sanitisation is the application author's responsibility and that STDIO behaviour is "expected." Agent-airlock is the Anthropic-side answer to that: a deny-by-default, in-process middleware that sits between the tool call and the subprocess.

We assert: 1. SecurityPolicy with an explicit tool allow-list blocks any call to an out-of-list tool (stops attack class 1 at the configuration seam — if spawn_stdio_server or equivalent is not in the allow-list, the payload never reaches execve). 2. UnknownArgsMode.BLOCK rejects ghost / LLM-invented arguments on a known tool (stops attack class 2, where the model was talked into inventing a malicious env or args field). 3. SafePath rejects a config-path traversal that would let the attacker write a poisoned entry into ~/.cursor or Claude Desktop's config directory (stops attack class 4).

Attack class 3 (prompt-injection of the chat UI) is a client-surface problem and out-of-scope for runtime middleware; see docs/cves/index.md fit-matrix notes.

CVE-2026-32211

Azure MCP Server SSE token-echo

Vulnerability

The reference Azure MCP server echoed the caller's Authorization header back in a WWW-Authenticate field on 401 responses, leaking short-lived AAD tokens. Fixed in Azure MCP Server 1.4.2.

CVE-2026-32625

(LibreChat MCP server-URL env-interpolation secret leak)

Vulnerability

LibreChat ≤ 0.8.3 (CVSS 9.6, CWE-200, published 2026-06-02) resolves ${VAR} placeholders in a user-supplied MCP server URL against the host process.env during schema validation, so an authenticated user exfiltrates server-side secrets (JWT_SECRET / CREDS_KEY / MONGO_URI) by embedding them in a URL that dials an attacker host. Patched in 0.8.4-rc1.

CVE-2026-33032

"MCPwn" — nginx-ui missing /mcp_message auth middleware

Vulnerability

letting unauthenticated clients invoke 12 destructive MCP tools. CVSS 9.8, ~2,689 exposed instances, actively exploited in April 2026.

agent-airlock doesn't ship nginx-ui, but we're the canonical place for the "would my MCPProxyGuard have caught a missing-auth on a destructive tool?" question. This module proves the preset fires on the exact nginx-ui tool inventory and on an IP-allowlist-only bypass attempt.

Primary sources

  • NVD: https://nvd.nist.gov/vuln/detail/CVE-2026-33032
  • Rapid7 ETR (2026-04-15): https://www.rapid7.com/blog/post/etr-cve-2026-33032-nginx-ui-missing-mcp-authentication/

CVE-2026-39884

flux159/mcp-server-kubernetes argv injection

Vulnerability

Primary source (cited per v0.5.1+ convention): - https://www.sentinelone.com/vulnerability-database/cve-2026-39884/ (2026-04-14, fixed in 3.5.0) - https://nvd.nist.gov/vuln/detail/CVE-2026-39884

CVE-2026-40933

Flowise MCP-stdio adapter RCE regression

Vulnerability

Flowise <= 3.0.x lets an authenticated user define a CustomMCP server with the stdio transport, supplying an arbitrary command + args that Flowise serialises straight into a child-process spawn on the server — no sandbox, no argv sanitisation. CVSS 9.9. Fixed upstream in Flowise 3.1.0. This regression pins the agent-airlock-side control:

CVE-2026-41349

OpenClaw agentic consent-bypass

Vulnerability

Primary source (cited per v0.5.1+ convention): - https://www.thehackerwire.com/vulnerability/CVE-2026-41349/ (CVSS 8.8, disclosed 2026-04-23). The fix surface is :meth:SecurityPolicy.freeze + :meth:SecurityPolicy.verify_frozen, plus the openclaw_cve_2026_41349_defaults() preset that returns a frozen

CVE-2026-41361

OpenClaw IPv6 SSRF guard bypass

Vulnerability

Primary source (cited per v0.5.1+ convention): - https://www.redpacketsecurity.com/cve-alert-cve-2026-41361-openclaw-openclaw/ (CVSS 7.1, disclosed 2026-04-23). The bypass was that OpenClaw's IPv6 guard covered only the four canonical ranges (::/128, ::1/128, fe80::/10, fc00::/7) and left IPv4-mapped / NAT64 / 6to4 / documentation ranges routable.

CVE-2026-42271

CISA KEV regression fixture (LiteLLM MCP command injection)

Vulnerability

This complements test_cve_2026_42271_mcp_subprocess_arg.py (which unit-tests the guard's internals) by reproducing the actual HTTP request-body shape of the two affected LiteLLM endpoints and proving the deny-by-default preset blocks it end-to-end. It is a credibility-proof of existing coverage of an actively-exploited KEV CVE — not a new guard. NVD verbatim (https://nvd.nist.gov/vuln/detail/CVE-2026-42271, retrieved

CVE-2026-44211

Cline Kanban cross-origin WebSocket hijack

Vulnerability

Cline Kanban server (npm kanban < 2.13.0, CVSS 9.7, CWE-1385 + CWE-306, published 2026-06): the agent's control WebSocket server on 127.0.0.1:3484 accepts every upgrade without validating the Origin header. Because browsers do not apply same-origin/CORS to ws://, any website the developer visits can open a WebSocket to the loopback control server and drive the agent (leak workspace data, inject prompts → RCE, kill

CVE-2026-47390

SSRF-protection bypass via alternate IP encodings

Vulnerability

CWE-918: an agent egress filter that validates the literal hostname string of an outbound URL — rather than the resolved IP — is bypassed by encoding a loopback / link-local / cloud-metadata address in a form ipaddress rejects but socket.inet_aton (and the HTTP client / kernel) resolves straight back to an internal address, or by DNS rebinding. This suite pins, per the brief:

CVE-2026-48782

SafeURL IPv6-transition cloud-metadata SSRF bypass

Vulnerability

pydantic-ai 1.56.0–1.101.0 / 2.0.0b1–b2 (CWE-918 SSRF): the cloud-metadata blocklist compared the hostname string, so encoding the metadata IP 169.254.169.254 in an IPv6-transition form (IPv4-mapped, IPv4-compatible, 6to4, Teredo) or as a decimal/octal/hex integer slipped past it while the HTTP client still connected to the metadata endpoint — exposing cloud IAM credentials. This is an incomplete-fix follow-up to CVE-2026-46678 (which

CVE-2026-5023

codebase-mcp RepoMix OS command injection

Vulnerability

Primary source (cited per v0.5.1+ convention): - https://www.sentinelone.com/vulnerability-database/cve-2026-5023/ (unpatched upstream as of 2026-04-24). The package codebase-mcp wrapped the RepoMix CLI and shelled out with user-controlled paths across four handlers. This preset refuses to run those handlers unless the caller explicitly opts into

CVE-2026-53710

ContextForge python_sandbox_server: RestrictedPython escape via raw getattr

Vulnerability

Prior to 1.0.2, the python_sandbox_server sub-project exposes raw getattr through safe_builtins, bypassing RestrictedPython's _getattr_ mediation, and its validate_code pre-check searches for literal dangerous dunder strings. The advisory's proof of concept therefore builds those dunder names at runtime, walks the Python class hierarchy through the exposed getattr, reaches subprocess.Popen and runs an OS command. Its transcript records the decisive line: validation={'valid': True, 'message': 'Code passed validation'} followed by success=True — the code passed the sandbox's own check and then escaped it. Fixed in 1.0.2.

Airlock mitigation

Three compounding weaknesses in the advisory, and agent-airlock reaches one, per the split in docs/cve-triage.md.

Out of scope: weakness 3, "The execute_code MCP tool can be exposed over HTTP/SSE transport with no authentication layer." Same documented shape as CVE-2026-33032 and CVE-2026-23744. Also out of scope is the repair itself — removing getattr from someone else's safe_builtins is upstream's 1.0.2, not something a contract layer can express.

In scope: the primitive. A caller-supplied code argument carrying a payload that reaches an interpreter sink is the documented in-scope shape (docs/cve-triage.md, "Code injection into an interpreter sink"), and getattr is already in :data:DEFAULT_EVAL_SINKS. So this is a second-defence regression fixture against an existing guard, not a new guard — the CVE-2026-90898 / CVE-2026-57124 pattern.

CVE-2026-53820

OpenClaw exec-denylist bypass at MCP loopback spawn

Vulnerability

OpenClaw < 2026.5.12 (exec-denylist bypass, CVSS 6.9, CWE-693 Protection Mechanism Failure): the bundled MCP loopback session-spawn path let an authenticated caller reach a denylisted command because the surface command checked against the exec restriction differs from the effective command actually spawned — a name that passes the surface check resolves, via an alias / wrapper binary / shell, to a denied executable.

CVE-2026-57124

PraisonAI UI /api/mcp/connect spawns caller-chosen local commands

Vulnerability

Prior to 4.6.59 the default PraisonAI UI host applications expose POST /api/mcp/connect without mandatory authentication and accept caller-controlled command and args values, which PraisonAIUI passes to StdioMCPClient to start a local process. The UI commands (praisonai ui, praisonai ui agents, praisonai claw) bind to 0.0.0.0 by default, so a reachable unauthenticated client executes commands as the UI service account. The advisory's own proof of concept records the decisive detail: the spawned touch marker file exists even though the MCP handshake then fails with Connection failed — the process starts before anything validates that this is really an MCP server. Fixed in 4.6.59.

Airlock mitigation

Two halves, and agent-airlock reaches exactly one of them, per the split in docs/cve-triage.md.

The missing authentication on the route, and the 0.0.0.0 bind, are out of scope — the documented shape, the same class as CVE-2026-33032 and CVE-2026-23744. Nothing here can require a credential on someone else's endpoint, and the upstream fix in 4.6.59 is the correct layer for it.

What is reachable is the primitive: command and args arriving flat in a request body and heading for a stdio spawn. That is the CVE-2026-42271 shape, so this is a second-defence regression fixture against an existing guard, not a new guard.

The handshake detail is what makes an argument-level guard the right second defence rather than a redundant one. Because the process starts before the MCP handshake is validated, a defence that waits for a well-formed MCP session is already too late; the refusal has to happen on the registration argument itself.

CVE-2026-57496

NetLicensing MCP splices product_number into a REST path, no segment check

Vulnerability

NetLicensing MCP Server (Labs64) before 0.1.8: netlicensing_get_product passes the caller-controlled product_number into a REST path (consumed by nl_get()) without path-segment validation. A traversal segment normalizes the intended product path onto the /token endpoint, whose response is serialized through _wrap_json instead of _wrap_json_token_read — bypassing redact_token_read() and leaking plaintext APIKEY number and SHOP shopURL token values. A non-administrative API key can thereby recover an administrative token. Fixed in 0.1.8.

Airlock mitigation

The traversal rides in the product_number argument — the seam this library sits on — so SafePathValidator, the same primitive that answers the git_init traversal (CVE-2025-68143), refuses any product_number carrying a .. segment before it can be interpolated into the REST path and normalized onto /token. Its pre-resolution check is a substring test, so the ..%2F slash-encoded form is refused too.

Two honest boundaries, both because the argument is a URL path segment, not a filesystem path, and no URL-path-segment guard exists — by design, a filesystem-path validator must not URL-decode, or it would reject legitimate filenames containing a literal %:

  • A fully percent-encoded segment (%2e%2e%2f) carries no literal .. and passes; whether it then traverses depends on the downstream HTTP layer, which is out of a filesystem-path validator's reach. TestScopeBoundary pins it.
  • The redaction bypass itself (_wrap_json vs _wrap_json_token_read) is server-side and structurally invisible here.

The complete fix is the upstream path-segment validation in 0.1.8. This is a second-defence regression fixture against an existing primitive, not a new guard.

CVE-2026-59176

functype-mcp-server: set_functype_version's version installs any package as functype through pnpm add

Vulnerability

"Prior to 1.4.4, the set_functype_version MCP tool in functype-mcp-server accepts an unconstrained version string using only z.string() validation in packages/mcp-server/src/index.ts and interpolates it into functype@<version> for pnpm add. Package-alias syntaxes such as file:, npm:, URLs, or paths can cause an arbitrary local or remote package to be installed as functype. Immediately afterward, initDocsData(true) in packages/mcp-server/src/lib/docs/data.ts resolves functype/cli and dynamically imports the newly installed module, executing attacker-controlled JavaScript with the MCP server process's privileges." The record goes on to say the tool is enabled by default without authentication in stdio mode, and that a prompt-injected assistant can be induced to invoke it. Fixed in 1.4.4.

Airlock mitigation

In scope: an argument the server hands a package installer, which must be a bare version and was not. ToolArgSinkGuard(installer_args={"version"}) requires a PEP 440 version or an npm semver at step 2.75 and refuses every alias NVD names (file:, npm:, a URL, a path) and their siblings (github:, git+, whitespace) as deny_not_a_version, before the tool runs. tool_arg_sink_defaults(installer_args={"version"}, mode="deny") does the same and claims this CVE. The argument has to be named: the preset's shell screen alone admits file:../evil, which carries no shell syntax (TestDocumentedLimit).

Out of scope: the dynamic import of what was installed, the tool being enabled without authentication in stdio mode, and the httpStream transport's reach: all three are functype's, and 1.4.4 fixes the argument at the source. A range (>=1.4.4) and a dist-tag (latest) are not bare versions and are refused too; resolve them on the host. An existing guard (0.10.28) already refuses the shape, so this is a second-defence fixture, not a new guard. Triage: #395.

CVE-2026-6980

GitPilot-MCP repo_path injection

Vulnerability

Primary source (cited per v0.5.1+ convention): - RedPacket Security CVE alert (2026-04-25): https://www.redpacketsecurity.com/cve-alert-cve-2026-6980-divyanshu-hash-gitpilot-mcp/ - vulnerability.circl.lu Vendor unresponsive; project does not version. Preset matches by tool-name regex only.

CVE-2026-75062

Google langfun lf.query evaluates model-generated Python unsandboxed

Vulnerability

"Improper Neutralization of Directives in Dynamically Evaluated Code ('Eval Injection') in the default lf.query Python protocol in Google langfun versions prior to 0.1.2 allows remote unauthenticated attackers to execute arbitrary Python code in the context of the host application via crafted prompt inputs that cause the model to generate executable Python expressions evaluated without a sandbox."

Airlock mitigation

This is the exploitation class EvalRCEGuard's own docstring names — "a model-derived string reaches a Python runtime evaluator" — and the guard refuses the disclosed payload shapes while admitting the arithmetic the protocol exists to evaluate.

It is partial for a reason worth stating rather than burying. The defect is that langfun calls an evaluator on model output at all; the sandbox is missing inside langfun, not at a tool-call boundary. agent-airlock only sees this if the generated expression passes through an airlocked tool argument. Where it does, the guard refuses it. Where langfun evaluates internally, nothing in this library is on the path, and no arrangement of presets changes that — upgrade to langfun 0.1.2.

So this fixture pins the primitive, not the patch: if a model-generated expression carrying an eval sink reaches a guarded argument, it is refused.

CVE-2026-75130

Upstash Context7 "ContextCrush" MCP instruction injection

Vulnerability

Context7 through 2.1.2 serves a per-library Custom AI Instructions ("Custom Rules") field through its MCP server without sanitising it. An attacker registers a library in the public Context7 registry and embeds instructions in that field; when any developer later asks their coding agent about that library, the text is inserted directly into the model's working context as though it were documentation. No user interaction with the attacker is required beyond a routine documentation request.

Noma Security's proof of concept chained three legs through the connected agent's own, already-authorised tools:

1. Search the workspace for ``.env`` files and read them.
2. File the contents as a **GitHub issue** on an attacker-owned
   repository.
3. Delete local folders on the victim's machine.

NVD records two very different scores. v3.1 rates it 9.0 Critical. v4.0 rates it 6.4 Medium — because it scores VC:N/VI:N/VA:N with SC:H/SI:H/SA:H: Context7 itself is unharmed and the entire impact lands on the connected downstream system. That gap is not a disagreement about how bad this is; it is an accurate description of the class, and the reason a server-side fix does not protect an agent talking to some other poisoned source. Upstash accepted the findings and shipped rule sanitisation with guardrails to production within days.

Airlock mitigation

Nothing in the chain crosses a network boundary the agent was not already authorised to cross. The agent may read files. It may call the GitHub tool. It may delete. Every individual call is in-policy for an authenticated principal — what is wrong is the arguments: a .env path nobody asked for, an issue body full of credentials, a delete nobody requested. A transport or identity layer sees three authorised calls and has nothing to object to. That is an argument-level failure, which is the seam this library exists to cover.

CVE-2026-77521

MaxKB SandboxShellBackend exposes an unapproved execute shell tool

Vulnerability

Prior to 2.10.5-lts, MaxKB assistants carrying a tool, MCP tool, skill or sub-application use SandboxShellBackend, which "exposes an execute shell tool without excluding it and omits execute from interrupt_on, so human approval is not required. Untrusted chat or ingested content can therefore cause command execution; source deployments with MAXKB_SANDBOX disabled run commands directly as the application user, while the official root container's string-based gosu wrapper allowed shell metacharacters to execute outside the intended sandbox." Fixed in 2.10.5-lts.

Airlock mitigation

Three weaknesses compound here and agent-airlock reaches one, per the split in docs/cve-triage.md.

Out of scope: the exposure and the approval gate. execute being offered at all (CWE-749) and being absent from interrupt_on (CWE-250) are decisions in MaxKB's own assistant configuration. agent-airlock ships the primitives that express both — SecurityPolicy(allowed_tools=...) for least privilege and PolicyEscalation / Approver for a human gate — but they are a posture an operator adopts for their own tools. Nothing in a contract layer can impose an approval step on someone else's agent backend, and 2.10.5-lts is the right place for it.

In scope: the primitive. Shell metacharacters riding in a caller-controlled command string is the documented in-scope shape (docs/cve-triage.md, "Metacharacter / escape neutralisation mismatch", anchored on CVE-2026-19591), and StdioCommandInjectionGuard already refuses it. So this is a second-defence regression fixture against an existing guard, not a new guard — the CVE-2026-90898 / CVE-2026-57124 pattern.

CVE-2026-78575

IBM Langflow MCP stdio server config takes unvalidated command-line arguments

Vulnerability

IBM Langflow OSS 1.0.0 through 1.11.5 "could allow a remote authenticated attacker to execute arbitrary commands due to improper validation of command-line arguments in the MCP stdio server configuration."

Its sibling CVE-2026-81941 (same bulletin, CWE-284, also CVSS 8.8) is the authorization half: an authenticated non-administrative user can build a flow whose MCP Tools component uses the local stdio subprocess transport, bypassing both LANGFLOW_CUSTOM_COMPONENT_ADMIN_ONLY and LANGFLOW_BLOCK_CODE_INTERPRETER_COMPONENTS — the two server-side controls meant to prevent exactly this.

Airlock mitigation

Same split as CVE-2026-79748 (MCPHub). Nothing here can restore Langflow's admin-only flag — that is CVE-2026-81941's half, it lives in Langflow's authorization layer, and this fixture does not pretend to reach it.

What is reachable is the primitive the missing check hands over: an attacker-controlled stdio command / args pair heading for a subprocess spawn. That is the shape agent-airlock already refuses for CVE-2026-40933 (Flowise) and CVE-2026-42271 (LiteLLM).

The reason this file exists rather than being folded into those: neither stdio guard covers this CVE on its own, and they fail in opposite directions. Measured, not assumed:

============================ ====================== ====================== payload metachar guard allowlist guard ============================ ====================== ====================== uvx ... "x.json; rm -rf /" blocks allows (cmd allowlisted) /bin/bash -c id allows (no metachar) blocks python -c "__import__..." allows (no metachar) blocks ============================ ====================== ======================

StdioCommandInjectionGuard is a shell-metacharacter detector, so an interpreter flag carries a payload straight past it — and that is precisely the shape Langflow's bulletin describes. McpSubprocessArgInjectionGuard allowlists the command, so once a launcher is allowlisted a metacharacter riding in its arguments goes through.

Run together they cover every disclosed shape and still admit the legitimate component, which test_running_both_guards_covers_every_disclosed_shape asserts end to end. That is a composition requirement rather than a bug in either guard, and it is pinned here so the pairing cannot be dropped from a preset on the belief that one is redundant.

CVE-2026-79538

MetaMCP inspector proxy spawns the stdio command its query string names

Vulnerability

MetaMCP up to and including 2.4.22 serves an internal MCP inspector proxy at GET /mcp-proxy/server/stdio. Its createTransport STDIO branch (apps/backend/src/routers/mcp-proxy/server.ts) reads command, args and env from the request's query string and hands them to ProcessManagedStdioTransport, which spawns them. In the advisory's words, "the handler accepts the process parameters from the request itself rather than resolving them from a server record the caller owns, and does not check them against an allowlist. The only gate is a logged-in session. Registration is open by default". No fixed release existed when this was catalogued: 2.4.22 (2025-12-19) is the newest release, and the advisory names no patched version.

Airlock mitigation

The CVE-2026-79748 split, on a GET route. That any registered user reaches the proxy is an authorization defect in MetaMCP's own Express router, out of scope for the same reason as CVE-2026-33032 and CVE-2026-23744. What that hole hands over is a request-controlled stdio spawn config (command / args / env) with no allowlist, the shape McpSubprocessArgInjectionGuard already refuses for CVE-2026-42271 and CVE-2026-79748. So this is a second-defence fixture against an existing guard, not a new guard.

The guard judges a decoded config. MetaMCP splits args with shell-quote and parses env as JSON before it spawns; the test's _spawn_config repeats those three lines, so the verdicts are about what reaches the spawn. Fed the raw query instead, the guard refuses it too: since 0.10.21 an env it cannot read, such as that JSON string, is refused rather than skipped.

CVE-2026-79748

MCPHub server-config endpoints spawn attacker-supplied stdio commands

Vulnerability

MCPHub before 0.12.15 exposes POST /api/servers and PUT /api/servers/:name, which create or update an MCP server configuration and then immediately spawn the configured stdio process via child_process.spawn. Authentication is required, but no authorization check restricts the endpoints to admins, and neither the command nor the args field is allowlisted or sanitised. Any authenticated non-admin user can therefore submit a configuration with command: "/bin/sh" and arbitrary args and execute it as MCPHub's OS user — commonly root in the published Docker image and in npx / systemd deployments. Fixed in 0.12.15 (PR #770).

Airlock mitigation

This CVE has two halves and agent-airlock reaches exactly one of them.

The assigned weakness is CWE-862 Missing Authorization — the same class as CVE-2026-33032 (nginx-ui) and CVE-2026-23744 (mcpjam), both of which are listed out-of-scope in tests/cves/README.md. Nothing in this library can add an admin check to an HTTP route that never calls into it, and this fixture does not pretend otherwise.

What is reachable is the primitive the missing check hands the attacker: a request-controlled stdio spawn config (command / args / env) arriving at a child_process.spawn sink. That is byte-for-byte the shape :class:~agent_airlock.mcp_spec.subprocess_arg_guard.McpSubprocessArgInjectionGuard already refuses for the KEV-listed CVE-2026-42271, so this is a second-defence regression fixture against an existing guard, not a new guard. A deployment that routes its spawn configs through the guard survives the authorization hole; one that does not, does not.

Per docs/cve-triage.md: where the CVE's class and its primitive split, the primitive decides whether a second-defence test is worth adding. This is that case, written down.

CVE-2026-90898

Bifrost MCP client registration spawns an unauthenticated stdio command

Vulnerability

Bifrost registers MCP clients through its management API. A stdio client is a command plus args, and Bifrost starts that program the moment the client is added — no MCP handshake required. The shipped default is governance.auth_config.is_enabled=false, and with auth off every caller is treated as a local admin, so a single unauthenticated POST /api/mcp/client runs a program as the Bifrost process user (appuser on the official image). transports/v2.1.0 refuses an unauthenticated stdio registration with 403; transports/v2.0.0 still allows it.

Airlock mitigation

Two halves, and agent-airlock reaches exactly one of them. This is the split docs/cve-triage.md describes, and it cuts the opposite way from CVE-2026-90617, which was dispositioned out of scope because its argument carried a natural-language prompt rather than a command.

The assigned weakness is CWE-306 Missing Authentication (with CWE-284 Improper Access Control) — the documented out-of-scope shape, the same class as CVE-2026-33032 and CVE-2026-23744. Nothing here can put an auth check on someone else's management route, and this fixture does not pretend to. The upstream fix is a 403 in the handler, which is the correct layer.

What is reachable is the primitive that missing check hands the attacker: a request-controlled stdio spawn config (command / args) arriving at a spawn sink. That is the CVE-2026-42271 shape byte for byte, so this is a second-defence regression fixture against an existing guard, not a new guard. A deployment that routes registration payloads through :class:~agent_airlock.mcp_spec.subprocess_arg_guard.McpSubprocessArgInjectionGuard survives the auth hole; one that does not, does not.