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:
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
- Advisory: https://labs.cloudsecurityalliance.org/research/csa-research-note-flowise-mcp-rce-exploitation-20260409-csa/
- Regression test:
tests/cves/test_cve_2025_59528_flowise.py
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)
- CVSS: 8.7 (High)
- Airlock fit: PARTIAL
- NVD: https://nvd.nist.gov/vuln/detail/CVE-2025-59536
- Advisory: https://research.checkpoint.com/2026/rce-and-api-token-exfiltration-through-claude-code-project-files-cve-2025-59536/
- Regression test:
tests/cves/test_cve_2025_59536_claude_code_hooks_rce.py
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
- CVSS: 8.2 (High)
- Airlock fit: strong
- NVD: https://nvd.nist.gov/vuln/detail/CVE-2025-68143
- Advisory: https://github.com/advisories/GHSA-5cgr-j3jf-jw3v
- Regression test:
tests/cves/test_cve_2025_68143_git_init_path_traversal.py
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
- CVSS: 8.1 (High)
- Airlock fit: strongest
- NVD: https://nvd.nist.gov/vuln/detail/CVE-2025-68144
- Advisory: https://github.com/advisories/GHSA-9xwc-hfwc-8w59
- Regression test:
tests/cves/test_cve_2025_68144_git_arg_injection.py
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
- CVSS: 7.1 (High)
- Airlock fit: strong
- NVD: https://nvd.nist.gov/vuln/detail/CVE-2025-68145
- Advisory: https://github.com/advisories/GHSA-j22h-9j4x-23w5
- Regression test:
tests/cves/test_cve_2025_68145_git_repo_root_escape.py
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
- CVSS: 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
- Airlock fit: partial
- NVD: https://nvd.nist.gov/vuln/detail/CVE-2026-102911
- Advisory: https://github.com/zosmaai/pi-llm-wiki/issues/185
- Regression test:
tests/cves/test_cve_2026_102911_pi_llm_wiki_url.py
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
- CVSS: 7.3 (HIGH) — CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:L/A:L, CWE-918
- Airlock fit: strong
- NVD: https://nvd.nist.gov/vuln/detail/CVE-2026-104120
- Advisory: https://github.com/modelcontextprotocol/servers/issues/4492
- Regression test:
tests/cves/test_cve_2026_104120_mcp_fetch_ssrf.py
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
- CVSS: 9.9 (CRITICAL) — CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:H, CWE-78
- Airlock fit: partial
- NVD: https://nvd.nist.gov/vuln/detail/CVE-2026-105697
- Advisory: https://github.com/langflow-ai/langflow/security/advisories/GHSA-w794-rj3p-xv45
- Regression test:
tests/cves/test_cve_2026_105697_langflow_stdio_spawn.py
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
- CVSS: 9.9 (CRITICAL) — CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:H, CWE-78
- Airlock fit: partial
- NVD: https://nvd.nist.gov/vuln/detail/CVE-2026-105740
- Advisory: https://github.com/langflow-ai/langflow/security/advisories/GHSA-7w94-79vh-5mr2
- Regression test:
tests/cves/test_cve_2026_105740_langflow_stdio_env.py
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
- CVSS: 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
- Airlock fit: partial
- NVD: https://nvd.nist.gov/vuln/detail/CVE-2026-105788
- Advisory: https://github.com/microsoft/UFO/security/advisories/GHSA-6ppj-5886-4f26
- Regression test:
tests/cves/test_cve_2026_105788_ufo_type_text.py
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
- CVSS: 9.1 (CRITICAL) — CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:L/I:H/A:L, CWE-78
- Airlock fit: partial
- NVD: https://nvd.nist.gov/vuln/detail/CVE-2026-105793
- Advisory: https://github.com/microsoft/UFO/security/advisories/GHSA-5cjx-4375-4877
- Regression test:
tests/cves/test_cve_2026_105793_ufo_press_key.py
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
- CVSS: 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
- Airlock fit: partial
- NVD: https://nvd.nist.gov/vuln/detail/CVE-2026-105797
- Advisory: https://github.com/microsoft/simplechat/security/advisories/GHSA-h4mw-qw8m-5x4j
- Regression test:
tests/cves/test_cve_2026_105797_simplechat_stdio_plugin.py
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
- CVSS: 7.8 (HIGH) — CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H
- Airlock fit: partial
- NVD: https://nvd.nist.gov/vuln/detail/CVE-2026-108953
- Advisory: https://github.com/cfdude/mac-shell-mcp/issues/45
- Regression test:
tests/cves/test_cve_2026_108953_mac_shell_mcp.py
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
- CVSS: 7.8 (HIGH) — CVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H
- Airlock fit: partial
- NVD: https://nvd.nist.gov/vuln/detail/CVE-2026-111451
- Advisory: https://github.com/wheels-dev/wheels/security/advisories/GHSA-cghp-86c6-6fjc
- Regression test:
tests/cves/test_cve_2026_111451_wheels_cli_mcp_tools.py
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 deployinterpolates 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.0deploywas listed as an MCP tool; 4.1.1 hid it. Its advertised input schema was empty, but LuCLI'stools/callforwards every argument key to the module askey=valueregardless, and Wheels 4.1.0 readsrelease=<value>exactly as it reads--release=<value>, the version its deploy parser uses.packages removeandgeneratetake a name with no validation, so..or a../prefix deletes the project's contents or writes.cfmfiles 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
StdioCommandInjectionGuardwhen it carries a shell metacharacter: the CVE-2026-108953 pattern._lucli_argvrepeats LuCLI's lines, as_spawn_configdoes 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
- Advisory: https://www.thehackerwire.com/agentcore-cli-rce-via-triple-quote-neutralization-bypass-cve-2026-11393/
- Regression test:
tests/cves/test_cve_2026_11393_codegen_delimiter.py
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
- CVSS: 9.4
- NVD: https://nvd.nist.gov/vuln/detail/CVE-2026-11624
- Advisory: https://github.com/googleapis/mcp-toolbox/issues/3113
- Regression test:
tests/cves/test_cve_2026_11624_mcp_origin_host.py
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
- CVSS: 7.8 (HIGH) — CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H, CWE-78
- Airlock fit: strong
- NVD: https://nvd.nist.gov/vuln/detail/CVE-2026-117558
- Advisory: https://www.vulncheck.com/advisories/ai-sdlc-mcp-server-through-0.31.2-command-injection-via-check-issue-and-check-pr-status
- Regression test:
tests/cves/test_cve_2026_117558_ai_sdlc_repo.py
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
- CVSS: 7.8 (HIGH) — CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H, CWE-78
- Airlock fit: strong
- NVD: https://nvd.nist.gov/vuln/detail/CVE-2026-117559
- Advisory: https://www.vulncheck.com/advisories/droidmind-through-0.4.0-command-injection-via-shell-allowlist-bypass
- Regression test:
tests/cves/test_cve_2026_117559_droidmind_shell_allowlist.py
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()
- CVSS: 7.8 (HIGH) — CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H, CWE-78
- Airlock fit: strong
- NVD: https://nvd.nist.gov/vuln/detail/CVE-2026-117560
- Advisory: https://www.vulncheck.com/advisories/atxp-gemini-cli-extension-through-1.0.0-os-command-injection-via-mcp-tools
- Regression test:
tests/cves/test_cve_2026_117560_atxp_tool_params.py
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()
- CVSS: 7.8 (HIGH) — CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H, CWE-78
- Airlock fit: strong
- NVD: https://nvd.nist.gov/vuln/detail/CVE-2026-117561
- Advisory: https://www.vulncheck.com/advisories/sf-mcp-through-1.3.2-os-command-injection-via-salesforce-cli-flag-values
- Regression test:
tests/cves/test_cve_2026_117561_sf_mcp_flag_values.py
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
- CVSS: 7.8 (HIGH) — CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H, CWE-78
- Airlock fit: strong
- NVD: https://nvd.nist.gov/vuln/detail/CVE-2026-117567
- Advisory: https://www.vulncheck.com/advisories/esp-mcp-through-commit-28afddf-os-command-injection-via-mcp-tool-parameters
- Regression test:
tests/cves/test_cve_2026_117567_esp_mcp_build_params.py
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
- CVSS: 7.0 (HIGH) — CVSS:3.1/AV:L/AC:H/PR:L/UI:N/S:U/C:H/I:H/A:H, CWE-78
- Airlock fit: partial
- NVD: https://nvd.nist.gov/vuln/detail/CVE-2026-117568
- Advisory: https://www.vulncheck.com/advisories/mcp-ffmpeg-through-1.0.5-os-command-injection-via-videopath-and-outputdir
- Regression test:
tests/cves/test_cve_2026_117568_mcp_ffmpeg_paths.py
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
- CVSS: 7.8 (HIGH) — CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H, CWE-78
- Airlock fit: strong
- NVD: https://nvd.nist.gov/vuln/detail/CVE-2026-117569
- Advisory: https://www.vulncheck.com/advisories/kali-gpt-through-commit-a608c74-os-command-injection-via-mcp-scanner-tool-parameters
- Regression test:
tests/cves/test_cve_2026_117569_kali_gpt_scanner_params.py
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
- CVSS: 7.8 (HIGH) — CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H, CWE-78
- Airlock fit: strong
- NVD: https://nvd.nist.gov/vuln/detail/CVE-2026-117573
- Advisory: https://www.vulncheck.com/advisories/hagent-through-commit-761eec6-os-command-injection-via-arduino-and-esp32-mcp-tools
- Regression test:
tests/cves/test_cve_2026_117573_hagent_args.py
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
- CVSS: 7.8 (HIGH) — CVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H, CWE-89
- Airlock fit: partial
- NVD: https://nvd.nist.gov/vuln/detail/CVE-2026-118378
- Advisory: https://github.com/TabularisDB/tabularis/security/advisories/GHSA-xrx3-j98v-67x9
- Regression test:
tests/cves/test_cve_2026_118378_tabularis_describe_table.py
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
- CVSS: 7.5 (HIGH) — CVSS:3.1/AV:N/AC:H/PR:N/UI:R/S:U/C:H/I:H/A:H, CWE-78
- Airlock fit: partial
- NVD: https://nvd.nist.gov/vuln/detail/CVE-2026-119025
- Advisory: https://www.vulncheck.com/advisories/open-7.2.0-before-11.0.0-os-command-injection-via-powershell-on-windows
- Regression test:
tests/cves/test_cve_2026_119025_open_authorization_endpoint.py
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, ...)
- CVSS: 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)
- Airlock fit: partial
- NVD: https://nvd.nist.gov/vuln/detail/CVE-2026-119050
- Advisory: https://www.vulncheck.com/advisories/agno-through-3.1.11-code-execution-via-pandastools-unsafe-reflection
- Regression test:
tests/cves/test_cve_2026_119050_agno_pandas_getattr.py
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
- CVSS: 8.0 (HIGH) — AV:N/AC:H/PR:N/UI:R/S:C/C:H/I:H/A:N, CWE-346
- Airlock fit: partial
- NVD: https://nvd.nist.gov/vuln/detail/CVE-2026-123271
- Advisory: https://github.com/diillson/chatcli/security/advisories/GHSA-95vg-3r9c-2v5q
- Regression test:
tests/cves/test_cve_2026_123271_chatcli_prm_resource.py
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
- CVSS: 8.0 (HIGH) — AV:N/AC:H/PR:N/UI:R/S:C/C:H/I:H/A:N, CWE-346
- Airlock fit: partial
- NVD: https://nvd.nist.gov/vuln/detail/CVE-2026-123272
- Advisory: https://github.com/mark3labs/mcp-go/security/advisories/GHSA-xf6j-693p-wqxx
- Regression test:
tests/cves/test_cve_2026_123272_mcp_go_prm_resource.py
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
- CVSS: 8.8 (HIGH) — AV:N/AC:L/PR:L/UI:N, CWE-184
- Airlock fit: partial
- NVD: https://nvd.nist.gov/vuln/detail/CVE-2026-123783
- Advisory: https://www.vulncheck.com/advisories/skyvern-through-1.0.56-script-sandbox-bypass-via-types-codetype-bytecode
- Regression test:
tests/cves/test_cve_2026_123783_skyvern_codetype.py
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 --%
- CVSS: 8.8 (HIGH) — CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H, CWE-150
- Airlock fit: strong
- NVD: https://nvd.nist.gov/vuln/detail/CVE-2026-19591
- Advisory: https://github.com/openai/codex/pull/22643
- Regression test:
tests/cves/test_cve_2026_19591_codex_stop_parsing.py
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:
- Whole-element, never substring.
--%is PowerShell's stop-parsing token only when it stands alone. Folding it intoDEFAULT_SHELL_METACHARSwould have used substring matching and denied ordinary arguments likedate +--%Y— noise, with no attack blocked in exchange. - 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
- CVSS: 7.3 (HIGH) — CWE-918
- Airlock fit: strong
- NVD: https://nvd.nist.gov/vuln/detail/CVE-2026-19753
- Advisory: https://nvd.nist.gov/vuln/detail/CVE-2026-19753
- Regression test:
tests/cves/test_cve_2026_19753_rdf_explorer_ssrf.py
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
- CVSS: 7.5 (HIGH)
- NVD: https://nvd.nist.gov/vuln/detail/CVE-2026-21520
- Regression test:
tests/cves/test_cve_2026_21520_capsule_indirect_injection.py
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
- CVSS: 9.8
- Advisory: https://github.com/advisories/GHSA-232v-j27c-5pp6
- Regression test:
tests/cves/test_cve_2026_23744_mcpjam.py
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
- CVSS: 9.3
- Advisory: https://www.sentinelone.com/vulnerability-database/cve-2026-25874/
- Regression test:
tests/cves/test_cve_2026_25874_lerobot.py
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)
- CVSS: 8.8 (High)
- Airlock fit: strong
- NVD: https://nvd.nist.gov/vuln/detail/CVE-2026-26118
- Advisory: https://msrc.microsoft.com/update-guide/vulnerability/CVE-2026-26118
- Regression test:
tests/cves/test_cve_2026_26118_azure_mcp_ssrf.py
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
- CVSS: 9.1 (Critical)
- Airlock fit: strong
- NVD: https://nvd.nist.gov/vuln/detail/CVE-2026-27825
- Advisory: https://advisories.gitlab.com/pkg/pypi/mcp-atlassian/CVE-2026-27825/
- Write-up: https://pluto.security/blog/mcpwnfluence-cve-2026-27825-critical/
- Regression test:
tests/cves/test_cve_2026_27825_mcp_atlassian_arbitrary_write.py
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
- CVSS: 7.5 (High, AV:A/PR:N/UI:N, C:H)
- Airlock fit: partial
- NVD: https://nvd.nist.gov/vuln/detail/CVE-2026-27826
- Advisory: https://advisories.gitlab.com/pkg/pypi/mcp-atlassian/CVE-2026-27826/
- Regression test:
tests/cves/test_cve_2026_27826_mcp_atlassian_header_ssrf.py
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
- NVD: https://nvd.nist.gov/vuln/detail/CVE-2026-30615
- Advisory: https://www.tenable.com/cve/CVE-2026-30615
- Regression tests:
tests/cves/test_cve_2026_30615_mcp_config_pin.py,tests/cves/test_cve_2026_30615_zero_click.py
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)
- CVSS: 9.8 (Critical)
- Airlock fit: strongest
- NVD: https://nvd.nist.gov/vuln/detail/CVE-2026-30616
- Advisory: https://www.ox.security/blog/mcp-supply-chain-advisory-rce-vulnerabilities-across-the-ai-ecosystem
- Write-up: https://www.theregister.com/2026/04/16/anthropic_mcp_design_flaw/
- Regression test:
tests/cves/test_cve_2026_30616_mcp_stdio_rce.py
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
- CVSS: 8.6 (High)
- NVD: https://nvd.nist.gov/vuln/detail/CVE-2026-32211
- Advisory: https://msrc.microsoft.com/update-guide/vulnerability/CVE-2026-32211
- Regression test:
tests/cves/test_cve_2026_32211_azure_mcp_token_echo.py
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)
- CVSS: 9.6
- Advisory: https://github.com/danny-avila/LibreChat/security/advisories/GHSA-6vqg-rgpm-qvf9
- Regression test:
tests/cves/test_cve_2026_32625_mcp_env_interpolation.py
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
- CVSS: 9.8
- NVD: https://nvd.nist.gov/vuln/detail/CVE-2026-33032
- Advisory: https://www.rapid7.com/blog/post/etr-cve-2026-33032-nginx-ui-missing-mcp-authentication/
- Regression test:
tests/cves/test_cve_2026_33032_mcpwn.py
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
- NVD: https://nvd.nist.gov/vuln/detail/CVE-2026-39884
- Advisory: https://www.sentinelone.com/vulnerability-database/cve-2026-39884/
- Regression test:
tests/cves/test_cve_2026_39884_kubectl.py
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
- CVSS: 9.9
- Advisory: https://advisories.gitlab.com/npm/flowise-components/CVE-2026-40933/
- Regression test:
tests/cves/test_cve_2026_40933_flowise_mcp_stdio.py
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
- CVSS: 8.8
- Advisory: https://www.thehackerwire.com/vulnerability/CVE-2026-41349/
- Regression test:
tests/cves/test_cve_2026_41349_consent_bypass.py
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
- CVSS: 7.1
- Advisory: https://www.redpacketsecurity.com/cve-alert-cve-2026-41361-openclaw-openclaw/
- Regression test:
tests/cves/test_cve_2026_41361_ipv6_ssrf.py
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)
- CVSS: 3.1
- NVD: https://nvd.nist.gov/vuln/detail/CVE-2026-42271,
- Advisory: https://www.cisa.gov/known-exploited-vulnerabilities-catalog?field_cve=CVE-2026-42271
- Regression tests:
tests/cves/test_cve_2026_42271_kev_regression.py,tests/cves/test_cve_2026_42271_mcp_subprocess_arg.py
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
- CVSS: 9.7
- Advisory: https://advisories.gitlab.com/npm/cline/CVE-2026-44211/
- Regression test:
tests/cves/test_cve_2026_44211_ws_origin.py
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
- Advisory: https://www.cve.org/CVERecord?id=CVE-2026-47390
- Regression test:
tests/cves/test_cve_2026_47390_ssrf_egress.py
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
- Advisory: https://github.com/pydantic/pydantic-ai/security/advisories/GHSA-cg7w-rg45-pc59
- Regression test:
tests/cves/test_cve_2026_48782_safeurl_ipv6_metadata.py
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
- Advisory: https://www.sentinelone.com/vulnerability-database/cve-2026-5023/
- Regression test:
tests/cves/test_cve_2026_5023_codebase_mcp.py
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
- CVSS: 10.0 (CRITICAL) — CWE-94 + CWE-693
- Airlock fit: partial
- NVD: https://nvd.nist.gov/vuln/detail/CVE-2026-53710
- Advisory: https://github.com/advisories/GHSA-xm98-3vcf-fph7
- Regression test:
tests/cves/test_cve_2026_53710_contextforge_sandbox_getattr.py
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
- CVSS: 6.9
- NVD: https://nvd.nist.gov/vuln/detail/CVE-2026-53820
- Regression test:
tests/cves/test_cve_2026_53820_loopback_spawn.py
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
- CVSS: 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
- Airlock fit: partial
- NVD: https://nvd.nist.gov/vuln/detail/CVE-2026-57124
- Advisory: https://github.com/MervinPraison/PraisonAI/security/advisories/GHSA-p75f-6fp4-p57w
- Regression test:
tests/cves/test_cve_2026_57124_praisonai_mcp_connect.py
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
- CVSS: 9.6 (CRITICAL) — CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:N
- Airlock fit: partial
- NVD: https://nvd.nist.gov/vuln/detail/CVE-2026-57496
- Advisory: https://github.com/Labs64/NetLicensing-MCP/security/advisories/GHSA-hxpf-9xvq-wph8
- Regression test:
tests/cves/test_cve_2026_57496_netlicensing_product_path.py
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.TestScopeBoundarypins it. - The redaction bypass itself (
_wrap_jsonvs_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
- CVSS: 7.8 (HIGH) — CVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H, CWE-829
- Airlock fit: strong
- NVD: https://nvd.nist.gov/vuln/detail/CVE-2026-59176
- Advisory: https://github.com/jordanburke/functype/security/advisories/GHSA-wcjj-9m6g-2fr2
- Regression test:
tests/cves/test_cve_2026_59176_functype_version.py
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
- Advisory: https://www.redpacketsecurity.com/cve-alert-cve-2026-6980-divyanshu-hash-gitpilot-mcp/
- Regression test:
tests/cves/test_cve_2026_6980_gitpilot.py
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
- CVSS: 9.2 (CRITICAL) — CWE-95, CWE-1188
- Airlock fit: partial
- NVD: https://nvd.nist.gov/vuln/detail/CVE-2026-75062
- Advisory: https://github.com/google/langfun/issues/725
- Regression test:
tests/cves/test_cve_2026_75062_langfun_eval.py
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
- CVSS: 9.0 (Critical, CVSS v3.1; NVD also records 6.4 Medium under v4.0)
- Airlock fit: strongest
- NVD: https://nvd.nist.gov/vuln/detail/CVE-2026-75130
- Advisory: https://www.vulncheck.com/advisories/context7-prompt-injection-via-custom-ai-instructions
- Write-up: https://noma.security/blog/contextcrush-context7-the-mcp-server-vulnerability/
- Regression test:
tests/cves/test_cve_2026_75130_context7_contextcrush.py
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
- CVSS: 10.0 (CRITICAL) — CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:H
- Airlock fit: partial
- NVD: https://nvd.nist.gov/vuln/detail/CVE-2026-77521
- Advisory: https://github.com/1Panel-dev/MaxKB/security/advisories/GHSA-f36j-f34j-h3rx
- Regression test:
tests/cves/test_cve_2026_77521_maxkb_sandbox_shell.py
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
- CVSS: 8.8 (HIGH) — CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H, CWE-78
- Airlock fit: partial
- NVD: https://nvd.nist.gov/vuln/detail/CVE-2026-78575
- Advisory: https://www.ibm.com/support/pages/node/7286666
- Regression test:
tests/cves/test_cve_2026_78575_langflow_mcp_stdio.py
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
- CVSS: 9.8 (CRITICAL) — CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H, CWE-94
- Airlock fit: partial
- NVD: https://nvd.nist.gov/vuln/detail/CVE-2026-79538
- Advisory: https://www.traceforce.ai/security-advisories/cve-2026-79538
- Regression test:
tests/cves/test_cve_2026_79538_metamcp_stdio_proxy.py
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
- CVSS: 9.9 (CRITICAL) — CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:H, CWE-862
- Airlock fit: partial
- NVD: https://nvd.nist.gov/vuln/detail/CVE-2026-79748
- Advisory: https://github.com/samanhappy/mcphub/security/advisories/GHSA-mx89-jjx9-gjr8
- Regression test:
tests/cves/test_cve_2026_79748_mcphub_spawn_config.py
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
- CVSS: 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
- Airlock fit: partial
- NVD: https://nvd.nist.gov/vuln/detail/CVE-2026-90898
- Advisory: https://github.com/maximhq/bifrost/pull/6757
- Regression test:
tests/cves/test_cve_2026_90898_bifrost_stdio_registration.py
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.