Back to skill

Security audit

Zouroboros Memory

Security checks for vulnerabilities and agentic risk

Overview

This memory skill largely matches its purpose, but it stores sensitive long-term agent data and can send memory or queries to configurable LLM and embedding services without enough scoping or privacy controls.

Install only if you are comfortable treating this as a sensitive local memory database. Use a private database path with restrictive permissions, avoid storing secrets or regulated data, pin npm/npx versions, keep OLLAMA_URL pointed only at a trusted local or HTTPS service, and enable OpenAI/reranker/HyDE features only when you explicitly accept sending query and memory snippets to that provider.

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
src/embeddings.ts:94
Finding
Persistent memory content can be transmitted to an unrestricted or unencrypted embedding endpoint<![CDATA[ ## Vulnerability Details **File Location**: `src/embeddings.ts:94-108`, with data originating from `src/facts.ts:69-108` and endpoint configuration from `src/cli.ts:28-35` **Vulnerability Type**: Sensitive-data exposure through an unrestricted network destination **Risk Level**: Medium ### Vulnerable Code ```ts // src/embeddings.ts:94-108 async function _generateEmbeddingFromOllama( text: string, config: MemoryConfig, ): Promise<number[]> { const response = await fetch(`${config.ollamaUrl}/api/embeddings`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ model: config.ollamaModel, prompt: text }), }); if (!response.ok) { throw new Error(`Ollama error: ${response.status} ${response.statusText}`); } const data = await response.json() as { embedding: number[] }; return data.embedding; } ``` The transmitted text is assembled directly from persistent memory fields: ```ts // src/facts.ts:69-75 const text = input.key ? `${input.entity} ${input.key} ${input.value}` : `${input.entity} ${input.value}`; const entry: MemoryEntry = { id, entity: input.entity, ``` Embedding generation occurs automatically when vector search is enabled: ```ts // src/facts.ts:104-115 // Generate and store embedding if vector search is enabled if (config.vectorEnabled) { try { const embedding = await generateEmbedding(text, config); const serialized = serializeEmbedding(embedding); db.run( 'INSERT INTO fact_embeddings (fact_id, embedding, model) VALUES (?, ?, ?)', [id, serialized, config.ollamaModel] ); } catch (error) { ``` The CLI accepts an environment-controlled destination and enables transmission whenever that variable is present: ```ts // src/cli.ts:28-35 const DEFAULT_DB = `${process.env.HOME ?? '~'}/.zouroboros/memory.db`; const DEFAULT_CONFIG: MemoryConfig = { enabled: true, dbPath: process.env.ZO_MEMORY_DB ?? DEFAULT_DB, vectorEnabled: !!(proces ...[truncated 2770 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Parse `ollamaUrl` with the standard `URL` class and reject unsupported protocols, embedded credentials, malformed URLs, and unexpected URL components. 2. Permit plain HTTP only for loopback destinations such as `127.0.0.1`, `::1`, and a carefully resolved `localhost`. 3. Require HTTPS for all non-loopback destinations. 4. Add an explicit configuration option such as `allowRemoteEmbedding: true`; do not infer consent solely from the presence of an environment variable. 5. Support a destination allowlist and validate the resolved IP address to reduce DNS-rebinding and internal-network targeting risks. 6. Clearly document that enabling embeddings transmits complete entity, key, value, and query text to the selected service. 7. Consider configurable redaction or field selection so callers can omit sensitive fields from embedding input. 8. Add request-size limits, response-schema validation, embedding-dimension limits, and timeouts to all embedding and generation calls. 9. Log the selected provider and normalized destination without logging prompts or credentials, enabling operators to detect unexpected routing. 10. Add automated tests proving that remote HTTP URLs are rejected and that remote transmission requires explicit consent. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
src/database.ts:109
Finding
Plaintext memory database and sidecar files are created without explicit restrictive permissions<![CDATA[ ## Vulnerability Details **File Location**: `src/database.ts:109-117` **Vulnerability Type**: Insecure local storage permissions for sensitive persistent data **Risk Level**: Medium ### Vulnerable Code ```ts // src/database.ts:109-117 export function initDatabase(config: MemoryConfig): Database { if (db) return db; const dir = dirname(config.dbPath); if (!existsSync(dir)) { mkdirSync(dir, { recursive: true }); } db = new Database(config.dbPath); db.exec('PRAGMA journal_mode = WAL'); db.exec(SCHEMA_SQL); ``` The schema stores memory values and metadata in plaintext: ```ts // src/database.ts:15-31 CREATE TABLE IF NOT EXISTS facts ( id TEXT PRIMARY KEY, persona TEXT, entity TEXT NOT NULL, key TEXT, value TEXT NOT NULL, text TEXT NOT NULL, category TEXT DEFAULT 'fact' CHECK(category IN ('preference', 'fact', 'decision', 'convention', 'other', 'reference', 'project')), decay_class TEXT DEFAULT 'medium' CHECK(decay_class IN ('permanent', 'long', 'medium', 'short')), importance REAL DEFAULT 1.0, source TEXT, created_at INTEGER DEFAULT (strftime('%s', 'now')), expires_at INTEGER, last_accessed INTEGER DEFAULT (strftime('%s', 'now')), confidence REAL DEFAULT 1.0, metadata TEXT ); ``` ### Technical Analysis The Skill creates the database directory recursively without specifying a restrictive mode and opens the SQLite database without subsequently enforcing file permissions. Consequently, directory and file access depends on the process umask and permissions of pre-existing parent directories. The database contains long-lived user facts, preferences, project context, episode summaries, procedure data, and cognitive profiles in plaintext. SQLite WAL mode can additionally create `-wal` and `-shm` sidecar files containing database pages or synchronization data. Those files are not explicitly protected either. On systems with permissive umasks, shared service accounts, containers with shared volumes, or operat ...[truncated 1908 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Create the database directory with mode `0700`: ```ts mkdirSync(dir, { recursive: true, mode: 0o700 }); ``` 2. After creation, verify ownership and apply `chmodSync(dir, 0o700)` where supported. 3. Enforce mode `0600` on the primary database after opening it. 4. Account for SQLite WAL and SHM sidecar files. Verify or enforce restrictive permissions after enabling WAL and during initialization. 5. Before opening the path, use `lstat` and ownership checks to reject unsafe symbolic links, non-regular files, or directories owned by unexpected principals. 6. Warn or fail when the database is located in a world-readable or world-writable directory. 7. Document that memory is stored in plaintext and that callers must select a private storage location. 8. Consider optional encryption at rest for deployments that store credentials, personal data, or confidential project context. 9. Add tests that initialize the database under a permissive umask and verify that the directory, database, WAL, and SHM files remain inaccessible to group and other users. 10. Provide platform-specific handling or documentation where POSIX permission bits are unavailable. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
Findings (58)

Tp4

High
Category
MCP Tool Poisoning
Confidence
94% confidence
Finding
The declared purpose presents the skill as a memory subsystem, but the analyzed behavior reportedly includes external model/API calls, prompt routing, and use of environment-based credentials that are not disclosed in the description. This mismatch can cause users to trust and install the skill under false assumptions, increasing the risk of unintended data egress, hidden external dependencies, and secret exposure when memory contents are sent to remote models.

Missing User Warnings

Medium
Confidence
92% confidence
Finding
The README prominently advertises persistent storage of facts, episodic memory, cognitive profiles, and interaction history but does not clearly warn about privacy, retention, or sensitive-data handling. In an agent-memory product, users may store personal data, secrets, or behavioral profiles without understanding the long-lived storage and retrieval implications, increasing the chance of privacy harm and policy violations.

Rp1

Medium
Category
MCP Rug Pull
Confidence
94% confidence
Finding
The README instructs users to execute a package via `npx` without pinning an exact version, which allows whatever version is currently published under that name to be fetched and run. If the package is compromised, typo-squatted, or a malicious update is published, users may execute attacker-controlled code directly on their system.

Rp1

Medium
Category
MCP Rug Pull
Confidence
94% confidence
Finding
This command example uses unpinned `npx` execution, causing users to download and run the latest published package at execution time. That creates a supply-chain execution path where a malicious or compromised release could immediately run arbitrary code on a developer or agent host.

Rp1

Medium
Category
MCP Rug Pull
Confidence
94% confidence
Finding
The README's `npx zouroboros-memory search` example runs a remote package without version pinning. In a package that targets agent memory and MCP workflows, this is especially risky because users are likely to run it in environments containing sensitive local data and credentials.

Rp1

Medium
Category
MCP Rug Pull
Confidence
94% confidence
Finding
This unpinned `npx` hybrid-search example creates the same supply-chain risk as the other CLI snippets: users may execute whichever package version is current at runtime. An attacker controlling publication or dependency resolution could abuse this to execute arbitrary code in the user's environment.

Rp1

Medium
Category
MCP Rug Pull
Confidence
94% confidence
Finding
The `stats` command is another instance of unpinned `npx` execution, which normalizes unsafe package execution practices for users. Even seemingly harmless commands can execute arbitrary install-time or runtime code before showing output.

Rp1

Medium
Category
MCP Rug Pull
Confidence
94% confidence
Finding
This `prune` example again relies on unpinned `npx`, exposing users to arbitrary code execution through a compromised package release. Because the tool manages persistent memory storage, users may run it on systems that contain high-value agent state and local databases.

Rp1

Medium
Category
MCP Rug Pull
Confidence
94% confidence
Finding
The bulk import example uses unpinned `npx`, which can lead to execution of a maliciously updated package at the moment the user imports data. In this context, the tool may process sensitive memory facts, so compromise could expose or tamper with private stored data.

Lp3

Medium
Category
MCP Least Privilege
Confidence
81% confidence
Finding
The skill advertises executable capabilities through Node/npm usage and an MCP server, and the package behavior reportedly includes network and environment access, but the manifest does not declare any explicit tool scope such as permissions or allowed-tools. That weakens least-privilege controls and makes it harder for users or hosts to understand and constrain what the skill may access at runtime.

Missing User Warnings

Medium
Confidence
94% confidence
Finding
This skill is specifically designed for persistent memory storage, which can naturally accumulate sensitive user data, but the markdown provides no warning about privacy, retention, or local database protection. In context, that omission is more dangerous because users may store credentials, personal data, or proprietary information without understanding that it persists on disk and may also be queried or transmitted by integrations.

Rp1

Medium
Category
MCP Rug Pull
Confidence
93% confidence
Finding
Using npx to execute an unpinned package name allows the resolved code to change over time based on the latest published version or registry state. This creates a supply-chain risk where a compromised new release, typosquatted dependency path, or unexpected breaking change could execute arbitrary code in the agent environment.

Rp1

Medium
Category
MCP Rug Pull
Confidence
93% confidence
Finding
Using npx to execute an unpinned package name allows the resolved code to change over time based on the latest published version or registry state. This creates a supply-chain risk where a compromised new release, typosquatted dependency path, or unexpected breaking change could execute arbitrary code in the agent environment.

Rp1

Medium
Category
MCP Rug Pull
Confidence
93% confidence
Finding
Using npx to execute an unpinned package name allows the resolved code to change over time based on the latest published version or registry state. This creates a supply-chain risk where a compromised new release, typosquatted dependency path, or unexpected breaking change could execute arbitrary code in the agent environment.

Rp1

Medium
Category
MCP Rug Pull
Confidence
93% confidence
Finding
Using npx to execute an unpinned package name allows the resolved code to change over time based on the latest published version or registry state. This creates a supply-chain risk where a compromised new release, typosquatted dependency path, or unexpected breaking change could execute arbitrary code in the agent environment.

Rp1

Medium
Category
MCP Rug Pull
Confidence
93% confidence
Finding
Using npx to execute an unpinned package name allows the resolved code to change over time based on the latest published version or registry state. This creates a supply-chain risk where a compromised new release, typosquatted dependency path, or unexpected breaking change could execute arbitrary code in the agent environment.

Missing User Warnings

Medium
Confidence
89% confidence
Finding
The documented tools include deletion and pruning operations, but there is no warning about irreversible data loss, scope of deletion, or safeguards such as confirmation, backup, or dry-run behavior. In a memory system, destructive actions directly affect long-term agent state and can silently remove important or sensitive records, making operational mistakes more likely.

Missing User Warnings

Medium
Confidence
94% confidence
Finding
The code sends arbitrary input text to `config.ollamaUrl/api/embeddings` for embedding generation without any visible consent, disclosure, or restriction on the destination. In a persistent-memory skill, that text may contain sensitive prompts, memories, or user data, so forwarding it to a configurable HTTP endpoint can expose private content to another service or host.

Missing User Warnings

Medium
Confidence
93% confidence
Finding
The hypothetical-answer generation path interpolates the raw user query into a prompt and transmits it to `config.ollamaUrl/api/generate` with no visible warning or trust boundary enforcement. Because this skill is a memory component, queries may include sensitive personal or operational context, increasing the risk of unintended disclosure to an external or misconfigured model endpoint.

Description-Behavior Mismatch

Medium
Confidence
94% confidence
Finding
Although the skill is presented as persistent memory, this file also includes outbound LLM-backed reranking and query expansion paths via OpenAI/Ollama. That materially expands the trust boundary: stored or queried memory content may leave the local environment, which is risky for a memory component likely to hold sensitive agent context.

Context-Inappropriate Capability

Medium
Confidence
89% confidence
Finding
The code reads OpenAI API credentials from environment variables and uses them to transmit prompts to a third-party service. For a skill whose primary purpose is local persistent memory, undisclosed credential use for remote inference increases data exposure and can surprise operators who expect a local-only storage component.

External Transmission

Medium
Category
Data Exfiltration
Content
const messages = [];
  if (opts.system) messages.push({ role: "system", content: opts.system });
  messages.push({ role: "user", content: opts.prompt });
  const resp = await fetch("https://api.openai.com/v1/chat/completions", {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
Confidence
95% confidence
Finding
The hardcoded OpenAI endpoint confirms that this component can send data to a third-party service. Given the skill’s memory-storage role, this undisclosed external dependency widens the attack surface and creates privacy/compliance exposure if sensitive memory is included in prompts.

External Transmission

Medium
Category
Data Exfiltration
Content
const messages = [];
  if (opts.system) messages.push({ role: "system", content: opts.system });
  messages.push({ role: "user", content: opts.prompt });
  const resp = await fetch("https://api.openai.com/v1/chat/completions", {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
Confidence
95% confidence
Finding
The hardcoded OpenAI endpoint confirms that this component can send data to a third-party service. Given the skill’s memory-storage role, this undisclosed external dependency widens the attack surface and creates privacy/compliance exposure if sensitive memory is included in prompts.

Missing User Warnings

Medium
Confidence
92% confidence
Finding
The OpenAI request sends user/system prompt content off-host with no user-facing disclosure in this file. In this skill, prompts may include memory queries, expansions, or context derived from stored agent data, so the lack of disclosure and gating creates a meaningful privacy and data-governance risk.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
The reranking path packages memory search results into a prompt and sends passage previews to an external LLM when reranking is enabled. Because these passages are drawn from stored memory, this can exfiltrate sensitive facts, profiles, or operational context without any confirmation, redaction, or policy gate.

Static analysis

Detected: suspicious.env_credential_access

Environment variable access combined with network send.

Critical
Code
suspicious.env_credential_access
Location
dist/chunk-CIYBIABX.js:317

Environment variable access combined with network send.

Critical
Code
suspicious.env_credential_access
Location
dist/index.js:704

Environment variable access combined with network send.

Critical
Code
suspicious.env_credential_access
Location
src/llm.ts:44