Skip to content

feat(prompts): security-hardening for upstream prompt aggregation (F2/F10/F12) - #1005

Merged
Dumbris merged 1 commit into
mainfrom
feat/prompts-security-hardening
Aug 19, 2026
Merged

feat(prompts): security-hardening for upstream prompt aggregation (F2/F10/F12)#1005
Dumbris merged 1 commit into
mainfrom
feat/prompts-security-hardening

Conversation

@Dumbris

@Dumbris Dumbris commented Aug 19, 2026

Copy link
Copy Markdown
Member

Step 2a of the PR #973 follow-up. Brings aggregated upstream prompts to parity with the tool path's security controls. The feature stays OFF by default (aggregate_upstream_prompts=false, shipped in #973); this hardens it for when a user opts in.

What this closes (from the multi-model review of #973)

F2 (P1) — scan + sanitise upstream prompt content

Prompt names, descriptions, and message content previously reached the client with zero inspection, while the tool path scans and sanitises. Two layers:

  • Sanitise prompts/get results with the same detector + policy tool results get: redact secrets, strip control sequences, spotlight untrusted text, and block on a critical secret. Reaches TextContent and embedded text resources; binary/image/audio untouched. Extracts a shared sanitiseTextValue helper reused by both the tool and prompt paths.
  • TPA-scan each aggregated prompt's name/description/argument-descriptions at refresh time and drop any "dangerous" verdict — the analogue of tool-description poisoning detection.

F10 (P2) — activity logging for prompts/get

prompts/get was invisible to mcpproxy activity list and telemetry. Adds a prompt_get activity type + event + emitter + handler (with sensitive-data detection over args/content), a minted request-id for activity list --request-id correlation, and the CLI --type allowlist entry.

F12 (P2) — size + count caps

No caps existed. Per-message text is truncated at 1 MiB (UTF-8-safe, clear marker); per-server prompt count is capped at 200 and the total aggregated list at 1000 — all logged, never silent.

Design

The three layers compose in one getPromptAggregated getter (a single upstream round-trip): fetch → F12 size-cap → F2 sanitise → F10 log, sharing one request-id so the sanitisation policy_decision and prompt_get rows join under activity list --request-id.

Tests

Prompt-result sanitisation (redact / block / embedded resource), poisoned-description drop, UTF-8-safe truncation + size cap + nil-safety, per-server count cap, and prompt_get activity persistence.

Deferred to step 2b (polish)

Per-prompt approval-hash/rug-pull baseline, list-metadata redaction, prompt name-collision/:-in-name hardening, per-server REST/UI expose_prompts wiring, pagination, and consuming upstream prompts/list_changed.

🤖 Generated with Claude Code

…/F10/F12)

Step 2a of the PR #973 follow-up: bring aggregated upstream prompts to
parity with the tool path's security controls. The feature remains OFF by
default (aggregate_upstream_prompts=false); this hardens it for when a user
opts in.

- F2 (P1): scan + sanitise upstream prompt content.
  - Sanitise prompts/get results with the same detector + policy tool results
    get: redact secrets, strip control sequences, spotlight untrusted text, and
    block on a critical secret. Reaches TextContent and embedded text resources;
    binary/image/audio untouched. Extracts a shared saniseTextValue helper reused
    by the tool and prompt paths.
  - TPA-scan each aggregated prompt's name/description/argument descriptions at
    refresh time and drop any "dangerous" verdict, the analogue of tool-
    description poisoning detection.
- F10 (P2): activity logging for prompts/get. New prompt_get activity type +
  event + emitter + handler (with sensitive-data detection over args/content),
  a minted request-id for `activity list --request-id` correlation, and the CLI
  --type allowlist entry.
- F12 (P2): size + count caps. Per-message text truncated at 1 MiB (UTF-8-safe,
  clear marker), per-server prompt count capped at 200 and total aggregated at
  1000 — all logged, never silent.

The three layers compose in one getPromptAggregated getter (single upstream
round-trip): fetch -> F12 size-cap -> F2 sanitise -> F10 log, sharing one
request-id so the sanitisation policy_decision and prompt_get rows join.

Tests: prompt-result sanitisation (redact/block/embedded), poisoned-description
drop, UTF-8-safe truncation + size cap + nil-safety, per-server count cap, and
prompt_get activity persistence.

Deferred to step 2b (polish): per-prompt approval-hash/rug-pull baseline, list-
metadata redaction, prompt name-collision/colon hardening, REST/UI expose_prompts
wiring, pagination, and consuming upstream prompts/list_changed.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01H7J8Yv5zr4tMQZaY3ot3Za
@cloudflare-workers-and-pages

Copy link
Copy Markdown

Deploying mcpproxy-docs with  Cloudflare Pages  Cloudflare Pages

Latest commit: bcec686
Status: ✅  Deploy successful!
Preview URL: https://9e843cdf.mcpproxy-docs.pages.dev
Branch Preview URL: https://feat-prompts-security-harden.mcpproxy-docs.pages.dev

View logs

@codecov-commenter

Copy link
Copy Markdown

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

Codecov Report

❌ Patch coverage is 65.16854% with 93 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
internal/server/mcp.go 0.00% 32 Missing ⚠️
internal/runtime/activity_service.go 66.66% 15 Missing and 4 partials ⚠️
internal/server/output_sanitisation.go 77.77% 11 Missing and 7 partials ⚠️
internal/runtime/event_bus.go 0.00% 14 Missing ⚠️
internal/upstream/manager_prompts.go 81.81% 6 Missing and 2 partials ⚠️
internal/server/mcp_routing.go 94.73% 1 Missing and 1 partial ⚠️

📢 Thoughts on this report? Let us know!

@github-actions

Copy link
Copy Markdown

📦 Build Artifacts

Workflow Run: View Run
Branch: feat/prompts-security-hardening

Available Artifacts

  • archive-darwin-amd64 (29 MB)
  • archive-darwin-arm64 (26 MB)
  • archive-linux-amd64 (17 MB)
  • archive-linux-arm64 (15 MB)
  • archive-windows-amd64 (28 MB)
  • archive-windows-arm64 (25 MB)
  • frontend-dist-pr (0 MB)
  • installer-dmg-darwin-amd64 (23 MB)
  • installer-dmg-darwin-arm64 (20 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 32225343916 --repo smart-mcp-proxy/mcpproxy-go

Note: Artifacts expire in 14 days.

@Dumbris
Dumbris merged commit 4a9f88d into main Aug 19, 2026
39 checks passed
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