T02 · Agent Memory Poisoning
Error
- Location
- plugin/index.ts:89
- Finding
- Persistent Prompt Injection Through Untrusted Memory Content<![CDATA[ ## Vulnerability Details **File Location**: `plugin/index.ts:89-120` **Vulnerability Type**: Persistent prompt injection through long-term memory **Risk Level**: High ### Vulnerable Code ```ts // Load memory.org + today/yesterday daily so the agent starts with context. const memoryOrg = await readOrgFile(join(cfg.dir, "memory.org"), MAX_FILE_BYTES); if (memoryOrg) { parts.push(`<org-memory-file path="memory.org">\n${memoryOrg}\n</org-memory-file>`); } const today = todayStr(); const yesterday = yesterdayStr(); const todayContent = await readOrgFile( join(cfg.dir, "daily", `${today}.org`), MAX_FILE_BYTES, ); if (todayContent) { parts.push( `<org-memory-file path="daily/${today}.org">\n${todayContent}\n</org-memory-file>`, ); } const yesterdayContent = await readOrgFile( join(cfg.dir, "daily", `${yesterday}.org`), MAX_FILE_BYTES, ); if (yesterdayContent) { parts.push( `<org-memory-file path="daily/${yesterday}.org">\n${yesterdayContent}\n</org-memory-file>`, ); } return { prependContext: `<org-memory>\n${parts.join("\n")}\n</org-memory>`, }; ``` The affected memory can be populated through mutation tools such as `org_memory_add_note`, `org_memory_append`, and `org_memory_roam_upsert`. For example, `org_memory_add_note` accepts caller-controlled text and a caller-selected file: ```ts parameters: Type.Object({ text: Type.String({ description: "Note text (becomes the headline title)" }), file: Type.Optional( Type.String({ description: "Filename relative to the workspace dir (default: inboxFile)", }), ), }), async execute(_id, params) { const typed = params as { text: string; file?: string }; const args = buildAddNoteArgs(cfg, typed); try { const { stdout } = await runOrg(cfg.orgBin, args); ``` ### Technical Analysis The `before_agent_start` hook reads `memory.org` and recent daily notes and inserts their contents verbatim into the agent's prepended session context. The stored content is pla ...[truncated 1971 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Treat every memory file as untrusted data, not as an instruction source. 2. Use a framework-supported structured data or retrieval channel that does not merge stored content into privileged instructions. 3. If contextual interpolation is unavoidable, serialize memory using a robust structured encoding and escape all delimiters. Do not rely on XML-like tags alone. 4. Add an immutable instruction outside the interpolated content stating that instructions, tool requests, and policy claims found in memory are quoted data and must never be followed. 5. Detect and reject or quarantine instruction-like entries, role markers, delimiter-closing strings, and other prompt-injection patterns before persistence. 6. Require explicit user confirmation before storing externally supplied or instruction-like content in files loaded automatically at session start. 7. Track provenance for every memory item and distinguish user-authored, agent-generated, and externally sourced content. 8. Load only narrowly relevant memory through retrieval rather than automatically injecting whole files. 9. Add regression tests using closing tags, role markers, and malicious instructions to verify that stored content cannot escape its data boundary. ]]>
