Comprehensive Code Review of thedotmack/claude-mem
GitHub Issue #1251 · Opened Feb 26, 2026 by CheswickDEV · Rated HIGH risk
ESL Analysis & Patch · April 2026
A third-party persistent memory plugin for Claude Code that captures, compresses, and reinjects session context across coding sessions.
Captures tool outputs, user prompts, code changes, and AI summaries. Stores them as searchable "observations" in SQLite + ChromaDB.
Exposes tools via Model Context Protocol: search, timeline, smart_unfold, smart_outline, smart_search, and corpus management.
Long-running Express HTTP API on port 37777 (bound to 127.0.0.1 by default). Handles data persistence, AI compression, and background processing.
62K+ GitHub stars · 5.2K forks · 2000+ commits · Single maintainer · AGPL-3.0 licensed · npm: v12.2.0
thedotmack). Grows rapidly with Claude Code adoption.startsWith containment. But NOT applied to smart_unfold / smart_outline.From Issue #1251 — 17 findings across Critical, Medium, and Low severity
| ID | Finding | Severity | CWE | Status |
|---|---|---|---|---|
| C-1 | Path Traversal in smart_unfold / smart_outline | CRITICAL | CWE-22 | OPEN |
| C-2 | Entire HTTP API has no authentication | HIGH | — | OPEN |
| C-3 | Network exposure when host set to 0.0.0.0 | HIGH | CWE-668 | OPEN |
| C-4 | API keys exposed via GET /api/settings | HIGH | CWE-200 | OPEN |
| M-1 | Credential files written without restrictive permissions | MEDIUM | CWE-732 | OPEN |
| M-2 | Auto-install via curl | bash (no checksum) | MEDIUM | CWE-494 | OPEN |
| M-4 | JSON body limit of 50 MB (memory exhaustion) | MEDIUM | CWE-400 | OPEN |
| M-5 | Error handler leaks internal details | MEDIUM | CWE-209 | OPEN |
| + 7 Low/Informational findings (no rate limiting, ReDoS, health endpoint info leak, etc.) | ||||
Issue #737 — ProcessRegistry with PID capture, orphan reaper (every 30s), stale session reaper (every 2min), hard cap of 10 processes, SIGKILL escalation after 5s timeout.
Commit 5d79bb7 (Feb 18) + 80a8c90 (Mar 16)
Proper two-layer defense added: alphanumeric regex allowlist + startsWith containment check after resolve(). But only for CorpusStore — not MCP tools.
CHANGELOG v12.1.0
requireLocalhost middleware added to /api/admin/restart, /api/admin/shutdown, /api/admin/doctor — IP-based guard.
CORS policy restricts browser origins to localhost / 127.0.0.1 only. Does NOT block curl, scripts, or local malware.
While these mitigations are welcome, the most critical findings from the audit remain open ↓
smart_unfold and smart_outline accept any file path. A compromised MCP session can read /etc/passwd, ~/.ssh/id_rsa, or any file on disk.
CWE-22 · UNPATCHED
Zero bearer token, API key, or session auth on any non-admin endpoint. Any local process can exfiltrate all observations, inject false memories, or extract API keys.
UNPATCHED
GET /api/settings returns Gemini, OpenRouter, and Anthropic API keys in cleartext. Combined with C-2, any local process can steal them.
CWE-200 · UNPATCHED
Single maintainer · curl | bash install · 2fa: false on npm · pre-1.0 SDK dependency · No SBOM · AI-modified code incident (v12.1.4)
STRUCTURAL
Both smart_unfold and smart_outline resolve the path but never check boundaries:
// src/servers/mcp-server.ts — VULNERABLE CODE handler: async (args) => { const filePath = resolve(args.file_path); // ← normalizes, does NOT restrict const content = await readFile(filePath, 'utf-8'); // ← reads ANY file on disk const unfolded = unfoldSymbol(content, filePath, args.symbol_name); // ... }
Meanwhile, CorpusStore in the same project does it correctly:
// CorpusStore.getFilePath() — PROPERLY GUARDED private getFilePath(name: string): string { const safeName = this.validateCorpusName(name); // ← regex allowlist const resolved = path.resolve(this.corporaDir, safeName); if (!resolved.startsWith(path.resolve(this.corporaDir) + path.sep)) { throw new Error('Invalid corpus name'); // ← containment check } return resolved; }
A malicious repo contains a crafted comment that tricks Claude into calling smart_unfold(file_path="~/.ssh/id_rsa"). Private keys are returned in the response and may be exfiltrated via subsequent tool calls.
Malware or a rogue npm package calls http://127.0.0.1:37777/api/settings to steal API keys, then /api/search to exfiltrate all stored observations — code, decisions, business logic.
An attacker POSTs fake observations via the unauthenticated API. Claude trusts injected "memories" in future sessions, potentially introducing backdoors or wrong decisions.
Developer sets CLAUDE_MEM_WORKER_HOST=0.0.0.0 for Docker. The entire API is now on the network. On shared WiFi or corporate LAN, anyone can access all data.
DataCamp (Mar 30, 2026): "claude-mem should be treated as unsafe on shared machines, cloud VMs, or environments with secrets."
Engineering Software Lab — creator of AI security tools — has developed and published a local patch that mitigates the path traversal vulnerability (C-1).
// ESL Patch: Path containment guard injected before readFile() handler: async (args) => { const filePath = resolve(args.file_path); + const cwd = resolve(process.cwd()); + if (!filePath.startsWith(cwd + sep) && filePath !== cwd) { + throw new Error("Path traversal blocked: " + filePath + " is outside " + cwd); + } const content = await readFile(filePath, 'utf-8'); // ... rest of handler unchanged }
Applied to both smart_unfold and smart_outline handlers in the compiled mcp-server.cjs
📤 Upstream PR submitted: ESL has submitted a pull request to thedotmack/claude-mem to fix this in the TypeScript source (src/servers/mcp-server.ts). Until merged, use the local patch below.
git clone https://github.com/ zuwasi/claude-mem-security-fix cd claude-mem-security-fix .\apply_patch.ps1
git clone https://github.com/ zuwasi/claude-mem-security-fix cd claude-mem-security-fix chmod +x apply_patch.sh ./apply_patch.sh
• The patch is overwritten on plugin updates — re-run after npm update -g claude-mem
• Restart Claude Code after applying for the fix to take effect
• Run .\verify_patch.ps1 or ./verify_patch.sh to confirm 2 guards are present
• This fixes C-1 (path traversal) only — C-2/C-4 require upstream changes
# PowerShell — should return 2 matches Select-String "Path traversal blocked" ` "$env:APPDATA\npm\node_modules\claude-mem\plugin\scripts\mcp-server.cjs" # Expected output: mcp-server.cjs:217: ... Path traversal blocked ... (smart_unfold) mcp-server.cjs:221: ... Path traversal blocked ... (smart_outline)
smart_unfold("./src/index.ts", "main") | ✓ Allowed — within project |
smart_unfold("/etc/passwd", "x") | ✗ Blocked — outside cwd |
smart_outline("../../.ssh/id_rsa") | ✗ Blocked — path traversal |
smart_outline("./lib/utils.py") | ✓ Allowed — within project |
1. Apply ESL path traversal patch
2. Never set CLAUDE_MEM_WORKER_HOST=0.0.0.0
3. Don't run claude-mem on machines with production secrets or customer data
4. Pin your claude-mem version after patching
1. Add Windows Firewall rule blocking port 37777 inbound
2. Audit ~/.claude-mem/settings.json for exposed API keys
3. Run verify_patch.ps1 after each plugin update
4. Consider containerizing claude-mem for isolation
1. Evaluate if you need claude-mem at all (Amp Code provides built-in threads and context)
2. Wait for upstream to add API authentication (C-2)
3. Monitor Issue #1251 for official security response
4. Consider alternative memory solutions
Creator of AI security tools — protecting teams from emerging AI supply-chain threats
github.com/zuwasi/claude-mem-security-fix
Automated patch scripts for Windows & macOS/Linux · Verification tools · Full vulnerability report
Original audit: CheswickDEV · Issue #1251 · Feb 26, 2026
ESL patch & advisory: April 2026