T02 · Agent Memory Poisoning
- Location
src/interchange.js:53- Finding
Persistent Indirect Prompt Injection Through Agent-Readable Interchange Markdown
- Content
View full analysis
{ let desc = 'No description provided.'; try { const settings = JSON.parse(p.settings_json); desc = settings.description || desc; } catch {} content += `## ${p.name} ${desc} `; }); fs.writeFileSync(path.join(opsDir, 'profiles.md'), content); } ``` Conversation summaries are likewise inserted without escaping or trust-boundary markers: ```js function generateRecent(dbOverride) { const db = dbOverride || getDb(); const now = new Date(); now.setHours(0, 0, 0, 0); const todayStart = now.toISOString(); const recent = db.prepare(`SELECT id, summary, started FROM conversations WHERE ended IS NOT NULL ORDER BY ended DESC LIMIT 5`).all(); const todayCount = db.prepare(`SELECT COUNT(*) as count FROM conversations WHERE started >= ?`).get(todayStart).count; const durations = db.prepare(`SELECT (julianday(ended) - julianday(started)) * 86400 as duration FROM conversations WHERE ended IS NOT NULL`).all(); let totalDuration = 0; durations.forEach(d => { totalDuration += d.duration || 0; }); const totalMinutes = Math.round(totalDuration / 60); let content = `# Recent Voice Activity ## Last 5 Conversations `; recent.forEach(c => { content += `- ${c.id.substring(0, 8)}: ${c.summary || 'No summary'} (started ${c.started})\n`; }); content += ` ## Today's Conversation Count: ${todayCount} ## Total Conversation Duration: ${totalMinutes} minutes `; fs.writeFileSync(path.join(stateDir, 'recent.md'), content); } ` ...[truncated 3414 chars]- Remediation
View remediation
