fix(disk-hygiene): retract false PowerShell non-firing claim, document the real bare-argv drop (#1195) - #1197
Conversation
…t the real bare-argv drop (#1195) 0.8.2 shipped a safety-model claim that PreToolUse guards do not intercept PowerShell-tool commands, scoped behind a preview caveat. A fresh-session controlled test falsified it: a Bash|PowerShell matcher DOES fire for the PowerShell tool on 2.1.218, payload tool_name is literally "PowerShell", and a live Set-Content through that tool was blocked. No harness divergence, no preview limitation — 0.8.2 asserted an un-isolated inference as fact. Removed. The real defect, now documented: hooks/hooks.json passes a bare ${user_config.disk_hygiene_enabled}, and upstream never implemented the declared userConfig default, so an unset-but-defaulted token is neither substituted nor exported to CLAUDE_PLUGIN_OPTION_* and its presence drops the entire hook entry. The engine gate has therefore never run for any consumer who never set the key — on Bash and PowerShell alike, which is the real shape of the reported bypass. The skill-scoped belt carries no such token and is unaffected. Code fix tracked separately. disk-hygiene 0.8.3. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
Claude finished @kyle-sexton's task in 1m 52s —— View job PR Review
This is a docs-only correction (disk-hygiene 0.8.3) retracting a false PowerShell safety claim from 0.8.2 and replacing it with an accurate description of the real defect. The changes are internally consistent and well-reasoned. What Changed
CorrectnessThe retraction is accurate. 0.8.2 asserted that a The real defect is described accurately. The bare The skill-scoped belt / plugin-level gate distinction is preserved correctly. Both Cross-file consistency: The inline caveat in Observations (non-blocking)
VerdictNo correctness bugs or security issues. The documentation now accurately reflects the behavioral reality: PreToolUse hooks do fire for the PowerShell tool, and the plugin-level engine gate has been silently inert for unconfigured consumers since initial deployment. The scope is properly limited to a docs retraction, and the code fix is correctly deferred. Safe to merge. |
|
Claude finished @kyle-sexton's task in 1m 6s —— View job Security Review — disk-hygiene 0.8.3Scope: Changed files only (
FindingsNo security vulnerabilities found in the changed files. All four files contain only documentation and a version bump. Full analysis below:
|
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: a28124f2c2
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
…re-argv drop (#1195) README still told consumers the engine gate "fires in every session" and that audit-only mode is "guard-enforced ... in every session". Per the proven bare-argv drop, the gate only registers once disk_hygiene_enabled is explicitly configured; on a default (unconfigured) install it does not run at all. Both claims scoped to the configured case with the caveat; the skill self-enforcement is named as the primary kill-switch honoring on a default install. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
Claude finished @kyle-sexton's task in 46s —— View job Security Review — disk-hygiene 0.8.3
Scope: Changed files only — No security vulnerabilities were introduced by this PR. Full analysis below.
|
|
Claude finished @kyle-sexton's task in 1m 45s —— View job PR Review
This is a docs-only retraction (disk-hygiene 0.8.3) retracting the false PowerShell non-firing claim from 0.8.2 and replacing it with accurate documentation of the real bare-argv drop defect. The latest commit ( What Changed (all three commits)
README.md — new commit (
|
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 3d658d497a
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
…ine-gate claims (#1195) Codex review + a full sweep found three more spots that still told consumers the plugin-level engine gate fires in every session / audit-only is guard-enforced, contradicting this retraction: clean SKILL.md Gotchas, setup SKILL.md step 1 (direct hygiene.py invocations "meet the engine gate"), and safety-model.md's kill-switch-reach paragraph. All three scoped to the explicitly-configured case with the bare-argv-drop caveat, so a default-configured consumer no longer gets contradictory safety guidance. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
Claude finished @kyle-sexton's task in 1m 7s —— View job Security Review — disk-hygiene 0.8.3Scope: Changed files only —
No security vulnerabilities were introduced by this PR. Per-file analysis
Security-relevant observationsThe substantive changes accurately document a pre-existing security limitation — not introduce one:
Points confirmed as clear:
Out of scope (pre-existing, not introduced here)The documented defect — engine gate inert on default installs — remains unresolved in the plugin until the upstream Verdict: docs-only retraction; no new attack surfaces, no permission widening, no injection risk, no GitHub Actions workflow changes, no secret handling. The documentation now more accurately describes the true security posture than 0.8.2 did. Safe to merge from a security standpoint. |
|
Claude finished @kyle-sexton's task in 1m 47s —— View job PR Review — disk-hygiene 0.8.3 (commit
|
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: b5c4a6617d
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
…d toggle (#1195) Review P2: the 0.7.0 trust-surface record still said the plugin-level hook "launches in every consumer session" and charged one python3 launch per Bash/ PowerShell call — contradictory in the exact default configuration this retraction corrects. Conditioned on disk_hygiene_enabled being explicitly configured, with the bare-argv-drop caveat: on a default install the hook does not register or launch, so neither the trust surface nor its per-call cost applies until the option is set. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
Claude finished @kyle-sexton's task in 1m 34s —— View job PR Review — disk-hygiene 0.8.3 (commit
|
| Claim | Status |
|---|---|
Engine gate only registers when disk_hygiene_enabled is explicitly configured |
✓ all files |
Skill-scoped belt carries no ${user_config.*} token, unaffected by the drop |
✓ all files |
| `Bash | PowerShell` PreToolUse hooks DO fire for the PowerShell tool |
| Trust surface and per-call cost conditional on configured install | ✓ README.md (new) |
Recheck marker #46477 / #39455 / #39827 |
✓ CHANGELOG, SKILL.md, safety-model.md |
Carried-forward minor observations (non-blocking, pre-existing)
-
safety-model.mdline 171–172: "Recheck when the upstream gap closes (#46477 / #39455 / #39827)." runs directly into "The gate defers instantly (no output)…" with no paragraph break. Noted in theb5c4a66review; not introduced here; readable as-is. -
README.mdline 60: "And the skill-scoped belt…" is a grammatical seam left from where the caveat paragraph was inserted. Noted in prior reviews; out of scope here.
Neither item is a correctness issue.
Verdict
All Codex P2 findings are resolved. No correctness bugs, no security regressions, no new attack surfaces. The documentation now accurately represents the behavioral reality — including the trust-surface and per-call-cost claims — across all six changed files. Safe to merge.
|
Claude finished @kyle-sexton's task in 49s —— View job Security Review — disk-hygiene 0.8.3
Scope: Changed files only — No security vulnerabilities were introduced by this PR. Per-file analysis
Security checks
Pre-existing defect now documented (not introduced here)The substantive changes accurately document a pre-existing security limitation rather than introduce one. The plugin-level engine gate in This is the correct course of action: documenting a known security gap reduces false trust without widening any attack surface. The remaining protection (skill-scoped frontmatter belt, documented accurately in every updated file) is correctly scoped and unaffected. VerdictDocs-only retraction; no new attack surfaces, no permission widening, no injection risk, no GitHub Actions workflow changes, no secret handling. The documentation now more accurately describes the true security posture than 0.8.2 did. Safe to merge from a security standpoint. |
|
CI classification: all checks green; all Codex review threads resolved after four rounds of caveat completion — every doc that claimed the plugin-level engine gate 'fires in every session' or that audit-only is 'guard-enforced' (clean + setup SKILL.md, safety-model.md, README ×3: two-surface record, config section, trust-surface record) is now conditioned on |
…tly (#1242) ## Summary The `disk_hygiene_enabled` kill switch was **inert on a default install**. The plugin-level engine-gate hook (`hooks/hooks.json`) carried a bare `${user_config.disk_hygiene_enabled}` argument; because the declared userConfig `default` is unimplemented upstream (#46477 / #39455 / #39827), an unset-but-defaulted token **drops the whole hook entry**, so the gate never ran. The skill-frontmatter belt could not receive the value either (skill hooks get neither the `${user_config.*}` substitution nor `CLAUDE_PLUGIN_OPTION_*`). Audit-only mode therefore degraded from **deny-outright** to **prompt-gated**. **Closes #1019.** ## Fix Both guard surfaces now resolve the toggle by **reading `disk_hygiene_enabled` directly** from user-scope `pluginConfigs` in `settings.json`, via a new shared `lib/killswitch_config.py` reader: - **Located from the tamper-resistant `${CLAUDE_PLUGIN_ROOT}`** both surfaces already receive (fallback to `CLAUDE_CONFIG_DIR`/`HOME` only when `--plugin-root` is absent — the report CLI / unit tests). - **Environment is never consulted** for the toggle or the settings path: a repo `.claude/settings.json` `env` block reaches hook subprocesses and carries no provenance. Since CC **2.1.207** `pluginConfigs` is honored only from user/managed/`--settings` scope (project/local ignored), so a hostile repo cannot forge it. - **Fails closed to enabled** on any absent/unreadable/ambiguous read. - The bare `${user_config.*}` argv is removed from `hooks.json` (fixing the hook-drop); `kill_switch_probe.py` now delegates to the same reader (its single-line JSON contract unchanged). ## Design note — supersedes the planned SessionStart + state-file ("C′") The two enforcement surfaces are the **same script through one resolve point** (`destructive_guard.py`), so there is nothing to distribute between sessions or surfaces. A direct read is a **smaller trust surface** (a settings *read*, no state-file *write*), honors a mid-session settings change, and needs no session-start timing dependency. Semantics are unchanged from the locked resolver decision (read user-scope `pluginConfigs`, ignore env, fail closed to enabled). ## Enforcement reach (accurate) - **Bash engine invocations** → denied outright in audit-only by the always-on engine gate, whether or not the clean skill is active. - **PowerShell deletion spellings** → denied outright by the skill-scoped belt **while `/disk-hygiene:clean` is active** (the always-on gate defers on non-engine commands). ## §643 plugin-acceptance security review — PASS The one new surface is a **settings-file read** (criterion 4). The playbook's own carve-out sanctions reading the plugin's own documented `~/.claude` user-global config; it's located from `${CLAUDE_PLUGIN_ROOT}` with no `../` reach-out into consumer-repo data. Criterion 1 (hooks) is *reduced* — a `${user_config.*}` arg removed. Surfaces 2/5/6/7 untouched. Trust-surface delta recorded in `README.md` + `CHANGELOG.md`. ## Testing - `test_hygiene.py`: **179 tests OK** (new `DirectReadKillSwitchTests` covering the plugin-root channel, `CLAUDE_CONFIG_DIR` fallback, tamper-resistance, env/argv ignored, fail-closed; new `hooks.json` contract test locking `--plugin-root` present + bare `${user_config.*}` gone; guard helpers made hermetic). - `test_kill_switch_probe.py`: **17 tests OK, unchanged** (behavior-preserving extraction). - markdownlint clean; `hooks.json`/`plugin.json` valid. ## Related - #1195 — the reported "PowerShell bypass" whose real cause (inert engine gate) this fixes. - #1197 — disk-hygiene 0.8.3, which documented this defect and left the code fix to a follow-up (this PR). - #1106 — the split-registration (Option E) decision that introduced the plugin-level engine-gate trust surface updated here. - Upstream userConfig `default` gap this works around: #46477 / #39455 / #39827 (recheck if resolved). 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Summary
Retracts a wrong claim this repo shipped in disk-hygiene 0.8.2. That release documented — in the
safety model — that "PreToolUse guards do not intercept PowerShell-tool commands," scoped behind a
preview caveat. A fresh-session controlled test falsified it:
Bash|PowerShellPreToolUse matcher does fire for the PowerShell tool on 2.1.218,tool_nameis literallyPowerShell(andCLAUDE_TOOL_NAMEis not a hook env var —hooks must read
tool_namefrom stdin),Set-Contentthrough the PowerShell tool was blocked by guardrails.No harness firing divergence, no preview limitation. 0.8.2 asserted an un-isolated inference as fact;
this removes it.
The real defect, now documented accurately:
hooks/hooks.jsonpasses a bare${user_config.disk_hygiene_enabled}. Upstream never implemented the declared userConfigdefault, soan unset-but-defaulted token is neither substituted nor exported as
CLAUDE_PLUGIN_OPTION_*, and itspresence drops the entire hook entry. The plugin-level engine gate has therefore been inert for every
consumer who never explicitly set the key — on Bash and PowerShell alike, which is the real shape of
the reported "PowerShell bypass". The skill-scoped belt carries no such token and is unaffected.
Docs-only retraction (disk-hygiene 0.8.3). The two code fixes — guardrails' PowerShell producer-scan
coverage gap, and a kill-switch delivery channel that doesn't depend on the unimplemented
default—are tracked separately.
Test plan
claude -psubprocesses, probe plugin, token-free control hooks):token-carrying hooks vanish when the key is unset while token-free controls fire, and reappear
unchanged once the key is configured → the bare-argv drop is proven, not inferred. An unset key is
also not exported to env (
CLAUDE_PLUGIN_OPTION_*unset), so an in-script default is mandatory.PreToolUse:PowerShellblock.skill-quality:check disk-hygiene/clean→ PASS (0 errors; markdownlint clean; triggers preserved).Related
Closes #1195 (reopened with the falsification recorded; its original diagnosis was wrong).
Upstream gap behind the real defect: anthropics/claude-code #46477 / #39455 / #39827 (
userConfigdefaultdeclared but never applied to substitution or env export).