T02 · Agent Memory Poisoning
- Location
scripts/rollup-monthly.py:48- Finding
Untrusted diary content can poison persistent agent memory
- Content
View full analysis
Vulnerability Details
File Locations:
scripts/append_to_daily.py:29-43scripts/rollup-daily.py:68-108scripts/rollup-daily.py:166-186scripts/rollup-weekly.py:62-78scripts/rollup-weekly.py:91-109scripts/rollup-monthly.py:48-74scripts/rollup-monthly.py:80-95SKILL.md:54-65
Vulnerability Type: Persistent memory poisoning through unsanitized content promotion
Risk Level: HighVulnerable Code
scripts/append_to_daily.py:29-43:python daily_dir = DIARY_ROOT / year / "daily" daily_dir.mkdir(parents=True, exist_ok=True) today_path = daily_dir / f"{date}.md" # Create file if it doesn't exist if not today_path.exists(): weekday = now.strftime("%A") today_path.write_text(f"# {date} ({weekday})\n\n", encoding="utf-8") # Append event event = f"\n## {time_str} - {title}\n" if details: event += f"{details}\n" with open(today_path, "a", encoding="utf-8") as f: f.write(event)scripts/rollup-daily.py:101-108:python # Call OpenClaw's LLM via subprocess result = subprocess.run( ['openclaw', 'ask', '--model', LLM_MODEL, '--'], input=prompt, capture_output=True, text=True, timeout=30 )The
promptpassed above embeds the entire diary through the following interpolation atscripts/rollup-daily.py:73-75:python **Original log:** {content}scripts/rollup-weekly.py:62-78:python for line in lines: line = line.strip() if not line or line.startswith('#'): continue # Extract milestones if any(marker in line.lower() for marker in ['created', 'implemented', 'completed', 'launched', 'published']): milestones.append(line) # Extract themes elif any(marker in line.lower() for marker in ['learned', 'discovered', 'realized', 'understood']): themes.append(line) result = [] if themes: result.append ...[truncated 3425 chars]- Remediation
View remediation
Remediation Suggestions
- Treat all diary entries and rollup summaries as untrusted data.
- Store events in a structured format with separate fields for content, source identity, creation time, trust level, and approval status.
- Explicitly instruct the summarization model that log contents are quoted data and that instructions found inside them must never be followed.
- Enforce an output schema that permits factual summaries but rejects commands, policy statements, tool instructions, and behavioral directives.
- Add detection for prompt-injection phrases and suspicious imperative language.
- Require explicit human approval before any text is promoted to
MEMORY.md. - Preserve source references for every promoted item so it can be traced to its original event.
- Load memory through a data-only channel rather than presenting it as authoritative session instructions.
- Add regression tests using malicious diary entries to verify that instructions cannot propagate into core memory.
