Severity: Critical (secret leak via MCP)
Discovered by
Codex code review (2026-06-03)
Location
src/perseus/mcp.py:197 — perseus_get_context calls render_source, not render_output
Problem
perseus_get_context returns the raw, pre-redaction rendered source. Other tool resolvers (perseus_read, perseus_query, etc.) also return their raw output via _call_resolver without passing the result through redact_text.
An MCP client receives unredacted secrets even when redaction.enabled: true is set in config.
Impact
- Secrets leak via the MCP transport to any tool client
- Defeats the entire defense-in-depth purpose of the redaction layer for MCP users
Fix
- Route
perseus_get_context through render_output (which applies redaction)
- In
_call_tool / _call_resolver, apply redact_text(result, cfg) to every tool result before returning
- Document the MCP redaction guarantee in the README
Acceptance criteria
- Test: configure a redaction rule for
SECRET_TOKEN_123, call perseus_get_context, assert response is redacted
- Test: same for
perseus_read, perseus_query, perseus_list, perseus_tree, perseus_date
- Test: with
redaction.enabled: false, the secret IS present (sanity)
Refs
Severity: Critical (secret leak via MCP)
Discovered by
Codex code review (2026-06-03)
Location
src/perseus/mcp.py:197—perseus_get_contextcallsrender_source, notrender_outputProblem
perseus_get_contextreturns the raw, pre-redaction rendered source. Other tool resolvers (perseus_read,perseus_query, etc.) also return their raw output via_call_resolverwithout passing the result throughredact_text.An MCP client receives unredacted secrets even when
redaction.enabled: trueis set in config.Impact
Fix
perseus_get_contextthroughrender_output(which applies redaction)_call_tool/_call_resolver, applyredact_text(result, cfg)to every tool result before returningAcceptance criteria
SECRET_TOKEN_123, callperseus_get_context, assert response is redactedperseus_read,perseus_query,perseus_list,perseus_tree,perseus_dateredaction.enabled: false, the secret IS present (sanity)Refs