Skip to content

fix(review): review screen starts fail-closed with exact-count approve (Spec fix-review-defaults) - #1481

Merged
github-actions[bot] merged 8 commits into
mainfrom
fix-review-fail-closed-defaults
Oct 3, 2026
Merged

github-actions[bot] merged 8 commits into
mainfrom
fix-review-fail-closed-defaults

Conversation

@Dumbris

@Dumbris Dumbris commented Oct 2, 2026

Copy link
Copy Markdown
Member

What changes for users

The review screen used to start every "Allow this tool" checkbox checked, including write and destructive tools whose definitions were never verified. It now fails closed.

  • Core / REST: every tool in GET /api/v1/servers/{id}/review carries default_allowed (always present). It is true for an approved, enabled tool, and for a pending or changed read tool whose scan verdict is clean and that is not held. Write, destructive, unannotated, unknown, not-scanned, warnings, dangerous and held tools are false. An older core has no field, which every surface reads as false.
  • Web: only the core-selected tools start checked. A hint explains why. The approve button names the exact count ("Approve server (5 of 14 tools)", or "Approve without seeing tools" when nothing is captured). A separate "Approve all (14 tools)" button approves everything. A reload keeps the user's explicit choices (an explicit check is dropped if the tool's definition, verdict or tier changed underneath it). The forced retry after a dangerous finding re-sends the block list of the attempt that triggered it.
  • macOS: the review sheet has the same defaults, hint, labels, Approve All button, reload behaviour and force retry.
  • CLI: mcpproxy review approve <server> on a quarantined server blocks every tool outside the default selection. --all allows every tool, --tools a,b (now valid on a quarantined server) allows exactly those, --except subtracts, and --all with --tools or an unknown tool name is an error before any write. The prompt now follows the review read and names the count and the blocked tools. Table output prints one summary line first; JSON and YAML stay the bare REST object. Behaviour change: review approve <server> --yes used to allow every tool; add --all for that. The command has not shipped in a release.
  • MCP: no semantic change. inspect_quarantined carries default_allowed (captured tools from the composer, live tools false); inspect_tools does not. No quarantine_security operation approves a server.

Findings and spec

Codex user test F-02 (medium, security default). Spec: specs/109-ux-navigation-consistency (FR-021, FR-023, US2-2; research D41; tasks T199 to T210; contracts, data model, quickstart recipe, acceptance index and parity matrix row 6 updated). Docs updated: docs/features/security-quarantine.md, docs/cli/review-commands.md, docs/cli/command-reference.md, docs/api/rest-api.md.

Accepted residual: a server that declares readOnlyHint dishonestly still gets its read tools pre-checked once the scan is clean, because annotations are self-declared and the scan checks descriptions. The tier stays visible and the tool can be blocked later.

Tests added

  • Go: TestReviewDefaultAllowed (tier by status by verdict by disabled by held table), coverage-driven composer cases, JSON key present when false, MCP captured and live parity, CLI default selection, --all, --tools, prompt count, JSON output, reviewApproveSelection table, three goldens.
  • Web: review-screen-default-selection.spec.ts (helpers and component); three assertions in review-screen.spec.ts re-baselined on purpose because they pinned the old all-checked default.
  • macOS: ReviewPresentationTests (selection, merge, labels, hint parity with the Web sentence) and ReviewPayloadTests decode cases.

…prove uses it (Spec fix-review-defaults)

ReviewTool.default_allowed is true only for an approved enabled tool or a pending/changed read tool with a clean current scan that is not held. mcpproxy review approve blocks every other tool unless --all or --tools is given, and its prompt names the exact count.
…exact-count approve label (Spec fix-review-defaults)

Both screens read default_allowed from the core, show the selection hint, name the exact count on the approve button, add an explicit Approve all, keep the user's choices across reloads and re-send the attempted block list on the force retry.
@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Oct 2, 2026 •

Copy link
Copy Markdown

Deploying mcpproxy-docs with  Cloudflare Pages  Cloudflare Pages

Latest commit: 5f7dacd
Status: ✅  Deploy successful!
Preview URL: https://07310bce.mcpproxy-docs.pages.dev
Branch Preview URL: https://fix-review-fail-closed-defau.mcpproxy-docs.pages.dev

View logs

F2.1: review approve rejects --tools/--except on a quarantined server with no
captured tools instead of silently dropping them and approving blind.
F3.1: ReviewScreen clears the force-retry block list and closes the force
dialog when serverName changes; a forced approve without a stored block list
recomputes from the current selection instead of reusing another server's.
@github-actions

github-actions Bot commented Oct 2, 2026 •

Copy link
Copy Markdown
Contributor

📦 Build Artifacts

