Skip to content

docs(specs): spec 100 — per-prompt rug-pull baseline (Spec 032 parity) - #1009

Merged
Dumbris merged 1 commit into
mainfrom
100-prompt-rugpull-baseline
Aug 19, 2026
Merged

docs(specs): spec 100 — per-prompt rug-pull baseline (Spec 032 parity)#1009
Dumbris merged 1 commit into
mainfrom
100-prompt-rugpull-baseline

Conversation

@Dumbris

@Dumbris Dumbris commented Aug 19, 2026

Copy link
Copy Markdown
Member

The last deferred item from the PR #973 multi-model review — finding F2's rug-pull half. Aggregated upstream prompts now have scope enforcement, content sanitisation, TPA description scanning, activity logging, size caps, name hardening, and reactive refresh (#973/#1005/#1006/#1007) — but no change detection. Tools carry a per-item approval hash (Spec 032) so a trusted server can't silently swap an approved tool's contract; prompts have no equivalent, so a server can pass admission with a benign prompt and later mutate it with no review and no record.

This is spec-only — no code. The design workflow's honest verdict was that the full parity feature is ~1600–2000 LOC across ~12 files (storage record + bucket, a hash/state-machine engine with the fail-closed enforceInvariant spine, the RefreshPrompts hook, MCP/REST/Vue review surfaces), which is spec-worthy by this repo's conventions rather than a drive-by PR. So this PR is the decision point.

Key design decisions captured

  • Baselines advertised LIST metadata only (name + description + argument descriptors). Get-time prompts/get message content has no list-time artifact to hash against and is inherently not cheaply baselineable — stated loudly as the headline Non-Goal so it's never mistaken for content protection (that path stays defended by F2 redaction + F12 caps).
  • Enforcement is by withholding: not registering a changed prompt is simultaneously list-hide and native get-fail, so there is no runtime get-time gate to build — the single biggest simplification over the tool machinery.
  • Parallel PromptApprovalRecord/bucket, not a ToolApprovalRecord overload (the server:tool vs server:prompt key spaces collide).
  • Reuses the quarantine_security approve path; a ~300–400 LOC MVP delivers the whole security guarantee, with REST twins, the Vue banner, trust_mode: scan, and a dedicated config dial explicitly deferred.

The enforceInvariant port is security-critical (the fail-closed guarantee), which is itself the argument for a spec + review rather than a quiet PR.

Next

If the shape looks right: speckit.planspeckit.tasks, then implement the MVP as the first task. Happy to proceed on your go-ahead.

🤖 Generated with Claude Code

… for prompts)

The deferred "big one" from the PR #973 review (finding F2, rug-pull half).
Aggregated upstream prompts have scope, sanitisation, TPA description scanning,
logging, caps, naming hardening, and reactive refresh — but no CHANGE detection.
A trusted server can pass admission with a benign prompt and later mutate its
metadata with no review and no record. This specs the tool-quarantine (Spec 032)
analogue for prompts.

Key design decisions captured:
- Baselines advertised LIST metadata only (name+description+args); get-time
  message content is inherently not cheaply baselineable — stated loudly as the
  headline Non-Goal so it is never mistaken for content protection.
- Enforcement is by WITHHOLDING (not registering a changed prompt = list-hide +
  native get-fail), so there is NO runtime get-time gate to build — the biggest
  simplification over tools.
- Parallel PromptApprovalRecord/bucket (not a ToolApprovalRecord overload).
- Reuses the quarantine_security approve path; a ~300-400 LOC MVP delivers the
  whole security guarantee, with REST/Vue/scan-mode explicitly deferred.

Spec-only; no code. Implementation (MVP first) is a follow-up task under this spec.

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: 4f29752
Status: ✅  Deploy successful!
Preview URL: https://84d873c8.mcpproxy-docs.pages.dev
Branch Preview URL: https://100-prompt-rugpull-baseline.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

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@github-actions

Copy link
Copy Markdown

📦 Build Artifacts

Workflow Run: View Run
Branch: 100-prompt-rugpull-baseline

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 32253908107 --repo smart-mcp-proxy/mcpproxy-go

Note: Artifacts expire in 14 days.

@Dumbris
Dumbris merged commit b46505a into main Aug 19, 2026
47 checks passed
Dumbris added a commit that referenced this pull request Aug 19, 2026
Implements the core of spec 100 (merged in #1009): the tool-quarantine
(Spec 032) analogue for aggregated upstream prompts. A trusted server that
passes admission with a benign prompt and later mutates its advertised metadata
is now caught and the changed prompt is WITHHELD from prompts/list until
approved — closing the last F2 gap from the PR #973 review.

Scope (metadata only): the baseline hashes advertised LIST metadata
(name + description + arguments); get-time prompts/get message content is
inherently not baselineable here (spec Non-Goals) and stays defended by the
existing F2 sanitisation + F12 caps.

- storage: parallel PromptApprovalRecord + prompt_approvals bucket + CRUD
  (+ Manager wrappers), mirroring the tool ops 1:1 — NOT a ToolApprovalRecord
  overload (the server:tool / server:prompt key spaces would collide).
- engine (internal/server/prompt_quarantine.go): calculatePromptApprovalHash
  (sha256(name|desc|normalizeJSON(args)), excludes Meta/Title), checkPromptApprovals
  with the pending/changed/approved state machine + baseline-pass + QuarantineEnabled
  kill-switch + TrustMode/AutoApproveToolChanges reuse, a fail-closed
  enforcePromptInvariant transition spine, filterBlockedPrompts, and the
  ApprovePrompt/ApproveAllPrompts mutators (which re-baseline + RefreshPrompts()).
- hook: checkPromptApprovals + filterBlockedPrompts run in RefreshPrompts after
  the TPA scan-and-drop, before registration. Withholding IS the block — a prompt
  never passed to SetPrompts is absent from prompts/list and fails prompts/get
  natively, so there is no runtime get-time gate.

Tests: hash stability/change, fail-closed invariant, first-seen→pending,
approve→registered, rug-pull change→withheld→revert→re-approved, trust=auto
auto-approve, ApproveAllPrompts, and a full-RefreshPrompts withholding integration
test. Existing aggregation tests set quarantine off (they verify aggregation, not
the baseline).

Deferred to a follow-up: the quarantine_security MCP ops
(inspect_prompts/approve_prompt/approve_all_prompts), REST twins, and the Vue
review banner — the approve API (ApprovePrompt/ApproveAllPrompts) is ready to
wire. Adding the MCP tool-schema op requires regenerating the 3 frozen
tool-surface goldens, done deliberately in that PR.


Claude-Session: https://claude.ai/code/session_01H7J8Yv5zr4tMQZaY3ot3Za

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
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