T02 · Agent Memory Poisoning
- Location
scripts/append_lesson.py:35- Finding
Persistent Indirect Prompt Injection Through Agent Lesson Storage
- Content
View full analysis
Vulnerability Details
File Location:
scripts/append_lesson.py:35-42,scripts/append_lesson.py:54-62,SKILL.md:154-165,CLAWHUB.md:42-46
Vulnerability Type: Persistent indirect prompt injection and memory poisoning
Risk Level: MediumVulnerable Code
scripts/append_lesson.py:35-42:python tags_str = ", ".join([t.strip() for t in tags.split(",")]) if tags else "" fix_applied = "true" if fix_target and fix_edit else "false" fix_plan = "" if fix_target and fix_edit: fix_plan = f"**Target:** `{fix_target}`\n**Edit:** `{fix_edit}`" content = content.format(date=date_str, tags=tags_str, task=task, expected=expected, actual=actual, why=why, lesson=lesson, fix_applied=fix_applied, fix_plan=fix_plan) filepath.write_text(content) print(f"Created lesson file: {filepath}")scripts/append_lesson.py:54-62:python content = LESSONS_INDEX.read_text() tags_list = [t.strip() for t in tags.split(",")] if tags else [] tags_str = ", ".join(tags_list) new_entry = f"## {date_str} - {task}\n- Task: {task}\n- Lesson: {lesson}\n- Detail: [lessons/{filename}](lessons/{filename})\n- Tags: {tags_str}\n" entry_header = f"## {date_str} - {task}" if entry_header in content: print(f"Entry already exists in index, skipping") return lines = content.split('\n')SKILL.md:154-165:markdown ## Loading lessons next session Two ways to make lessons automatic: 1. **Project-local:** AGENTS.md has pointer: "Read LESSONS.md before starting work." 2. **On demand:** Before similar task, run `python3 scripts/append_lesson.py --search <keyword>` A lessons file nobody loads is a diary, not a loop. The loop only closes when the next session actually reads it before acting.CLAWHUB.md:42-46:markdown ## Integration Add to AGENTS.md or project instructions:Read LESSONS.md before starting work on tasks similar to past ones. Run `python3 skil ...[truncated 2622 chars]
- Remediation
View remediation
Remediation Suggestions
- Treat all lesson fields as untrusted data rather than agent instructions.
- Store structured records in JSON or another format with explicit data boundaries instead of interpolating raw values into instruction-bearing Markdown.
- If Markdown remains necessary, quote or fence every user-controlled field and escape YAML, Markdown heading, link, and code-fence syntax.
- Add a fixed warning before loaded entries stating that lesson content is untrusted historical data and must never override system, developer, user, or safety instructions.
- Require explicit human approval before adding a lesson to
LESSONS.mdor another file automatically loaded by agents. - Separate raw task observations from reviewed operational rules. Only reviewed, normalized lessons should be promoted into agent-readable memory.
- Detect instruction-like or high-risk content, including requests involving secrets, policy overrides, command execution, or external communication, and quarantine it for review.
- Configure future agents to use stored lessons only as non-authoritative reference material and to validate each recommendation against current trusted instructions.
