Skip to content

zeph-subagent: TranscriptWriter::append has no MessagePart::Image filter (defense-in-depth gap for spec-072 C1) #6305

Description

@bug-ops

Description

TranscriptWriter::append (crates/zeph-subagent/src/transcript.rs:120) serializes the full Message (including parts) verbatim to <task_id>.jsonl with no filtering. Found during review of #6239 (P1 ephemeral persistence strip), which enforces invariant C1/M5 (specs/072-multimodal-mcp-passthrough/spec.md §4) only at Agent::persist_message — the single strip point for the three surfaces spec-072 §4/AC-5 explicitly name (SQLite parts_json, Qdrant embeddings, durable JSONL session log). Subagent transcripts are a structurally separate subsystem, never named by spec-072, and subagents never call Agent::persist_message.

extract_parent_messages (crates/zeph-core/src/agent/subagent_commands.rs:745) reads live self.msg.messages (unstripped, can contain MessagePart::Image from the existing user-upload path) and passes it as initial_messages into a spawned subagent's AgentLoopArgs.

Currently not exploitable: independently verified (twice, by both the #6239 implementation-critic and code-reviewer) that crates/zeph-subagent/src/agent_loop.rs::init_loop_state extends initial_messages into the in-memory messages Vec for the subagent's own LLM calls, but never loops over and writes them to the transcript file — only the freshly-built task_msg (empty parts) and subsequently turn-generated messages go through append_transcript. A grep of crates/zeph-subagent/src/ for MessagePart::Image returns zero hits — no current code path constructs a new Image part inside a subagent turn.

This becomes a real, reachable leak once #6240 (P2, MCP image emission inside tool-result processing) lands and a subagent's own turn can produce a new MessagePart::Image that flows through append_transcript with no filter.

Reproduction Steps (future, once #6240 lands)

  1. Enable MCP image passthrough on a server (media_passthrough = true, per Implement: P2 MediaSanitizer decode/validate/attach + vision-tier routing for MCP image passthrough #6240/Implement: P3 config/CLI/TUI/migration surface for MCP image passthrough #6241).
  2. Spawn a subagent that calls a tool returning an image.
  3. Inspect the subagent's <task_id>.jsonl transcript file — expect raw base64 image bytes present in a parts array.

Expected Behavior

TranscriptWriter::append (or its call sites in agent_loop.rs) should strip MessagePart::Image before writing, mirroring the strip pattern landed in #6239's Agent::persist_message.

Actual Behavior

No filter exists; append serializes whatever Message it's given.

Environment

Related

Suggested next step

Run /rust-agents:solve-issue on this issue once #6240's implementation shape is known (or independently, applying the same filter pattern as #6239 at the append_transcript/TranscriptWriter::append call sites).

Metadata

Metadata

Assignees

Labels

P2High value, medium complexitysecuritySecurity-related issuesub-agentsSub-agent orchestration

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions