Back to skill

Security audit

SpacetimeDB Memory

Security checks for vulnerabilities and agentic risk

Overview

The skill matches its memory-management purpose, but it can persist, reveal, delete, and migrate sensitive memory files through a configurable database endpoint that needs careful review.

Review before installing. Use this only with a SpacetimeDB endpoint you control, preferably local loopback. Do not set SPACETIMEDB_URL to a remote or plaintext server unless you intend memory content and imported files such as MEMORY.md, IDENTITY.md, USER.md, AGENTS.md, and TOOLS.md to be sent there. Avoid legacy-import unless you have separate backups and have confirmed the database is working.

Vulnerability Patterns
  • Insecure Skill Coding PracticesFinds exploitable flaws such as hardcoded secrets or command injection
  • Skill Instruction HijackingAlters the agent's session goals or safety constraints when the skill loads
  • Agent Memory PoisoningWrites attacker-controlled rules into memory that affect later sessions
  • Remote Payload Retrieval and ExecutionFetches external code whose behavior can change after review
  • Embedded Malicious CodeShips malicious scripts inside the skill and executes them locally
Findings (2)

T09 · Insecure Skill Coding Practices

Warning
Location
legacy-import.js:14
Finding
Long-term memory data can be transmitted to an unrestricted plaintext endpoint<![CDATA[ ## Vulnerability Details **File Location**: `legacy-import.js:14-19, 25-31, 34-55`; `tools/stdb_store.js:7-16, 28`; `tools/stdb_search.js:8-21`; `tools/stdb_edit.js:8-16, 34`; `tools/stdb_forget.js:8-16, 29` **Vulnerability Type**: Unrestricted external endpoint and plaintext transmission of sensitive memory **Risk Level**: Medium ### Relevant Code ```js const filesToImport = [ 'MEMORY.md', 'IDENTITY.md', 'USER.md', 'SOUL.md', 'HEARTBEAT.md', 'AGENTS.md', 'TOOLS.md' ]; const URL = process.env.SPACETIMEDB_URL || 'http://127.0.0.1:3001'; const DB_NAME = process.env.SPACETIMEDB_NAME || 'stdb-memory-1vgys'; async function main() { console.log(`Starting legacy import for workspace: ${workspace}`); let db; await new Promise((resolve, reject) => { const builder = sdk_1.DbConnection.builder() .withUri(URL) .withDatabaseName(DB_NAME) .onConnect(() => resolve()) .onConnectError((ctx, err) => reject(err)); db = builder.build(); }); for (const file of filesToImport) { const fullPath = path.join(workspace, file); if (fs.existsSync(fullPath)) { const content = fs.readFileSync(fullPath, 'utf8'); if (content.includes("migrated to SpacetimeDB")) { console.log(`Skipping ${file} - already migrated`); continue; } console.log(`Importing ${file}...`); const backupPath = `${fullPath}.bak`; fs.copyFileSync(fullPath, backupPath); const id = Date.now().toString() + Math.floor(Math.random() * 1000).toString(); const timestamp = BigInt(Date.now()) * 1000n; const tags = ['legacy', 'import', file.replace('.md', '').toLowerCase()]; try { db.reducers.storeMemory({ id, content: content.trim(), timestamp, tags }); ``` The ordinary storage tool uses the same unrestricted endpoint: ```js const content = args.c ...[truncated 2966 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Enforce loopback-only operation by default: - Resolve the configured hostname and reject non-loopback IPv4 and IPv6 addresses. - Reject redirects or alternate resolved addresses that bypass the restriction. 2. Introduce an explicit remote-storage mode requiring separate, informed user consent. 3. Require TLS-protected transport for every non-loopback endpoint; reject plaintext remote URLs. 4. Authenticate the database server and validate its certificate and expected identity. 5. Display the resolved destination and the categories of data being transferred before legacy migration. 6. Avoid relying solely on inherited environment variables for security-sensitive routing. Prefer a protected configuration file or explicit invocation argument with restrictive permissions. 7. Consider client-side encryption for long-term memory and legacy file contents before transmission. 8. Document clearly that setting a remote endpoint causes memory and imported workspace content to leave the local machine. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
legacy-import.js:47
Finding
Destructive legacy migration overwrites source files without confirming database persistence<![CDATA[ ## Vulnerability Details **File Location**: `legacy-import.js:47-63`; related false-success behavior in `tools/stdb_store.js:28-31`, `tools/stdb_edit.js:34-42`, and `tools/stdb_forget.js:29-37` **Vulnerability Type**: Unverified asynchronous mutation followed by destructive file modification **Risk Level**: Medium ### Relevant Code ```js try { // Store natively db.reducers.storeMemory({ id, content: content.trim(), timestamp, tags }); // Wait for processing await new Promise(r => setTimeout(r, 200)); console.log(`Imported ${file}`); // Overwrite the file fs.writeFileSync(fullPath, `# ${file}\nContent migrated to SpacetimeDB. Use stdb_search.\n`); } catch (err) { console.error(`Failed to import ${file}: ${err.message}`); } ``` The ordinary mutation tools use the same fixed-delay assumption. For example, the edit tool reports success without observing reducer completion: ```js db.reducers.updateMemory({ id: memoryId, content, tags }); // Wait for the server to process the reducer await new Promise(resolve => setTimeout(resolve, 500)); console.log(JSON.stringify({ status: "success", id: memoryId, message: "Memory updated successfully" })); ``` The deletion tool behaves similarly: ```js db.reducers.deleteMemory({ id: memoryId }); // Wait for the server to process the reducer await new Promise(resolve => setTimeout(resolve, 500)); console.log(JSON.stringify({ status: "success", id: memoryId, message: "Memory deleted successfully" })); ``` ### Technical Analysis Calling a reducer initiates an asynchronous database mutation, but the code does not wait for a transaction acknowledgement or verify the resulting database state. Instead, it sleeps for a fixed 200 or 500 milliseconds and assumes success. In the legacy importer, this assumption is security-relevant because the source file is overwritten immediately afterward. A delayed transaction, server-side rejection, authorizati ...[truncated 2283 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Replace fixed delays with the SpacetimeDB SDK's reducer transaction callback, promise, or event that explicitly reports committed or failed status. 2. Before overwriting a source file, query the database and verify: - The expected record ID exists. - Its content or a cryptographic digest matches the source. - Its tags and timestamp are correct. 3. Treat timeouts, disconnects, rejected transactions, and ambiguous states as failures. Preserve the original file in every uncertain case. 4. Implement migration as a two-phase operation: - Phase one imports and verifies every record. - Phase two modifies source files only after the user reviews a complete verification summary. 5. Write replacement files atomically by creating a temporary file in the same directory, syncing it, and renaming it over the original only after verification. 6. Preserve backups without silently replacing existing ones. Use unique, timestamped backup names and verify the backup before modifying the source. 7. Remove forced `process.exit()` calls until pending acknowledgements and cleanup operations have completed. 8. Apply the same acknowledgement requirement to store, edit, and delete tools so they report success only after the server confirms the mutation. 9. Add automated tests covering reducer rejection, delayed acknowledgement, connection loss, duplicate IDs, authorization failures, and restoration from backup. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
Findings (26)

Tp4

High
Category
MCP Tool Poisoning
Confidence
96% confidence
Finding
The declared description describes a substantial memory subsystem with database-backed storage, import tools, and consolidation features. The actual code chunk is merely a `.d.ts` declaration exposing an object shape with `targetTag`. Declaration files describe types and do not implement runtime behavior. Based on this chunk alone, the code does not substantiate the claimed functionality, so the description does not accurately represent what this supplied code actually does.

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding
The declared description presents a complete memory integration system with WebAssembly/SpacetimeDB-backed storage, CRUD, imports, and consolidation. The supplied code chunk is only a minimal auto-generated definition for a reducer/input shape containing one string field (`targetTag`). There is no observable logic for reading/writing memories, importing legacy files, or consolidating data. This is a material mismatch between the broad declared functionality and the actual behavior of the provided code chunk.

Tp4

High
Category
MCP Tool Poisoning
Confidence
93% confidence
Finding
The declared description promises a complete SpacetimeDB memory system with CRUD, file import, and consolidation features. The supplied code chunk is only a minimal autogenerated TypeScript definition for a reducer/input shape with one string field. It does not itself perform database operations, memory replacement, import logic, or consolidation actions. Because the actual code's purpose is much narrower than the declared functionality, the description does not accurately represent this chunk.

Tp4

High
Category
MCP Tool Poisoning
Confidence
94% confidence
Finding
The declared description describes a substantial memory system integration with multiple capabilities and local database behavior. The supplied code chunk does not implement any of that; it is merely a minimal declaration file exposing an object shape with an `id` property. While the filename suggests a delete-memory reducer, the actual content provides no executable delete logic or any evidence of the broader claimed functionality. This is a material mismatch between the declared purpose and the observed code behavior.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding
The declared description promises substantial functionality centered on a local SpacetimeDB memory system with multiple features. However, the provided code chunk is merely a minimal declaration stub (`declare const _default: any; export default _default;`) and does not demonstrate any of those behaviors. Based on the supplied code alone, the actual behavior is just re-exporting an untyped default symbol, so the description materially overstates what this code chunk does.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding
The declared description promises a substantial memory integration system with database-backed storage, CRUD operations, import tools, and consolidation features. The supplied code chunk does not implement any of that behavior. It is only a .d.ts declaration defining/exporting an object with four properties: id, content, timestamp, and tags. This may be a supporting type definition for memory records, but by itself it does not demonstrate the described functionality. Therefore, the description materially overstates what this code chunk actually does.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding
The declared description presents a complete memory integration system with CRUD operations, import support, and consolidation features. The actual code chunk does not implement those behaviors; it merely declares the data shape for a SpacetimeDB-backed memory record. While this schema is consistent with part of a SpacetimeDB memory system, it does not substantiate the broader claimed functionality. Therefore, the description overstates what this supplied code chunk actually does.

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding
The description advertises a substantial feature set and concrete runtime behavior, but the supplied code chunk is only a type declaration file. It defines an exported `Memory` symbol and inferred type from `spacetimedb`, which is consistent with schema/type exposure but does not implement the described memory backend or any of the claimed capabilities. This is a material description-to-code mismatch because the actual code shown is declarative typing only, not the described integration or tooling.

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding
The description advertises substantial functionality around a local SpacetimeDB memory system, CRUD support, imports, and tooling. The provided code chunk contains no such behavior; it is just an empty .d.ts file with no operational logic. This is a material mismatch in primary purpose and implemented capabilities.

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding
The declared description promises a substantial memory integration feature set centered on SpacetimeDB, including CRUD operations, legacy imports, and consolidation utilities. However, the provided code chunk is only an auto-generated placeholder file with no actual procedure schemas or implementation logic. It does not demonstrate the claimed primary purpose or capabilities. Based on this code alone, the description materially overstates what the supplied code does, so this is a mismatch.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding
The description advertises a complete memory integration system with CRUD, import, and consolidation features. The provided code chunk does not implement any of those behaviors; it is merely a generated type-definition scaffold for procedures. Since the actual code shown has no substantive functionality matching the declared purpose, the description is not accurately represented by this chunk.

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding
The declared description promises substantial memory-system functionality backed by SpacetimeDB, including CRUD, imports, and consolidation. The supplied code chunk does not implement any of that; it is effectively an empty generated file with no reducers or operational behavior. Because the actual code does not reflect the claimed primary purpose or capabilities, this is a clear description-behavior mismatch.

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding
The declared description promises a substantial memory integration subsystem with database-backed storage, WebAssembly acceleration, CRUD operations, import support, and consolidation tools. The supplied code chunk does not implement any of that behavior. It is only a `.d.ts` declaration exporting an object shape containing `id`, `content`, and `tags`. While this may be a small supporting type related to memory updates, on its own it does not substantiate the declared purpose and is materially different in scope and behavior from the described feature set.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding
The declared description promises a substantial memory subsystem with database-backed storage and multiple operational capabilities. However, the provided code chunk is just an empty declaration module (`export {}`) with no logic. This is a material mismatch because the actual code does not implement, expose, or evidence any of the claimed behavior. There are no triggers or permissions to compare, but the primary purpose described is not represented by this code at all.

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding
The declared description promises a substantial memory integration with multiple concrete capabilities, but the provided code chunk contains no functional code at all—only an empty TypeScript declaration module export. Because the actual code does not implement or demonstrate any of the advertised behavior, the description does not accurately represent this code chunk.

Tp4

High
Category
MCP Tool Poisoning
Confidence
93% confidence
Finding
The code chunk implements a narrow SpacetimeDB write tool: it parses input arguments, connects to SpacetimeDB, invokes `storeMemory`, waits briefly, prints success JSON, and exits. That aligns with part of the description about SpacetimeDB-backed memory storage, but it does not demonstrate the fuller declared functionality such as CRUD, legacy imports, or consolidation tools. Because the description makes substantial capability claims not reflected in the supplied code chunk, the description does not accurately represent this specific code's actual behavior.

Lp3

Medium
Category
MCP Least Privilege
Confidence
70% confidence
Finding
Without declared permissions the skill's intent is opaque and cannot be validated.

Missing User Warnings

Medium
Confidence
96% confidence
Finding
The code invokes a delete operation on stored memory data via `db.reducers.deleteMemory({ id: memoryId })`, which is an irreversible, safety-relevant action. There is no confirmation prompt, prior warning, or explanatory comment/docstring disclosing that this script deletes persisted data before performing the operation.

Missing User Warnings

Medium
Confidence
92% confidence
Finding
The search tool prints full matching memory records, including raw content and tags, directly to stdout with no access control, redaction, or user warning. In a memory integration skill, those records are likely to contain sensitive prompts, notes, or operational context, so broad disclosure through a simple query interface can expose confidential data to any caller that can invoke the tool.

Missing User Warnings

Medium
Confidence
91% confidence
Finding
This code transmits user-provided `content` and `tags` to a local or configured SpacetimeDB instance via `db.reducers.storeMemory(...)` without any user-facing notice, consent flow, or visible data-handling disclosure in the file. Even though the default endpoint is localhost, the destination is configurable through environment variables, so the same code path can silently send potentially sensitive memory contents to a different service.

Missing User Warnings

Medium
Confidence
86% confidence
Finding
This code performs a persistent write operation by storing `content` and `tags` via `db.reducers.storeMemory`, but the file contains no confirmation prompt, disclosure message, or explanatory comment indicating that user data will be saved. Because the operation writes potentially sensitive user-provided data to long-term storage, a warning or explicit disclosure is expected.

Known Vulnerable Dependency: esbuild==0.27.3 — 1 advisory(ies): GHSA-g7r4-m6w7-qqqr (esbuild allows arbitrary file read when running the development server on Window)

Low
Category
Supply Chain
Confidence
95% confidence
Finding
The lockfile pins esbuild to 0.27.3, which is reported as affected by GHSA-g7r4-m6w7-qqqr. This package is only present as a development dependency via tsx, and the cited issue requires running esbuild's development server on Windows, so the risk is contextual and likely limited for this skill, but the vulnerable version is still genuinely present.

Unpinned Dependencies

Low
Category
Supply Chain
Content
"author": "",
  "license": "ISC",
  "dependencies": {
    "spacetimedb": "^2.0.3"
  },
  "devDependencies": {
    "@types/node": "^25.4.0",
Confidence
92% confidence
Finding
The runtime dependency uses a caret range, which permits automatic installation of newer minor/patch releases. If the upstream package is compromised or introduces a malicious or breaking update, installations of this skill could pull that version without review, creating a supply-chain risk in code that will run locally during use.

Unpinned Dependencies

Low
Category
Supply Chain
Content
"spacetimedb": "^2.0.3"
  },
  "devDependencies": {
    "@types/node": "^25.4.0",
    "tsx": "^4.21.0",
    "typescript": "^5.9.3"
  },
Confidence
86% confidence
Finding
The development dependency is not pinned to an exact version, so builds or local development may consume unexpected upstream updates. While this is less dangerous than an unpinned runtime dependency, it still creates supply-chain exposure if a malicious or compromised release is published.

Unpinned Dependencies

Low
Category
Supply Chain
Content
},
  "devDependencies": {
    "@types/node": "^25.4.0",
    "tsx": "^4.21.0",
    "typescript": "^5.9.3"
  },
  "openclaw": {
Confidence
86% confidence
Finding
This devDependency allows version drift via a caret range, which can introduce unreviewed code into developer or CI environments. Tooling packages can be attractive supply-chain targets because they execute during development workflows.

Static analysis

No suspicious patterns detected.