Skip to content

Critical: sub-agent granted secrets leak into transcript, LLM context, and debug dumps unmasked #6492

Description

@bug-ops

Vulnerability

When a sub-agent is granted a vault secret (zeph-subagent grants system), the raw value is injected as a per-call ExecutionContext env override before a tool executes (crates/zeph-subagent/src/agent_loop.rs:684-694, deliver_secret in crates/zeph-subagent/src/manager/secrets.rs:87-112). If the tool's own output happens to include that value (e.g. a bash step that runs env, echoes the variable, or a verbose HTTP client that logs its Authorization header), the raw secret becomes part of the tool-result content (agent_loop.rs:709-717) with no secret-masking pass applied to it.

From there the unmasked value is:

  • Written verbatim to the on-disk JSONL transcript (no scrubbing in transcript.rs::append).
  • Sent back to the LLM provider in the next turn's request (network egress to a third party).
  • Captured by --debug-dump when enabled (agent_loop.rs:186-223, dump_request/dump_response receive the full unmasked messages).

The crate already has an optional SecretMaskRegistry/PiiFilter layer (manager/mod.rs:395-405, forward.rs:125-139), but it is wired only into the TUI forwarding/display pipeline (maybe_spawn_forward) — deliver_secret never registers the delivered value into that registry, so even the forwarding path doesn't catch it. The ContentSanitizer passed into the loop (sanitizer.sanitize(...) at agent_loop.rs:769) is only invoked when a PostToolUse hook replaces the tool output, not on the raw exec_result content in the common (no-hook) case, and it targets injection-pattern detection, not secret-value masking. zeph-sanitizer (owner of SecretMaskRegistry) is already a dependency of zeph-subagent, so a fix is architecturally straightforward.

The main agent (zeph-core) has an analogous placeholder-masking scheme ("PAAC": unmask_json_value/prepare_tool_dispatch in tier_loop.rs) that keeps raw secrets out of LLM-facing context entirely — the sub-agent path has no equivalent protection.

Severity

Critical — a granted secret can reach the transcript file, the third-party LLM provider, and on-disk debug dumps in plaintext with no masking anywhere in the common path.

Location

  • crates/zeph-subagent/src/manager/secrets.rs:87-112 (deliver_secret — raw value delivered, never registered for masking)
  • crates/zeph-subagent/src/agent_loop.rs:684-798 (secret injected into tool env; tool output pushed into messages unmasked)
  • crates/zeph-subagent/src/agent_loop.rs:186-223 (debug dump receives unmasked messages)

Attack Scenario

A sub-agent is granted a vault secret (e.g. ZEPH_TELEGRAM_BOT_TOKEN) scoped to a specific tool call. The sub-agent (whether through a legitimate but overly-verbose tool, a buggy skill, or a prompt-injected instruction from untrusted tool output earlier in its own turn) runs a bash command like env | grep ZEPH or a curl command with -v that echoes headers. The raw secret value appears in the tool's stdout, gets wrapped into content unmodified, and is: (1) persisted forever in the transcript JSONL, (2) sent to the external LLM provider as part of the next request, and (3) written to disk in --debug-dump output if enabled. Any of these three sinks constitutes a credential leak outside the intended trust boundary.

Remediation

Register every value delivered by deliver_secret into SecretMaskRegistry (or equivalent) at grant time, and apply that registry's masking pass to tool-result content unconditionally (not just on the hook-replacement branch) before it is pushed into messages, written to the transcript, or handed to the debug-dump sink — mirroring the zeph-core PAAC placeholder-masking approach used for the top-level agent loop.

References

Related: #6460, #6461, #6453 (recent transcript/vault-anchor integrity hardening — this finding is a distinct gap, secret value exposure rather than transcript-tampering)

Metadata

Metadata

Assignees

Labels

P0Blocking — security advisory or compile errorbugSomething isn't workingllmzeph-llm crate (Ollama, Claude)securitySecurity-related issue

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions