[ ⌘K ]
← BACK TO SEARCH

yoloshii/ClawMem

critical

On-device memory layer for AI agents. Claude Code, Hermes and OpenClaw. Hooks + MCP server + hybrid RAG search.

MCP server (purpose undetermined)

purpose: MCP server (purpose undetermined)threat: network exposed
TypeScript170May 20, 2026May 20, 2026GITHUB
ai-agent-memoryai-agentsbunclaude-codeembeddingshybrid-searchllama-cpplocal-firstmcp-servermcp-toolsmemorymodel-context-protocolon-device-aiopenclawpluginragretrieval-augmented-generationsqlitetypescriptvector-search
5/20/2026
high1 finding
src/server.ts
525function handleExport(_req: Request, _url: URL, store: Store): Response {
526  const docs = store.db.prepare(`
527    SELECT d.id, d.collection, d.path, d.title, d.content_type, d.confidence,
528           d.access_count, d.quality_score, d.pinned, d.created_at, d.modified_at,
529           d.duplicate_count, d.revision_count, d.topic_key, d.normalized_hash,
530           c.doc as body
531    FROM documents d
532    JOIN content c ON c.hash = d.hash
533    WHERE d.active = 1
534    ORDER BY d.collection, d.path
535  `).all() as any[];
536
537  return jsonResponse({
538    version: "1.0.0",
539    exported_at: new Date().toISOString(),
540    count: docs.length,
541    documents: docs,
542  });
543}

// Exploitable if MCP is exposed to network (network_exposed) or if a compromised LLM can make HTTP requests to localhost.

The /export endpoint returns all document bodies (including full content) without any authentication check. While the server has optional Bearer token auth, if CLAWMEM_API_TOKEN is not set, this endpoint is completely open. Even with auth, the endpoint dumps all documents in bulk, which is excessive for the intended purpose of a memory server.

ImpactAn attacker can exfiltrate all stored documents, including potentially sensitive information, in a single request.

FixAdd authentication requirement to the export endpoint, or remove it if not needed. If kept, require admin-level auth and add rate limiting.

high1 finding
src/server.ts
214function handleGetDocument(_req: Request, url: URL, store: Store): Response {
215  const docid = url.pathname.split("/").pop();
216  if (!docid) return jsonError("docid is required");
217
218  const result = store.findDocument(docid, { includeBody: true });
219  if ("error" in result) {
220    return jsonError(`Document not found: ${docid}`, 404);
221  }
222
223  return jsonResponse({
224    docid: result.docid,
225    path: result.displayPath,
226    title: result.title,
227    collection: result.collectionName,
228    modifiedAt: result.modifiedAt,
229    bodyLength: result.bodyLength,
230    body: result.body,
231    context: result.context,
232  });
233}

// Exploitable if MCP is exposed to network (network_exposed) or if a compromised LLM can make HTTP requests to localhost.

The /documents/:docid endpoint returns the full body of any document by its docid. Similarly, /documents?pattern=... returns bodies for all matching documents. These endpoints have no access control beyond optional Bearer token. If the token is not set, any network attacker can read all documents.

ImpactAn attacker can read the full content of any stored document, potentially exposing sensitive information.

FixAdd authentication to all document retrieval endpoints. Consider limiting body exposure or requiring explicit consent.

medium1 finding
src/server.ts
38const API_TOKEN = process.env.CLAWMEM_API_TOKEN || null;
39
40function checkAuth(req: Request): Response | null {
41  if (!API_TOKEN) return null; // No token configured — open access
42  const auth = req.headers.get("authorization");
43  if (!auth || auth !== `Bearer ${API_TOKEN}`) {
44    return jsonResponse({ error: "Unauthorized" }, 401);
45  }
46  return null;
47}

// Exploitable if MCP is exposed to network (network_exposed) or if a compromised LLM can make HTTP requests to localhost.

The server's authentication is optional and based solely on a static Bearer token. When no token is configured, all endpoints are open. Even with a token, the token is static and shared, providing no granular access control. The server exposes powerful endpoints like /export (full database dump), /reindex (reindex all collections), and /lifecycle/sweep (archive/purge documents) that should require elevated privileges.

