T02 · Agent Memory Poisoning
Error
- Location
- scripts/working_buffer.py:62
- Finding
- Untrusted Conversation Content Is Persisted and Reintroduced as Authoritative Agent Memory<![CDATA[ ## Vulnerability Details **File Location**: `scripts/working_buffer.py:62-72`, `scripts/wal_protocol.py:132-141`, and `scripts/compaction_recovery.py:157-169` **Vulnerability Type**: Persistent memory poisoning through unsanitized conversation logging **Risk Level**: High ### Vulnerable Code `scripts/working_buffer.py:62-72`: ```python def append_human_message(message: str, timestamp: str = None) -> str: """Append a human message to the working buffer.""" if not timestamp: timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S") if not WORKING_BUFFER_FILE.exists(): init_buffer() entry = f"## [{timestamp}] Human\n{message}\n\n" with open(WORKING_BUFFER_FILE, 'a', encoding='utf-8') as f: f.write(entry) return entry ``` `scripts/wal_protocol.py:132-141`: ```python # Append WAL entry wal_section = f"\n## WAL Entry [{timestamp}]\n" wal_section += f"**Triggers:** {', '.join(triggers)}\n" wal_section += f"**Human:** {human_message[:150]}{'...' if len(human_message) > 150 else ''}\n" if details: wal_section += "**Extracted:**\n" for key, values in details.items(): wal_section += f"- {key}: {', '.join(values[:5])}\n" wal_section += "\n---\n" # Write updated state new_content = content + wal_section state_file.write_text(new_content, encoding='utf-8') ``` `scripts/compaction_recovery.py:157-169`: ```python # Step 1: Read working buffer FIRST buffer_content = read_working_buffer() if buffer_content: recovery['sources_checked'].append('working_buffer') recovery['recovered_context']['working_buffer'] = extract_context_from_buffer(buffer_content) # Step 2: Read session state session_state = read_session_state() if session_state: recovery['sources_checked'].append('session_state') recovery['recovered_context']['session_state'] = session_state[:2000] # Tru ...[truncated 2687 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Store conversation records as structured JSON rather than executable-looking Markdown: ```json { "type": "untrusted_user_quote", "trust": "untrusted", "content": "..." } ``` 2. Ensure recovery prompts explicitly delimit stored content and state that it is historical data, not instructions. 3. Escape or encode Markdown headings, XML-like tags, role markers, and instruction delimiters before persistence. 4. Do not promote raw messages into authoritative state. Extract proposed facts into a staging area and require validation or user confirmation. 5. Associate each memory entry with its source, session trust level, author, and creation time. 6. Disable persistent capture for public, shared, or low-trust channels. 7. Apply prompt-injection screening before storage and again before recovered content is supplied to an agent. 8. Allow recovery code to return only structured facts needed for the current task rather than arbitrary message text. 9. Add tests demonstrating that stored strings such as “ignore previous instructions” remain quoted data and cannot alter recovery behavior. ]]>
