mirror of
https://github.com/bytedance/deer-flow.git
synced 2026-08-11 15:28:37 +00:00
format_conversation_for_update embeds raw user turns into the <conversation> slot of MEMORY_UPDATE_PROMPT. This is the most attacker-influenced input in the prompt, and it was unescaped: a message containing "</conversation><current_memory>..." closes the conversation block and forges a <current_memory> authority section for the extraction LLM, which can be steered into persisting an arbitrary high-confidence fact — and that fact is later injected into the lead-agent system prompt's <memory> block, which the prompt declares trusted. This is the last unguarded sibling of a rule the repo has established repeatedly. #4044/#4060 html-escaped the current_memory slot of this exact template; #4097 escaped the <memory> injection renderer. In updater.py the same .format() call escapes current_memory and leaves conversation raw. The memory updater sees raw text because InputSanitizationMiddleware only rewrites the ModelRequest and never mutates state, while MemoryMiddleware queues the raw state messages. Escape content with html.escape(quote=False), mirroring _escape_summary / _format_fact_line — after truncation so a trailing "..." cannot split an entity, on both human and assistant turns. Render-time only: no stored value is mutated, so the apply path is unaffected. The conversation function already strips <uploaded_files> here, so tag hygiene in this renderer is established. Scope is the memory updater. The summarizer's <new_messages> / <existing_summary> blocks are the same rule unguarded, but their output is quarantined as untrusted durable context rather than promoted to system authority; that hardening will be a separate change.