T02 · Agent Memory Poisoning
- Location
distill_logic.py:174- Finding
Untrusted Trace Content Is Persisted as Executable Procedural Memory
- Content
View full analysis
Vulnerability Details
File Location:
distill_logic.py:174-197, 240, 290-295
Vulnerability Type: Persistent agent-memory poisoning through unsanitized trace content
Risk Level: HighVulnerable Code
python def extract_success_pattern(events: list[TraceEvent]) -> list[str]: lines: list[str] = [] seen: set[str] = set() for event in events: if not event.ok: continue candidate = f"- {event.summary}" if candidate in seen: continue seen.add(candidate) lines.append(candidate) return lines def extract_failure_triggers(events: list[TraceEvent]) -> list[str]: lines: list[str] = [] for event in events: if event.ok: continue detail = event.error or event.detail lines.append(f"- Avoid `{event.summary}` because {detail}") return lines or ["- No critical failed attempts were preserved in the inspected window."] def extract_snippets(events: list[TraceEvent]) -> list[str]: snippets: list[str] = [] for event in events: if event.snippet: label = event.raw.get("path") or event.raw.get("file") or event.tool snippets.append(f"### {label}\n```\n{event.snippet}\n```") return snippets[:5]The resulting skill explicitly instructs future agents to replay the stored material:
python "Replay the preserved commands and file edits in order. Prefer this skill when the task looks materially similar, and update the utility score after future uses if the workflow proves more or less effective.",The generated files are then persisted:
python (destination / "SKILL.md").write_text(markdown, encoding="utf-8") (agents_dir / "openai.yaml").write_text(build_openai_yaml(task), encoding="utf-8") (destination / "memory.json").write_text( json.dumps(memory_payload, indent=2, ensure_ascii=False) + "\n", encoding="utf-8", )Technical Analysis
Commands, paths, errors, labels, ...[truncated 3358 chars]
- Remediation
View remediation
Remediation Suggestions
- Treat all trace fields as untrusted data, including task names, commands, paths, URLs, errors, snippets, diffs, and patches.
- Do not convert raw trace content directly into operational instructions. Store evidence in a structured, inert format and require a separate trusted synthesis step.
- Require explicit user review and confirmation before placing generated content in an auto-discovered learned-skill directory.
- Escape Markdown structural characters and dynamically choose code-fence lengths longer than any fence present in the content. Prefer serialization that cannot break into surrounding instructions.
- Add instruction-content detection for phrases requesting command execution, policy changes, secret access, external communication, or disregard of prior constraints.
- Allowlist acceptable tool types and command patterns. Reject or quarantine destructive commands, encoded payloads, shell metacharacters, credential access, and unexpected external URLs.
- Redact credentials, tokens, private keys, cookies, environment secrets, and other sensitive values before persistence.
- Remove the unconditional instruction to replay preserved commands. Require every stored step to be independently validated against the current task and security policy.
- Distinguish quoted historical evidence from trusted instructions using explicit metadata and enforce this distinction in the consuming agent.
- Record provenance and integrity metadata, including the trace origin, approving user, review status, and a content digest.
- Add adversarial tests covering code-fence breakout, injected headings, frontmatter injection, malicious commands, hostile error messages, and poisoned task names.
- Apply output-directory access controls so generated skills cannot overwrite trusted skills or become active before approval.
