Skip to content

Security Audit: Comprehensive Code Review of claude-mem #1251

@CheswickDEV

Description

@CheswickDEV

I performed a full security audit of this repository (v10.5.2, main branch as of Feb 2026). The analysis covered all source files under src/, plugin/, scripts/, openclaw/, and configuration files. Pre-built binaries (plugin/scripts/claude-mem, 61 MB) and compiled .cjs bundles were excluded.

Overall Risk Rating: HIGH

Claude-mem captures and persists extensive data from coding sessions (tool usage, user prompts, code changes, AI-generated summaries). This data can contain confidential business logic, API keys, passwords, and proprietary source code. The fully unauthenticated HTTP API on a well-known port (37777) makes all of this accessible to any local process and to the entire network if the host is misconfigured.

Findings Overview:

Severity Count
🔴 Critical / High 4
🟡 Medium 6
🟢 Low / Info 7

🔴 Critical / High Findings

C-1: Path Traversal via MCP Tools smart_unfold and smart_outline

File: src/servers/mcp-server.ts, lines 296–298 and 337–339
Type: CWE-22 Path Traversal

The MCP tools smart_unfold and smart_outline accept a file_path parameter from the caller. The path is resolved to an absolute path via resolve(), but no validation is performed to check whether the resulting path stays within an allowed directory. The file is then read directly:

// Line 296-297  no path boundary check
const filePath = resolve(args.file_path);
const content = await readFile(filePath, 'utf-8');

Risk: An attacker (or a compromised LLM context) that can control MCP tool calls can read arbitrary files on the filesystem e.g. ~/.ssh/id_rsa, ~/.claude-mem/.env (containing API keys), /etc/passwd, or any source code files.

Recommendation: Validate the resolved path against an allowed base directory:

const basePath = resolve(process.cwd());
const filePath = resolve(args.file_path);
if (!filePath.startsWith(basePath + path.sep)) {
  return { content: [{ type: 'text', text: 'Access denied: path outside project' }], isError: true };
}

C-2: Entire HTTP API Has No Authentication

File: src/services/worker/http/middleware.ts (entire file), src/services/server/Server.ts
Type: CWE-306 Missing Authentication for Critical Function

The worker service on port 37777 exposes 30+ HTTP endpoints, including:

Endpoint Exposed Data / Action
GET /api/settings Returns all settings including API keys in cleartext
POST /api/settings Allows setting any configuration value including API keys
GET /api/observations Returns all stored coding observations
GET /api/prompts Returns all stored user prompts
GET /api/summaries Returns all session summaries
POST /api/memory/save Allows injecting new "memories" into the database
POST /api/import Allows data import
DELETE /api/pending-queue/all Deletes the processing queue
GET /api/logs Returns complete log files
POST /api/logs/clear Deletes log files

None of these endpoints require any form of authentication. Only the admin endpoints (/api/admin/restart, /api/admin/shutdown) have a localhost IP check via requireLocalhost.

Risk: Any process on the local system can:

  1. Exfiltrate all stored coding observations, user prompts, and session summaries
  2. Extract API keys for Gemini, OpenRouter, and Anthropic
  3. Inject false "memories" that Claude will use as context in future sessions (memory poisoning)
  4. Manipulate configuration (e.g. switch AI provider, change host binding to 0.0.0.0)
  5. Delete all data

Recommendation: Implement at least one of:

  1. Token-based auth: Generate a random bearer token at startup, store it in a permission-restricted file, require it on all API calls
  2. Unix socket instead of TCP: Access is then restricted by filesystem permissions
  3. Extend requireLocalhost middleware to ALL endpoints as an interim measure

C-3: Network Exposure When Host Is Set to 0.0.0.0

File: src/services/worker/http/routes/SettingsRoutes.ts, lines 268–274
Type: CWE-668 Exposure of Resource to Wrong Sphere

The settings validation explicitly allows 0.0.0.0 as a valid host value:

const validHostPattern = /^(127\.0\.0\.1|0\.0\.0\.0|localhost|\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})$/;

When CLAUDE_MEM_WORKER_HOST is set to 0.0.0.0, the entire unauthenticated API becomes available on all network interfaces. The CORS policy blocks browser requests from foreign origins, but direct HTTP calls (curl, scripts, other tools) are not blocked.

Risk: On any shared network (corporate, co-working, home), any participant can read all data and manipulate configuration. Especially critical in combination with C-2 (no authentication).

Recommendation:

  1. Remove 0.0.0.0 from host validation, or at minimum require authentication when binding to non-localhost
  2. CORS alone is not a security mechanism against non-browser clients

C-4: API Keys Exposed via GET /api/settings

File: src/services/worker/http/routes/SettingsRoutes.ts, lines 46–51
Type: CWE-200 Exposure of Sensitive Information

The GET /api/settings endpoint reads the entire settings.json and returns it unfiltered as JSON. This file contains CLAUDE_MEM_GEMINI_API_KEY, CLAUDE_MEM_OPENROUTER_API_KEY, and CLAUDE_MEM_CHROMA_API_KEY in cleartext:

private handleGetSettings = this.wrapHandler((req: Request, res: Response): void => {
    const settings = SettingsDefaultsManager.loadFromFile(settingsPath);
    res.json(settings); // <-- API keys returned in cleartext
});

Risk: Combined with C-2 (no auth), any local process can extract API keys. These could be used to make API calls at the user's expense.