ImpactAn attacker with network access can perform administrative actions like exporting all data, reindexing, or archiving documents without any authentication.

FixMake authentication mandatory. Implement role-based access control for sensitive endpoints. Use per-user tokens or session-based auth.

medium1 finding
src/server.ts
421async function handleLifecycleRestore(req: Request, _url: URL, store: Store): Promise<Response> {
422  const body = await parseBody<{ query?: string; collection?: string }>(req);
423
424  const filter: { ids?: number[]; collection?: string; sinceDate?: string } = {};
425  if (body?.collection) filter.collection = body.collection;
426
427  const restored = store.restoreArchivedDocuments(filter);
428  return jsonResponse({ restored });
429}

// Exploitable if MCP is exposed to network (network_exposed) or if a compromised LLM can make HTTP requests to localhost.

The /lifecycle/restore endpoint accepts a collection name from the request body without validation. The collection name is passed directly to store.restoreArchivedDocuments, which may use it in SQL queries or file operations. Similarly, /lifecycle/sweep uses the lifecycle policy from config but does not validate the collection name in the request.

ImpactPotential for SQL injection or unintended operations if the collection name is used unsafely in database queries.

FixValidate the collection name against the list of configured collections before using it in any operations.

medium1 finding
src/server.ts
493async function handleReindex(req: Request, _url: URL, store: Store): Promise<Response> {
494  const body = await parseBody<{ collection?: string }>(req);
495
496  const { indexCollection } = await import("./indexer.ts");
497  const collections = listCollections();
498  const targetCollections = body?.collection
499    ? collections.filter(c => c.name === body.collection)
500    : collections;
501
502  if (targetCollections.length === 0) {
503    return jsonError(`Collection not found: ${body?.collection}`, 404);
504  }
505
506  let totalAdded = 0, totalUpdated = 0, totalRemoved = 0;
507
508  for (const coll of targetCollections) {
509    const stats = await indexCollection(store, coll.name, coll.path, coll.pattern);
510    totalAdded += stats.added;
511    totalUpdated += stats.updated;
512    totalRemoved += stats.removed;
513  }
514
515  return jsonResponse({
516    collections: targetCollections.length,
517    added: totalAdded,
518    updated: totalUpdated,
519    removed: totalRemoved,
520  });
521}
src/server.ts:496src/server.ts:509

// Exploitable if MCP is exposed to network (network_exposed) or if a compromised LLM can make HTTP requests to localhost.

The /reindex endpoint accepts a collection name from the request body and uses it to filter collections. While it does check against the configured collections list, the collection name is not validated for path traversal or special characters. The indexCollection function (imported from indexer.ts) likely uses the collection's path to read files from disk. If a malicious collection name could somehow bypass the filter, it could lead to arbitrary file reads.

ImpactPotential for path traversal or arbitrary file indexing if the collection name filter can be bypassed.

FixValidate that the collection name matches expected patterns (alphanumeric, hyphens, underscores) and ensure the filter is strict.

low1 finding
src/server.ts
38const API_TOKEN = process.env.CLAWMEM_API_TOKEN || null;

// Local-only MCP, requires compromised LLM to exploit. Low severity because environment variable exposure typically requires local access.

The server reads the API token from an environment variable. While this is a common pattern, the token is stored in plaintext in the process environment and could be exposed through error messages, debug logs, or process dumps. Additionally, the embedding API key (CLAWMEM_EMBED_API_KEY) is also read from environment and sent as a Bearer token in HTTP requests.

ImpactAn attacker who gains access to the process environment or logs could obtain the API token and use it to authenticate to the server.

FixUse a secrets manager or encrypted storage for credentials. Ensure error messages do not leak sensitive values. Consider using short-lived tokens.

shell.execfilesystem.readfilesystem.writenetwork.httpenv.exposureauth.nonepostgres.access
100
LLM-based
low findings+5
high findings+50
medium findings+45