T02 · Agent Memory Poisoning
- Location
SKILL.md:18- Finding
Project-Controlled Persistent Learning Entries Are Loaded as Mandatory Agent Instructions
- Content
View full analysis
Vulnerability Details
File Location:
SKILL.md:18-29;scripts/learn.py:25-53, 153-167
Vulnerability Type: Persistent instruction injection through project-local memory
Risk Level: HighTechnical Analysis
The Skill requires the Agent to load historical learning entries before creating a storyboard. The instructions state that this step cannot be skipped and that every returned entry must be followed.
Relevant instruction from
SKILL.md:18-29, translated into English:text Before writing, the historical experience must be reviewed. This step cannot be skipped. avs learn digest # If the CLI is unavailable: python3 scripts/learn.py digest Every entry in the output must be followed.The learning implementation loads both global and project-local state. The project path is relative to the current working directory:
python GLOBAL_PATH = Path.home() / ".workbuddy" / "avs" / "learnings.jsonl" PROJECT_PATH = Path(".avs") / "learnings.jsonl" def load(scope): items = [] paths = [] if scope in ("all", "global"): paths.append(("global", GLOBAL_PATH)) if scope in ("all", "project"): paths.append(("project", PROJECT_PATH)) for name, p in paths: if not p.exists(): continue for line in p.read_text(encoding="utf-8").splitlines(): line = line.strip() if not line: continue try: obj = json.loads(line) except json.JSONDecodeError: continue obj["scope"] = name obj["file"] = str(p) items.append(obj) return itemscmd_digestthen emits the storedtextfield without validation, escaping, provenance enforcement, or an allowlist restricting it to storyboard preferences:python def cmd_digest(args): items = load(args.scope) lessons = [i for i in ...[truncated 3044 chars]- Remediation
View remediation
Remediation Suggestions
- Treat all learning-file values as untrusted data rather than executable or mandatory instructions.
- Replace free-form
textrules with a structured schema containing narrowly defined fields such as color palette, narration style, prohibited visual object, and target model. - Validate categories and values against an allowlist. Reject entries containing tool requests, file paths, URLs, shell commands, credential requests, or instructions unrelated to storyboard generation.
- Do not state that every digest entry must be obeyed. Instruct the Agent to use entries only as optional storyboard preferences and never as authority to change security constraints or invoke unrelated tools.
- Require explicit user approval before loading project-local
.avs/learnings.jsonl, especially for cloned repositories and shared workspaces. - Display each entry's scope and source path, and clearly separate global user-owned preferences from repository-controlled project data.
- Store trusted global entries in a user-owned directory with restrictive permissions and apply ownership or integrity checks where appropriate.
- Add a safe digest mode that serializes entries as quoted data under a fixed prompt boundary, explicitly stating that embedded imperatives are content and must not be executed.
- Add tests using instruction-like learning entries to verify that they cannot trigger unrelated tool operations or override the Skill's intended workflow.
