T02 · Agent Memory Poisoning
Error
- Location
- scripts/segment.py:148
- Finding
- Untrusted Podcast Content Can Poison Persistent Agent Memory<![CDATA[ ## Vulnerability Details **File Location**: `scripts/segment.py:148-186`, `scripts/diary.py:94-115`, `scripts/utils/config.py:174-187` **Vulnerability Type**: Prompt injection leading to persistent memory poisoning **Risk Level**: High ### Vulnerable Code ```python # scripts/segment.py:148-186 max_words = 10000 text_preview = " ".join(transcript_text.split()[:max_words]) prompt = f"""Analyze this podcast transcript and identify distinct topical segments. For each major topic discussed, provide: 1. label (short topic title) 2. key_entities (array of strings) 3. summary (1-2 sentences) 4. information_density (number between 0 and 1) Return strictly a JSON array and nothing else. Transcript: {text_preview} """ model = config.get("model", "gpt-4o-mini") response_text = "" # Preferred OpenAI Python API. try: response = client.responses.create( model=model, input=[{"role": "user", "content": prompt}], temperature=0.2, max_output_tokens=2000, ) response_text = _extract_response_text(response) except Exception: # Backward-compatible fallback for older client surfaces. try: response = client.chat.completions.create( model=model, messages=[{"role": "user", "content": prompt}], temperature=0.2, max_tokens=2000, ) response_text = _extract_response_text(response) ``` ```python # scripts/diary.py:94-115 markdown = f""" ### {entry['show']}: {entry['title']} (WYT: {wyt_percent}%) - **Worth listening:** {novel_text} - **Key topics:** {', '.join(entry['topics_exposed'][:3])} - **Recommended segments:** {len(entry['segments_recommended'])} / {len(entry['segments_recommended']) + len(entry['segments_skipped'])} """ if entry['overlap_flags']: markdown += f"- **Note:** Overlaps with {', '.join(entry['overlap_flags'][:2])}\n" markdown += "\n" save_markdown_note(markdown) ``` ```python # scripts/utils/config.py:174-187 def save_markdown ...[truncated 2801 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Treat transcripts and all model output as untrusted data. 2. Use the provider's structured-output or JSON-schema functionality rather than extracting arbitrary JSON with a regular expression. 3. Impose strict schema constraints on every returned field: - Maximum label and summary lengths. - Allowed character sets. - No line breaks, Markdown, XML, URLs, or instruction-like phrases in labels. - Bounded array sizes and entity lengths. 4. Clearly delimit transcript data and state that content inside the delimiter is data, not instructions. This reduces but does not eliminate prompt-injection risk. 5. Add a deterministic sanitizer before model output reaches diary or memory storage. 6. Store factual diary records in a non-agent-memory data directory unless persistence in agent memory is explicitly required. 7. If Markdown memory is required, encode untrusted values and mark them as quoted external content. 8. Require user confirmation before persisting content derived from an untrusted, manually supplied episode. 9. Record provenance so future consumers can distinguish remote podcast content from trusted user-authored memory. ]]>
