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. ]]>