Workflow Run: View Run
Branch: fix-review-fail-closed-defaults

Available Artifacts

  • archive-darwin-amd64 (31 MB)
  • archive-darwin-arm64 (28 MB)
  • archive-linux-amd64 (19 MB)
  • archive-linux-arm64 (17 MB)
  • archive-windows-amd64 (31 MB)
  • archive-windows-arm64 (27 MB)
  • frontend-dist-pr (0 MB)
  • installer-dmg-darwin-amd64 (27 MB)
  • installer-dmg-darwin-arm64 (24 MB)
  • smart-mcp-proxymcpproxy-goL5LB9Z.dockerbuild (0 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 37086006049 --repo smart-mcp-proxy/mcpproxy-go

Note: Artifacts expire in 14 days.

@codecov-commenter

codecov-commenter commented Oct 2, 2026 •

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 89.10891% with 11 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
cmd/mcpproxy/review_cmd.go 88.17% 9 Missing and 2 partials ⚠️

📢 Thoughts on this report? Let us know!

…changed

Review.vue showed the spinner on every background reload, which unmounted ReviewScreen and dropped its per-instance explicit checks and unchecks. Only the first load blanks the page now, and a failed background reload shows its error above the screen instead of replacing it.
- web: approve()/rescan() capture the server name and drop a response for a
  server the screen no longer shows (no error, no force dialog); the serverName
  watcher also resets approving. Late-response vitest cases added (fail without
  the fix).
- Approve all wording: every pending or changed tool, previously blocked tools
  stay blocked (Web tooltip, macOS help, CLI --all help, docs, spec text). The
  fail-closed behaviour is unchanged.
- macOS: ReviewTool decodes held_reason/held_signals so mergeSelection notices a
  hold-only change; XCTest varies held_reason.

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approved (Model B): Paperclip review verdicts = ACCEPT and qa-gate green at this head SHA. Arming auto-merge; GitHub merges when all required checks pass.

@github-actions
github-actions Bot merged commit 335477e into main Oct 3, 2026
62 checks passed
github-actions Bot pushed a commit that referenced this pull request Oct 3, 2026
…le reach (#1482)

Docs, changelog and verified fixes from a first-run, upgrade and
user-test pass on `main` (335477e).

## Docs
- `docs/features/profiles.md`: profiles are optional. Without an
effective profile, no profile-level restriction applies (a scoped
credential's own grant still does). The quick start is presented as
being for scoped access.
- `docs/configuration.md` (`quarantined` row): explains the real
default. A server added through the UI, CLI or API follows its trust
mode. A first-seen config-file server with no `quarantined` key and no
`config.db` record is held for review. An explicit value wins, and a
recorded server keeps its state. Links to the admission rules.
- `docs/features/security-quarantine.md`: the hand-editing section names
the extra conditions: quarantine is enabled, and the trust mode is not
`auto` (including legacy `auto_approve_tool_changes`/`skip_quarantine`
resolving to `auto`), matching `ServerConfig.EffectiveTrustMode`.
- `docs/getting-started/quick-start.mdx`: a plain note that a server
added by editing the file is held for review until approved.
- `CHANGELOG.md`: the security, review, catalog, telemetry, CLI and
first-run fixes merged since v0.69.0 (#1463, #1467–#1473, #1481) plus
this PR's.

## Fixes (each reproduced in an isolated instance, regression test fails
on main)
- **Upgrade start advisory repeated.** `reportPreFixAdmissions` ran on
every gate pass (initial gate, `LoadConfiguredServers`, every publish),
so an upgrade start logged the "predate the config-load admission gate"
advisory 2–5 times. It is now reported once per server per process. Gate
decisions are unchanged.
- **Benign startup race logged as ERROR.** Both the supervisor reconcile
and `LoadConfiguredServers` → `AddServer` asked a not-yet-ready client
to connect. The second call was correctly refused with "connection
already in progress or established (state: Connecting)" but logged at
ERROR, and server identity registration was skipped. A new
`managed.ErrConnectAlreadyActive` sentinel (same message text) lets
`AddServer` treat it as success at debug level.
- **`set_profile` under-reported a switchable client's reach.** After a
successful switch, `resolveEffectiveProfileForJustSetSlug` treated the
client binding as authoritative and reported the binding's servers
instead of the selected profile's.

## Verification
- Cross-model review: codex gpt-6.1-sol, chunked. Three low findings:
two doc-wording findings are applied, and the third (the per-process
advisory set is never cleared) is a deliberate trade-off.
- Live re-verification on isolated instances: each symptom was
reproduced on a main build and is gone on this branch. The upgrade case
was run over a v0.52.1 data dir.
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