T01 · Skill Instruction Hijacking
Warning
- Location
- references/agent-context-loader-template.js:147
- Finding
- Unsanitized Episodic Memory Is Injected into Subagent Instructions<![CDATA[ ## Vulnerability Details **File Location**: `references/agent-context-loader-template.js`, lines 147-174 and 240-251 **Vulnerability Type**: Prompt injection through untrusted stored content **Risk Level**: Medium ### Vulnerable Code ```javascript function getRecentEpisodicEntries(taskType, workspaceRoot, limit = 3) { const episodicDir = path.join(workspaceRoot, 'memory', 'episodic'); const keywords = keywordsFrom(taskType); const results = []; let files = []; try { files = fs.readdirSync(episodicDir) .filter(f => f.endsWith('.md')) .map(f => ({ name: f, fullPath: path.join(episodicDir, f), mtime: (() => { try { return fs.statSync(path.join(episodicDir, f)).mtimeMs; } catch (_) { return 0; } })() })) .sort((a, b) => b.mtime - a.mtime); } catch (_) { return []; } for (const file of files) { if (results.length >= limit) break; const lines = safeReadLines(file.fullPath); const content = lines.join('\n'); if (matchesKeywords(content, keywords)) { const snippet = lines.find(l => l.trim().length > 0) || ''; results.push({ file: file.name, snippet: snippet.trim().slice(0, 100) }); } } return results; } ``` The selected memory content is later inserted directly into the subagent context: ```javascript if (episodic.length > 0 || routing.length > 0) { parts.push('\n## Relevant Context'); parts.push('> Refer to INTENT.md for optimization priorities.\n'); if (episodic.length > 0) { parts.push('**Recent episodic memory:**'); for (const e of episodic) parts.push(`- ${e.file}: ${e.snippet}`); } if (routing.length > 0) { parts.push('**Recent routing decisions:**'); for (const r of routing) parts.push(`- ${r.task_type} → ${r.target} (${r.timestamp})`); } } const block = parts.join('\n'); const context = block.length <= 700 ? block : block.slice(0, 697) + '...'; ``` ### Technical Analysis The loader searches Markdown fil ...[truncated 2240 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Treat all episodic-memory and routing-log content as untrusted data. 2. Do not concatenate retrieved memory into the same instruction channel as the task. Pass it through a structured, explicitly non-authoritative data field when the agent framework supports message-role separation. 3. Define a strict schema for memory entries and select only validated declarative fields instead of arbitrary Markdown lines. 4. Reject or neutralize instruction-like content, role markers, tool directives, prompt delimiters, and other control syntax before inclusion. 5. Place the original task and immutable safety requirements in a higher-priority channel than retrieved context. 6. Add an explicit statement that retrieved memory is untrusted reference material and must not override the task or system constraints. 7. Apply workspace permission controls so only trusted components can modify `memory/episodic`. 8. Add security tests using malicious memory entries to verify that stored text cannot redirect tasks or override safety requirements. ]]>