Recommendation: Mask API key fields before returning them (e.g. show only last 4 characters: sk-...abcd). Accept full keys on write, redact on read.


🟡 Medium Findings

M-1: Credential Files Written Without Restrictive Permissions

File: src/shared/EnvManager.ts, line 169
Type: CWE-732 Incorrect Permission Assignment for Critical Resource

~/.claude-mem/.env is written with writeFileSync() without restricting file permissions. Default umask on most systems is 0022, creating files with 0644 readable by all users on the system. Same applies to ~/.claude-mem/settings.json, which contains API keys.

Recommendation: Set restrictive permissions after writing:

writeFileSync(ENV_FILE_PATH, content, { mode: 0o600 });

M-2: Automatic Tool Installation via curl | bash

File: scripts/smart-install.js, lines 168–173 and 215–218
Type: CWE-494 Download of Code Without Integrity Check

The smart-install script auto-installs Bun and uv by executing:

execSync('curl -fsSL https://bun.sh/install | bash', { stdio: 'inherit', shell: true });
execSync('curl -fsSL https://astral.sh/uv/install.sh | bash', { stdio: 'inherit', shell: true });

No checksums or GPG signatures are verified.

Risk: Supply-chain attack if bun.sh or astral.sh are compromised (DNS hijacking, CDN compromise), arbitrary code is executed with the user's privileges.

Recommendation: Implement checksum verification, or prompt the user to install these tools manually instead.


M-3: Gemini API Key Passed as URL Parameter

File: src/services/worker/GeminiAgent.ts, line 374
Type: CWE-598 Sensitive Query Strings in GET Requests

const url = `${GEMINI_API_URL}/${model}:generateContent?key=${apiKey}`;

While this is Google's standard API method, the key may appear in server logs, proxy logs, and error messages.

Recommendation: Use the x-goog-api-key HTTP header instead where supported. Ensure URLs containing keys are not logged.


M-4: JSON Body Limit of 50 MB

File: src/services/worker/http/middleware.ts, line 25
Type: CWE-400 Uncontrolled Resource Consumption

middlewares.push(express.json({ limit: '50mb' }));

Risk: Memory exhaustion by sending large JSON payloads to any POST endpoint. Multiple concurrent requests can crash the worker with OOM.

Recommendation: Reduce to a realistic limit (1–5 MB) or use per-endpoint limits. The import endpoint can retain a higher limit.


M-5: Error Handler Leaks Internal Details

File: src/services/server/ErrorHandler.ts, lines 54–80
Type: CWE-209 Error Messages Containing Sensitive Information

The global error handler returns err.message and err.details directly to the client. Unhandled exceptions may contain internal paths, stack traces, or database errors.

Recommendation: Return generic error messages to clients in production. Log details internally only.


M-6: CORS Allows Requests Without Origin Header

File: src/services/worker/http/middleware.ts, lines 29–32
Type: CWE-346 Origin Validation Error

if (!origin || origin.startsWith('http://localhost:') || ...) {
    callback(null, true);
}

All non-browser clients send requests without an Origin header. CORS is not a security mechanism against non-browser clients this reinforces C-2.


🟢 Low / Informational Findings

ID Description File
L-1 No rate limiting on any API endpoint middleware.ts
L-2 ReDoS protection incomplete tag count > 100 is logged but processing continues (actual regex risk is low due to non-backtracking patterns) src/utils/tag-stripping.ts:39–47
L-3 SQLite synchronous = NORMAL minimal data loss risk on system crash (acceptable for use case) src/services/sqlite/Database.ts:43
L-4 Health endpoint reveals process details PID, platform, workerPath, uptime, AI provider status src/services/server/Server.ts:162–176
L-5 DOMPurify is a dependency but usage in server code wasn't found verify it's used consistently in the viewer UI package.json:103
L-6 Handlebars dependency has known SSTI risks if user-controlled data is used as template strings package.json:106
L-7 PID file race conditions in process management mitigated by cooldown mechanisms, low practical risk ProcessManager.ts

Recommended Fix Priority

Priority Finding Effort Impact
P0 C-2: Add authentication to HTTP API Medium Eliminates the single largest risk unauthorized data access
P0 C-1: Path boundary check in MCP tools Low Simple fix, prevents arbitrary file reading
P1 C-4: Redact API keys in GET /api/settings Low Prevents cleartext key exfiltration
P1 M-1: Set 0600 permissions on credential files Low One-line fix per write operation
P1 C-3: Remove or restrict 0.0.0.0 host binding Low Prevents accidental network exposure
P2 M-5: Sanitize error responses Low Prevents information disclosure
P2 M-4: Reduce JSON body limit Low Prevents memory exhaustion
P2 M-2: Add integrity checks for auto-installed tools Medium Reduces supply-chain risk

Scope & Methodology

  • Analyzed: All TypeScript source files (src/, plugin/hooks/, scripts/, openclaw/), configuration files (.gitignore, .mcp.json, conductor.json, package.json, tsconfig.json), Dockerfile, install scripts
  • Not analyzed: Pre-built binary plugin/scripts/claude-mem (61 MB), compiled bundles (worker-service.cjs, mcp-server.cjs, context-generator.cjs), documentation translations (docs/i18n/), test files (reviewed for pattern confirmation only)
  • No dynamic testing was performed all findings are based on static code analysis
  • No private infrastructure was accessed or assumed

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions