T01 · Skill Instruction Hijacking
Error
- Location
- scripts/generate.py:72
- Finding
- Untrusted Memory Content Can Hijack Sub-Agent Instructions and Poison Persistent State<![CDATA[ ## Vulnerability Details **File Location**: `scripts/generate.py:72-125, 154-208, 445-496`; `scripts/digest.py:86-92, 178-241` **Vulnerability Type**: Indirect prompt injection and persistent memory poisoning **Risk Level**: High ### Vulnerable Code ```python def load_session_log(date_str, workspace): """Load session log for a specific date""" memory_dir = workspace / "memory" session_file = memory_dir / f"{date_str}.md" if session_file.exists(): with open(session_file) as f: content = f.read() # Truncate if too long for context if len(content) > 15000: content = content[:15000] + "\n\n[... truncated for context ...]" return content return None ``` ```python if today_log: context_parts.append(f"## Today's Session Log ({date_str}):\n{today_log}") if recent_sessions: context_parts.append(f"## Recent Session Context:\n{recent_sessions}") if persistent_files.get("quotes"): context_parts.append( f"## Quote Hall of Fame (existing):\n{persistent_files['quotes']}" ) if persistent_files.get("curiosity"): context_parts.append( f"## Curiosity Backlog (existing):\n{persistent_files['curiosity']}" ) if persistent_files.get("decisions"): context_parts.append( f"## Decision Log (existing):\n{persistent_files['decisions']}" ) if persistent_files.get("relationship"): context_parts.append( f"## Relationship Notes (existing):\n{persistent_files['relationship']}" ) context = "\n\n---\n\n".join(context_parts) task = build_generation_task(date_str=date_str, context=context) ``` ```python user_prompt = f"""Write your personal diary entry for {date_str}. Based on the following context from today and recent days: {context} --- Write a RICH, reflective diary entry (400-600 words minimum) with these sections: ... """ ``` ```python def update_persistent_files(entry_content, date_str, workspace): ...[truncated 3588 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Wrap retrieved records in explicit, unambiguous data delimiters and state in both system and user prompts that instructions found inside those delimiters must never be followed. 2. Prefer structured JSON fields over direct free-form prompt concatenation, while still declaring every retrieved field untrusted. 3. Separate current-session instructions from historical content using model-supported message roles or dedicated document/context APIs. 4. Scan retrieved content for prompt-injection indicators and either remove them, quote them safely, or require user approval before generation. 5. Validate generated Markdown against an allowlisted schema before saving it. 6. Do not automatically copy generated sections into persistent memory. Require confirmation or apply a second validation step that detects imperative instructions and suspicious role-like content. 7. Record provenance for persistent entries so content originating from external or generated sources can be excluded from future prompts. 8. Apply the same controls to `build_digest_task()` in `scripts/digest.py`. 9. Run diary-generation sub-agents with minimal tools and no command-execution or network privileges unless those capabilities are independently required. ]]>
