You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Commit 20df286
Browse filesBrowse the repository at this point in the historyBrowse files
fix(setup): address review of isolated-setup-verify checks
Check 10 counted a key as hardware only for an sk- public key or an
OpenPGP-format card. ssh-format signing through gpg-agent uses the
card's aut slot and shows a plain ssh-ed25519 or ssh-rsa key, so a card
gpg --card-status finds now counts whatever gpg.format is.
Check 7a was inconclusive on every correctly secured host, because with
denyRead covering ~/ every path under it looks missing. It now proves
the denial with a sentinel: a file known to exist on the host, such as
the shell rc, that the sandbox cannot see. Inconclusive is left for the
case with no sentinel.
The step-1 output spec gains the inconclusive and unchecked verdicts
the skill now emits, with cases for both and a step-2 case for the
unchecked routing. The rest is review follow-up: managed settings in
check 1's merge, the opt-in question for flagged variable names, the
running claude-code version beside an unchecked check 5, the
credential-shaped wording of the no-print rule, the update prompt's
denial probes, and probe output attributed to stat in the fixtures.
Generated-by: Claude Code (Claude Opus 5.5)
Copy file name to clipboardExpand all lines: plugins/magpie-setup/skills/isolated-setup-verify/SKILL.md
+14-7Lines changed: 14 additions & 7 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -18,7 +18,7 @@ when_to_use: >-
18
18
capability: capability:platform
19
19
surface_hash: sha256:ce33cb9437d44992
20
20
license: Apache-2.0
21
-
measured_tokens: 8586
21
+
measured_tokens: 8836
22
22
---
23
23
24
24
<!-- Placeholder convention (see AGENTS.md#placeholder-convention-used-in-skill-files):
@@ -84,7 +84,7 @@ Local modifications go in the override file; framework changes go via PR to `apa
84
84
The doc-allowed "richer custom statusLine" that embeds the framework's sandbox-prefix logic in a larger script is ⚠ (the by-name helper is absent, but the functionality is there).
85
85
Use ⚠ for any *intentional* variation from the doc default; ✗ only for genuine gaps.
86
86
-**Surface evidence.** Each check's report line names the file path, version string, command output, or `sandbox.enabled` value; never just "✓" or "✗" alone.
87
-
-**Never print a credential.** No probe may print a credential file's contents or an environment variable's value: probe files with `test -r`, never `cat` or `head`, and list variables by name only.
87
+
-**Never print a credential.** No probe may print a credential file's contents or a credential-shaped variable's value: probe files with `test -r`, never `cat` or `head`, and list variables by name only.
88
88
-**Sandbox placeholders are not files.** Inside the Claude Code sandbox a denied path that does not exist on the host can appear as a `/dev/null` bind mount: `stat -c '%F %t,%T' <path>` prints `character special file 1,3`.
89
89
`git status` lists such placeholders as untracked (`.bash_profile`, `.claude/settings.json`, `.mcp.json`), and reading one returns nothing, so an empty settings file seen this way is not evidence for checks 1 or 6.
90
90
Take the host state from git (`git ls-files <path>`, `git log -- <path>`) or from a read the user runs outside the sandbox, and never delete the placeholder.
@@ -100,9 +100,10 @@ Look, in order, for: the current repository when it is the framework itself; an
100
100
A candidate counts only when it has `tools/agent-isolation/pinned-versions.toml`; a copy under `~/.claude/agent-isolation/` is not a checkout.
101
101
With none found, ask the user for the path once.
102
102
With no checkout at all, report check 5's version comparisons, 10a's script comparison and 13c as **unchecked — no framework checkout**, not ✗ or ⚠, and say which path to supply.
103
+
Check 5 still reports the running `claude --version` beside that verdict.
103
104
104
105
1. Effective settings shape: `sandbox.enabled: true`, `permissions.deny`, `permissions.ask`, `sandbox.network.allowedDomains`, the `sandbox.filesystem` allowlist (`allowRead`/`allowWrite`), and `sandbox.filesystem.denyRead` containing `~/` (or the resolved absolute home).
105
-
Evaluate the merge of the project `.claude/settings.json`, the project-local `.claude/settings.local.json` and the user-scope `~/.claude/settings.json`, and name the file each key came from.
106
+
Evaluate the merge of the project `.claude/settings.json`, the project-local `.claude/settings.local.json`, the user-scope `~/.claude/settings.json` and, where readable, the managed settings (`/etc/claude-code/managed-settings.json` and `managed-settings.d/` on Linux, `/Library/Application Support/ClaudeCode/` on macOS, `C:\Program Files\ClaudeCode\` on Windows), and name the file each key came from.
106
107
A missing `denyRead` home entry is ✗: Linux bubblewrap reads are deny-only, so without it every credential file under `~/` stays readable to sandboxed commands (`tools/sandbox-lint` enforces the same rule).
107
108
A missing committed project `.claude/settings.json` is not ✗ on its own when the merged settings pass; say which file carries them.
108
109
2. User-scope `~/.claude/settings.json` wiring: `PreToolUse``Bash` matcher → `sandbox-bypass-warn.sh`, `PostToolUse``Bash` matcher → `sandbox-error-hint.sh`, `statusLine` → `sandbox-status-line.sh` (or a custom statusline script that embeds the framework's prefix logic, the doc-allowed variant; report ⚠).
@@ -138,10 +139,14 @@ With no checkout at all, report check 5's version comparisons, 10a's script comp
138
139
-**7a — credential files.** For each of `~/.aws/credentials`, the `~/.ssh/id_*` private keys (not `*.pub`), `~/.config/gh/hosts.yml`, `~/.netrc` and `~/.docker/config.json`, run `test -r <path>` and `stat` it (`stat -c '%F %t,%T'` on Linux, `stat -f %HT` on macOS).
139
140
The first one the sandbox reports readable is ✗, unless `stat` prints `character special file 1,3` (a sandbox placeholder, which counts as denied).
140
141
`Operation not permitted` from `stat` (Seatbelt) is a denial of a file that exists: ✓.
141
-
Under bubblewrap a denied file and a missing one look the same, so when no candidate is visible report 7a as **inconclusive**, not ✓, and ask the user to `ls` the candidates from their own terminal; ✓ once one of them exists on the host.
142
+
Under bubblewrap a denied file and a missing one look the same, so a candidate that is not visible proves nothing on its own.
143
+
When check 1 found `denyRead` covering `~/`, prove the denial with a sentinel: a file under `~/` known to exist on the host (such as the shell rc check 4 read) that no `allowRead` entry covers.
144
+
A sandboxed `test -e <sentinel>` that fails proves the denial: ✓.
145
+
Only with no sentinel available is 7a **inconclusive**: ask the user to `ls` the candidates from their own terminal, and report ✓ once one exists there that the sandbox cannot see.
142
146
-**7b — clean environment.** List the variable names, never the values: `env | cut -d= -f1 | sort`.
143
147
Flag every credential-shaped name (containing `TOKEN`, `SECRET`, `PASSWORD` or `API_KEY`, ending in `_KEY`, or starting with `AWS_`, `GH_`, `GITHUB_` or `AZURE_`) that is not on the `agent-iso.sh` passthrough list; any such name is ⚠, listed by name.
144
148
A single empty variable proves nothing: the reference `sandbox.credentials` already unsets `AWS_ACCESS_KEY_ID` for sandboxed commands, wrapper or not.
149
+
An `AGENT_ISO_ALLOW` / `CLAUDE_ISO_ALLOW` opt-in is invisible from inside the session, so ask the user whether a flagged name was opted in before calling it a gap.
145
150
Report `CLAUDE_CODE_ENTRYPOINT`.
146
151
An IDE extension's value (such as `claude-vscode`) means the session never went through `claude-iso`, so no wrapper stripped its environment: ⚠, with the credential-shaped names found or "none found" ([why](../../../../docs/setup/secure-agent-setup.md#the-clean-env-wrapper)).
147
152
-**7c — network.**`curl https://example.com` should deny at the permission-prompt layer (`Permission to use Bash with command curl … has been denied`).
@@ -175,7 +180,9 @@ With no checkout at all, report check 5's version comparisons, 10a's script comp
175
180
176
181
10.**Hardware-key touch overlay and the signing key.** Only when commits are signed (`git config --get commit.gpgsign` is `true`) or a remote is reached over ssh through gpg-agent (`SSH_AUTH_SOCK` names its socket); otherwise **n/a**.
177
182
10c applies to any ssh-format signing key.
178
-
10a, 10b, 10d and 10e apply only to a hardware key and are **n/a** otherwise: with `gpg.format=ssh` the public key line starts with `sk-` (`sk-ssh-ed25519@openssh.com`, `sk-ecdsa-sha2-nistp256@openssh.com`); with OpenPGP, `gpg --card-status` finds a card (the user runs it when the sandbox cannot see the device).
183
+
10a, 10b, 10d and 10e apply only to a hardware key and are **n/a** otherwise.
184
+
A hardware key is an `sk-` public key (`sk-ssh-ed25519@openssh.com`, `sk-ecdsa-sha2-nistp256@openssh.com`), or a card `gpg --card-status` finds, whatever `gpg.format` is: ssh-format signing through gpg-agent uses the card's `aut` slot and shows a plain `ssh-ed25519` or `ssh-rsa` key.
185
+
The user runs `gpg --card-status` when the sandbox cannot see the device.
For a missing `denyRead` home entry, name the settings file to add it to.
294
-
-**Unchecked — no framework checkout** on check 5, 10a or 13c → not a failure; ask the user for their `magpie` clone's path, or to clone one ([install item 2](../isolated-setup-install/SKILL.md)), and re-run.
295
-
- Inconclusive 7a → the user runs `ls` on the candidate credential paths from their own terminal; one that exists there while the sandbox cannot see it is ✓.
301
+
-**Unchecked — no framework checkout** on check 5, 10a or 13c → not a failure; re-run `setup-isolated-setup-verify` with the path of the user's `magpie` clone, cloning one first if they have none ([install item 2](../isolated-setup-install/SKILL.md)).
302
+
- Inconclusive 7a (no sentinel) → the user runs `ls` on the candidate credential paths from their own terminal; one that exists there while the sandbox cannot see it is ✓.
296
303
- ⚠ on check 7b (an IDE-launched session, or credential-shaped names in the environment) → no skill fixes it: start the session through `claude-iso` from a terminal, or keep secrets out of the shell that starts the IDE and rely on `sandbox.credentials` ([The clean-env wrapper](../../../../docs/setup/secure-agent-setup.md#the-clean-env-wrapper)).
297
304
-**✗ on check 5 (claude-code below the `min_version` floor, running under Claude Code)** → **hard fail; stop.**
298
305
Tell the operator to upgrade (`npm install -g --no-save @anthropic-ai/claude-code@latest`) and re-run; the setup cannot be certified on a below-floor runtime.
Copy file name to clipboardExpand all lines: tools/skill-evals/README.md
+1-1Lines changed: 1 addition & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -35,7 +35,7 @@ Suites are currently implemented for:
35
35
-**pr-management-stats** — 6 cases across 1 step (build-loop)
36
36
-**pr-management-triage** — 33 cases across 6 steps (backport-check, classify-loop, deliver-note, guard-reroute, terminal-links, interaction-progress); the pre-filters, decision table and pagination are pytest-tested in `tools/pr-management`
37
37
-**list-skills** — 8 cases across 2 steps (step-1-command, step-2-present)
Copy file name to clipboardExpand all lines: tools/skill-evals/evals/setup-isolated-setup-verify/README.md
+3-3Lines changed: 3 additions & 3 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -5,14 +5,14 @@
5
5
6
6
Behavioral evals for the `setup-isolated-setup-verify` skill.
7
7
8
-
## Suites (31 cases total)
8
+
## Suites (34 cases total)
9
9
10
10
| Suite | Step | Cases | What it covers |
11
11
|---|---|---|---|
12
12
| codex-native | Codex security model | 3 | untrusted or absent guard despite project trust, unsafe vetted write allowance, and the credential read-isolation gap |
13
13
| runtime-routing | Runtime routing | 2 | Codex and Gemini route to their native adapters and never require Claude files |
14
-
| step-1-classify | The 12 checks | 20 | all-pass, sandbox disabled, missing scripts, version drift, project root missing, injection attempt, signing key unreadable in the sandbox (check 10), dev-tool paths missing from settings.local.json (check 8 ⚠), whole-user hook dir unreadable from the sandbox (check 8 ✗), working directories missing under the read block with a glob entry (check 15 ⚠), roborev installed with `review` and `show` auto-allowed no config-edit deny and an incomplete `ask` list (check 17b, 17d and 17e ✗), roborev's hook replacing the shared whole-user dispatcher (check 17f ✗), `~/.roborev/` made writable with and without step B.2 (check 17c ✗), a roborev MCP server registered (check 17g ⚠), the vetted-ops checkout listed as a working directory (check 16f ✗), `denyRead` missing from every merged scope (check 1 ✗), settings only in `settings.local.json` beside a sandbox placeholder (check 1 ✓ naming the file), an IDE-launched session with credential-shaped variable names (check 7 ⚠), a plain `ssh-ed25519` signing key with `commit.gpgsign` on (check 10 ✓, the overlay sub-checks n/a) |
15
-
| step-2-recommend | After the report |6| all-pass, install needed, update needed, project-root missing, multiple gaps, roborev findings (settings edit by the operator, `.roborev.toml` left to them) |
14
+
| step-1-classify | The 12 checks | 22 | all-pass, sandbox disabled, missing scripts, version drift, project root missing, injection attempt, signing key unreadable in the sandbox (check 10), dev-tool paths missing from settings.local.json (check 8 ⚠), whole-user hook dir unreadable from the sandbox (check 8 ✗), working directories missing under the read block with a glob entry (check 15 ⚠), roborev installed with `review` and `show` auto-allowed no config-edit deny and an incomplete `ask` list (check 17b, 17d and 17e ✗), roborev's hook replacing the shared whole-user dispatcher (check 17f ✗), `~/.roborev/` made writable with and without step B.2 (check 17c ✗), a roborev MCP server registered (check 17g ⚠), the vetted-ops checkout listed as a working directory (check 16f ✗), `denyRead` missing from every merged scope (check 1 ✗), settings only in `settings.local.json` beside a sandbox placeholder (check 1 ✓ naming the file), an IDE-launched session with credential-shaped variable names (check 7 ⚠), a plain `ssh-ed25519` signing key with `commit.gpgsign` on (check 10 ✓, the overlay sub-checks n/a), no sentinel and no `ls` for 7a (check 7 inconclusive), no framework checkout (checks 5, 10 and 13 unchecked, installed versions still reported) |
15
+
| step-2-recommend | After the report |7| all-pass, install needed, update needed, project-root missing, multiple gaps, roborev findings (settings edit by the operator, `.roborev.toml` left to them), no framework checkout (pass, with a re-run once a clone exists) |
0 commit comments