From 324b4ac9a0445123abbfe9927f878822b2870672 Mon Sep 17 00:00:00 2001 From: Jarek Potiuk Date: Sat, 10 Oct 2026 09:16:42 +0100 Subject: [PATCH 1/2] fix(adversarial-review): name a fixed path in the sandbox exclusion, not a versioned glob The adversarial-review sandbox exclusion matched the tool as ~/.claude/plugins/cache/apache-magpie/magpie-adversarial-review/*/tools/adversarial-review. The `*` stood for the plugin version, but it also matches spaces, so once a command was approved it ran outside the sandbox even with extra `uv` options spliced in at that position (`--with `, a second `--from`). This is the gap #1406 closed for vetted-ops. The magpie-adversarial-review plugin now ships a SessionStart hook that points ~/.claude/magpie/adversarial-review at the installed version each session, and the exclusion, the Claude Code command, the shared pre-PR block and every skill that runs the tool name that fixed path. The existing Edit(~/.claude/magpie/**) deny covers it. The Codex, Gemini and Copilot commands keep resolving the installed version, since only Claude Code runs the hook and only its exclusion is a permission rule. sandbox-lint now rejects a `*` before the end of any sandbox.excludedCommands entry; #1406 only checked permissions.allow. isolated-setup-update and -verify report the old exclusion as a must-fix. Generated-by: Claude Opus 5 --- .claude/settings.json | 2 +- docs/setup/secure-agent-setup.md | 13 ++++-- .../.claude-plugin/plugin.json | 15 +++++- .../fix-workflow/pre-pr-adversarial-review.md | 6 +-- .../skills/code-review/adversarial.md | 6 +-- .../skills/announce-draft/SKILL.md | 8 ++-- .../skills/audit-report/SKILL.md | 8 ++-- .../skills/prepare/automated-signing.md | 6 +-- .../skills/prepare/post.md | 6 +-- .../skills/prepare/prep.md | 6 +-- .../pre-pr-adversarial-review.md | 6 +-- .../magpie-security/skills/issue-fix/SKILL.md | 8 ++-- .../skills/issue-import-from-pr/SKILL.md | 8 ++-- .../skills/issue-import-from-scan/SKILL.md | 8 ++-- .../skills/model-prepare/SKILL.md | 8 ++-- .../skills/model-verify/SKILL.md | 8 ++-- .../skills/isolated-setup-install/SKILL.md | 12 +++-- .../skills/isolated-setup-update/SKILL.md | 6 ++- .../conditional-checks.md | 8 +++- .../skills/override-upstream/SKILL.md | 8 ++-- plugins/magpie-setup/skills/setup/config.md | 6 +-- .../setup_preflight/isolated_fingerprint.py | 2 +- plugins/magpie-setup/skills/setup/verify.md | 4 +- .../magpie-setup/skills/upstream-fix/SKILL.md | 8 ++-- tools/adversarial-review/README.md | 4 +- .../commands/adversarial-review.md | 8 ++-- .../hooks/link-stable-path.sh | 39 ++++++++++++++++ .../src/adversarial_review/commands.py | 46 +++++++++++++------ .../adversarial-review/tests/test_commands.py | 37 +++++++++++---- tools/dev/blocks/pre-pr-adversarial-review.md | 6 +-- tools/dev/check-family-plugins.py | 23 +++++++++- tools/sandbox-lint/expected.json | 2 +- .../sandbox-lint/src/sandbox_lint/__init__.py | 12 +++++ tools/sandbox-lint/tests/test_validator.py | 18 ++++++++ .../specs/agent-isolation-sandbox.md | 9 ++-- .../specs/marketplace-distribution.md | 8 ++-- 36 files changed, 278 insertions(+), 110 deletions(-) create mode 100755 tools/adversarial-review/hooks/link-stable-path.sh diff --git a/.claude/settings.json b/.claude/settings.json index 54d23de5e..b8bbae2ee 100644 --- a/.claude/settings.json +++ b/.claude/settings.json @@ -7,7 +7,7 @@ "uv run --project ~/.claude/magpie/vetted-ops vetted-op-read *", "uv run --project ~/.claude/magpie/vetted-ops vetted-op-tracker *", "uvx --from ~/.claude/magpie/vetted-ops vetted-op-read *", - "uvx --from ~/.claude/plugins/cache/apache-magpie/magpie-adversarial-review/*/tools/adversarial-review adversarial-review *", + "uvx --from ~/.claude/magpie/adversarial-review adversarial-review *", "uvx --from ~/.claude/magpie/vetted-ops vetted-op-tracker *", "~/.claude/scripts/magpie-run-evals.sh *" ], diff --git a/docs/setup/secure-agent-setup.md b/docs/setup/secure-agent-setup.md index ef2cda401..55f97f1fe 100644 --- a/docs/setup/secure-agent-setup.md +++ b/docs/setup/secure-agent-setup.md @@ -507,13 +507,18 @@ below, annotated. // and their runner refuses any gh call outside repos//. It keeps // its `ask` below. Never exclude `vetted-op`: that runs the whole write // catalogue unsandboxed. + // The adversarial-review tool runs the reviewer CLIs, which need network + // and their own credentials. Like the vetted-ops entries, it names the + // fixed link its plugin's SessionStart hook maintains, never a `*` where + // the plugin version sits: that would also match `uv` options spliced in + // at that position and run them unsandboxed. "excludedCommands": [ "gh *", "uv run --project ~/.claude/magpie/vetted-ops vetted-op-read *", "uvx --from ~/.claude/magpie/vetted-ops vetted-op-read *", "uv run --project ~/.claude/magpie/vetted-ops vetted-op-tracker *", "uvx --from ~/.claude/magpie/vetted-ops vetted-op-tracker *", - "uvx --from ~/.claude/plugins/cache/apache-magpie/magpie-adversarial-review/*/tools/adversarial-review adversarial-review *" + "uvx --from ~/.claude/magpie/adversarial-review adversarial-review *" ], // The `lychee` link-check hook runs in OFFLINE mode (`offline = // true` in `.lychee.toml`): it validates only local cross-file and @@ -3400,12 +3405,14 @@ below and report ✓ done / ✗ missing / ⚠ partial, with the evidence 14. **Adversarial-review exclusion**, if the `magpie-adversarial-review` plugin is installed (n/a otherwise). `sandbox.excludedCommands` contains - `"uvx --from ~/.claude/plugins/cache/apache-magpie/magpie-adversarial-review/*/tools/adversarial-review adversarial-review *"`, + `"uvx --from ~/.claude/magpie/adversarial-review adversarial-review *"`, `permissions.deny` contains `Edit(~/.claude/plugins/cache/apache-magpie/magpie-adversarial-review/**)`, and **no** `permissions.allow` entry covers the tool. A missing exclusion is ⚠ (every reviewer reports `unavailable` from inside - the sandbox); a missing deny is ✗ (the exclusion runs that code + the sandbox); the older `magpie-adversarial-review/*/tools/adversarial-review` + exclusion is ✗ (its `*` also matches options spliced in where the version + sits); a missing deny is ✗ (the exclusion runs that code unsandboxed); an `allow` is ✗ (each run sends the change to other model providers and must keep its prompt). 15. **Working directories under the read block**, if diff --git a/plugins/magpie-adversarial-review/.claude-plugin/plugin.json b/plugins/magpie-adversarial-review/.claude-plugin/plugin.json index 2e25bbb62..97317eefd 100644 --- a/plugins/magpie-adversarial-review/.claude-plugin/plugin.json +++ b/plugins/magpie-adversarial-review/.claude-plugin/plugin.json @@ -8,5 +8,18 @@ }, "homepage": "https://magpie.apache.org/", "repository": "https://github.com/apache/magpie", - "license": "Apache-2.0" + "license": "Apache-2.0", + "hooks": { + "SessionStart": [ + { + "hooks": [ + { + "type": "command", + "command": "bash \"${CLAUDE_PLUGIN_ROOT}/tools/adversarial-review/hooks/link-stable-path.sh\"", + "timeout": 10 + } + ] + } + ] + } } diff --git a/plugins/magpie-issue/skills/fix-workflow/pre-pr-adversarial-review.md b/plugins/magpie-issue/skills/fix-workflow/pre-pr-adversarial-review.md index ae4f95689..f3da97963 100644 --- a/plugins/magpie-issue/skills/fix-workflow/pre-pr-adversarial-review.md +++ b/plugins/magpie-issue/skills/fix-workflow/pre-pr-adversarial-review.md @@ -52,11 +52,11 @@ exclusion matches; a quoted or expanded path stays sandboxed and every reviewer reports `unavailable`: ```bash -uvx --from ~/.claude/plugins/cache/apache-magpie/magpie-adversarial-review//tools/adversarial-review adversarial-review run --project-root --repo-dir --base --title "" --body-file +uvx --from ~/.claude/magpie/adversarial-review adversarial-review run --project-root --repo-dir --base --title "" --body-file ``` -`` is the newest directory under -`~/.claude/plugins/cache/apache-magpie/magpie-adversarial-review/`. The body +The plugin points `~/.claude/magpie/adversarial-review` at its installed version +at the start of every session. The body file must sit in the checkout or a temporary directory; the tool refuses any other path. For a patch someone else proposed, replace `--base … --body-file …` with `--target pr: --repo `; for a diff file, with diff --git a/plugins/magpie-pr-management/skills/code-review/adversarial.md b/plugins/magpie-pr-management/skills/code-review/adversarial.md index 5d5a9591a..f41ea600c 100644 --- a/plugins/magpie-pr-management/skills/code-review/adversarial.md +++ b/plugins/magpie-pr-management/skills/code-review/adversarial.md @@ -85,11 +85,11 @@ The agent runs the reviewers itself, at Step 5 of exclusion matches: ```bash - uvx --from ~/.claude/plugins/cache/apache-magpie/magpie-adversarial-review//tools/adversarial-review adversarial-review run --reviewers --target pr: --repo --project-root --repo-dir + uvx --from ~/.claude/magpie/adversarial-review adversarial-review run --reviewers --target pr: --repo --project-root --repo-dir ``` - `` is the newest directory under - `~/.claude/plugins/cache/apache-magpie/magpie-adversarial-review/`. Omit + The plugin points `~/.claude/magpie/adversarial-review` at its installed version + at the start of every session. Omit `--reviewers` when the list came from `adversarial-review.md`. - **What leaves the machine.** The PR's diff, title and body go to each diff --git a/plugins/magpie-release-management/skills/announce-draft/SKILL.md b/plugins/magpie-release-management/skills/announce-draft/SKILL.md index 2654581f1..a9f1e928a 100644 --- a/plugins/magpie-release-management/skills/announce-draft/SKILL.md +++ b/plugins/magpie-release-management/skills/announce-draft/SKILL.md @@ -18,7 +18,7 @@ argument-hint: " [--planning-issue ]" capability: capability:resolve surface_hash: sha256:edffafcd9d9948ab license: Apache-2.0 -measured_tokens: 6825 +measured_tokens: 6809 ---