Skip to content

feat(054-B): output sanitisation enforcement (Spec 054 Track B)#535

Merged
Dumbris merged 1 commit into
mainfrom
059-output-sanitisation
May 29, 2026
Merged

feat(054-B): output sanitisation enforcement (Spec 054 Track B)#535
Dumbris merged 1 commit into
mainfrom
059-output-sanitisation

Conversation

@Dumbris
Copy link
Copy Markdown
Member

@Dumbris Dumbris commented May 29, 2026

Summary

Implements Spec 054 Track B — output sanitisation enforcement — the next track of the security-gateway umbrella (#521) after Track A (output-schema validation, #525/056). Makes mcpproxy's already-computed-but-discarded content-trust classification (Spec 035) and secret detector (Spec 026) actually contain untrusted tool output before it reaches the agent, at the single response chokepoint.

Fully opt-in — with no output_sanitisation block, every response is forwarded byte-for-byte (pre-feature behaviour; keeps the API E2E suite green).

Behaviours (all opt-in)

Config Effect FR
spotlight_untrusted: true wrap untrusted (open-world) tool text in «untrusted:server/tool» delimiters, escaping the sentinel so content can't forge the wrapper B1/B2
response_action: redact mask detected secrets → [REDACTED:<category>] (reuses Spec 026 detector) B3
strip_control_chars: true neutralise ANSI / C0-C1 / zero-width / bidi on untrusted text (per-class toggles) B4
response_action: block replace payload with a remediation error on a critical detection B7

Non-text blocks (image/audio/embedded) are never modified (B5). Mutating actions emit a policy_decision activity record.

Ordering / read_cache

Redact / strip / block run on the raw result before forwardContentResult truncates and caches it, so read_cache never stores an unredacted secret and a blocked response is never cached. The lossless spotlight wrapper is applied post-truncation and is not cached.

Verification

Verified end-to-end (artifacts kept local, not committed):

  • curl/MCP roundtrip vs an untrusted stub upstream — spotlight, redact, strip, block, and the read_cache leak check (cached copy is [REDACTED:cloud_credentials], raw secret count 0)
  • API E2E ./scripts/test-api-e2e.sh → 65/65, no regression
  • Web UI activity view (Playwright) — Policy Decision/redact rows + redacted response body
  • Unit + integration tests, -race, both editions build, lint clean

Spec artifacts

Speckit set under specs/059-output-sanitisation/ (spec, plan, research, data-model, tasks, quickstart).

Relates to Spec 054 Track B (#521).

@cloudflare-workers-and-pages
Copy link
Copy Markdown

cloudflare-workers-and-pages Bot commented May 29, 2026

Deploying mcpproxy-docs with  Cloudflare Pages  Cloudflare Pages

Latest commit: 523075c
Status: ✅  Deploy successful!
Preview URL: https://33252495.mcpproxy-docs.pages.dev
Branch Preview URL: https://059-output-sanitisation.mcpproxy-docs.pages.dev

View logs

@codecov-commenter
Copy link
Copy Markdown

codecov-commenter commented May 29, 2026

⚠️ Please install the 'codecov app svg image' to ensure uploads and comments are reliably processed by Codecov.

Codecov Report

❌ Patch coverage is 69.27083% with 118 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
e2e/stubs/saniclient/main.go 0.00% 41 Missing ⚠️
e2e/stubs/sanitisation/main.go 0.00% 37 Missing ⚠️
internal/server/output_sanitisation.go 88.79% 7 Missing and 6 partials ⚠️
internal/security/detector.go 70.58% 5 Missing and 5 partials ⚠️
internal/server/mcp.go 65.38% 7 Missing and 2 partials ⚠️
internal/server/content_forward.go 61.11% 5 Missing and 2 partials ⚠️
internal/config/config.go 97.61% 1 Missing ⚠️

📢 Thoughts on this report? Let us know!

Make mcpproxy's existing-but-discarded content-trust classification
(Spec 035) and secret detector (Spec 026) actually contain untrusted
tool output, instead of only logging. All behaviour is fully opt-in;
default config forwards every response byte-for-byte.

- spotlight: wrap untrusted (open-world) tool text in source-identifying
  «untrusted:server/tool» delimiters, escaping the sentinel so content
  cannot forge the wrapper (FR-B1/B2). Applied post-truncation, not cached.
- redact: mask detected secrets as [REDACTED:<category>] reusing the
  Spec 026 detector (FR-B3).
- strip: neutralise ANSI / C0-C1 / zero-width / bidi sequences on
  untrusted text, per-class toggles (FR-B4).
- block: replace the payload with a remediation error on a critical
  detection (FR-B7).

Redact/strip/block run on the raw result BEFORE forwardContentResult
truncates and caches it, so read_cache never stores an unredacted secret
and a blocked response is never cached. Non-text blocks are untouched
(FR-B5). Mutating actions emit a policy_decision activity record.

New OutputSanitisationConfig mirrors OutputValidationConfig (Track A).
Verified end-to-end: curl/MCP roundtrip (spotlight/redact/strip/block +
read_cache), API E2E (65/65), and the Web UI activity view.

Relates to Spec 054 Track B (#521).
@Dumbris Dumbris force-pushed the 059-output-sanitisation branch from 4c7ab7f to 523075c Compare May 29, 2026 07:12
@github-actions
Copy link
Copy Markdown

📦 Build Artifacts

Workflow Run: View Run
Branch: 059-output-sanitisation

Available Artifacts

  • archive-darwin-amd64 (28 MB)
  • archive-darwin-arm64 (25 MB)
  • archive-linux-amd64 (16 MB)
  • archive-linux-arm64 (14 MB)
  • archive-windows-amd64 (27 MB)
  • archive-windows-arm64 (24 MB)
  • frontend-dist-pr (0 MB)
  • installer-dmg-darwin-amd64 (21 MB)
  • installer-dmg-darwin-arm64 (18 MB)

How to Download

Option 1: GitHub Web UI (easiest)

  1. Go to the workflow run page linked above
  2. Scroll to the bottom "Artifacts" section
  3. Click on the artifact you want to download

Option 2: GitHub CLI

gh run download 26623650066 --repo smart-mcp-proxy/mcpproxy-go

Note: Artifacts expire in 14 days.

@Dumbris Dumbris merged commit 92977cd into main May 29, 2026
39 checks passed
@Dumbris Dumbris deleted the 059-output-sanitisation branch May 29, 2026 07:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants