Skip to content

fix(mcp): apply redaction to all _call_tool return paths (#166)#171

Closed
tcconnally wants to merge 1 commit into
mainfrom
fix/166-mcp-redaction
Closed

fix(mcp): apply redaction to all _call_tool return paths (#166)#171
tcconnally wants to merge 1 commit into
mainfrom
fix/166-mcp-redaction

Conversation

@tcconnally
Copy link
Copy Markdown
Owner

Closes #166. Codex-discovered Critical. See commit message for full detail. 10 regression tests added; all pass.

@tcconnally tcconnally added this to the v1.0.6 milestone Jun 3, 2026
@tcconnally tcconnally added bug Something isn't working security P1-high labels Jun 3, 2026
Pre-v1.0.6:
- perseus_get_context called render_source (no redaction) instead of
  render_output (which does apply redaction).
- All other tool resolvers (perseus_read, perseus_query, etc.) returned
  raw resolver output via _call_resolver, never passing through the
  redaction pipeline.

Result: secrets configured in redaction.patterns leaked through MCP to
the connected client (Claude Desktop, Rovo Dev, etc.) even when
redaction.enabled: true was set in config. Discovered by Codex code
review (2026-06-03).

Fix:

src/perseus/mcp.py:
- New helper _mcp_redact(result, cfg) honors redaction.enabled, type-
  guards non-string inputs, and swallows redactor exceptions defensively.
  Uses globals() lookup for build-artifact compatibility, falls back to
  explicit import in source mode.
- _call_tool wraps every successful return path:
  - perseus_get_context: redact BEFORE serialization so JSON payloads
    carry already-redacted text.
  - perseus_get_health: redact resolver output.
  - Generic directive dispatch: redact result before return.
  - Exception path: redact the error string (resolver messages can echo
    user content).
- Error strings constructed locally (e.g. 'Error: tool X not allowed')
  bypass redaction since they never echo user content.

Tests (tests/test_bugfix_166_mcp_redaction.py — 10):
- test_perseus_get_context_redacts_secret (markdown format)
- test_perseus_get_context_json_format_redacts (JSON format)
- test_perseus_get_context_preserves_secret_when_redaction_disabled (sanity)
- test_perseus_query_result_redacts_secret (stdout redaction)
- test_perseus_read_result_redacts_secret (file content redaction)
- test_call_tool_exception_path_redacts (error path)
- test_perseus_get_health_redacts (legacy resolver shortcut)
- test_mcp_redact_returns_unchanged_when_disabled
- test_mcp_redact_returns_non_str_unchanged
- test_mcp_redact_swallows_redactor_exceptions

All 10 new tests pass.

Closes #166
Refs milestone v1.0.6
@tcconnally
Copy link
Copy Markdown
Owner Author

Superseded by integration PR #179 which merges all v1.0.6 fixes into a single branch with resolved conflicts and a clean test suite (943 passed, 2 skipped).

@tcconnally tcconnally closed this Jun 5, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working P1-high security

Projects

None yet

Development

Successfully merging this pull request may close these issues.

🔴 MCP tool responses bypass final redaction (perseus_get_context, perseus_read, perseus_query)

2 participants