feat: add guardrails plugin (bundled safety hooks)#42
Conversation
Migrate four medley PreToolUse guard hooks into one bundled marketplace
plugin whose core contract is per-hook kill switches:
- secret-pattern-detection (blocking) — high-confidence secret/credential
patterns in new content; HOOK_SECRET_PATTERN_DETECTION_ENABLED
- hardcoded-path-check (blocking) — machine-specific paths; bundles the
path-detection lib via ${CLAUDE_PLUGIN_ROOT};
HOOK_HARDCODED_PATH_CHECK_ENABLED
- block-no-verify (blocking) — git hook-bypass (--no-verify, core.hooksPath,
LEFTHOOK env prefix); inlines the literal-stripping the byte-identical
hook-utils.sh does not carry; HOOK_BLOCK_NO_VERIFY_ENABLED
- cli-flag-verify (advisory) — hallucinated CLI flags; bundles verify-cli-flag.sh
via ${CLAUDE_PLUGIN_ROOT}; HOOK_CLI_FLAG_VERIFY_ENABLED
Blocking semantics (exit 2 + stderr) preserved identically. Guards emit the
marketplace hook-telemetry envelope (status blocked/ok/skipped) with a
privacy-safe data payload (category labels only — never secrets, matched
paths, or full commands); adds per-hook data schemas + examples and the
Implementers-table rows. hook-utils.sh is a byte-identical copy of the shared
lib. Consumer seams: $CLAUDE_PROJECT_DIR scoping, git check-ignore, and env
overrides. Ships self-contained contract tests (119 assertions) covering each
guard's block/allow verdicts, disabled-path no-op, and telemetry.
Marketplace category: security (first non-formatting concern in the catalog).
…aths lane The guardrails plugin bundles lib/path-detection/hardcoded-path-patterns.sh, whose ERE bodies self-match the machine-specific-paths detector. Add an exclude pathspec to that lane (the ci-workflows action's `exclude` input). Pinned action SHA untouched.
Confirmed bypass: `git commit "--no-verify"`, `git commit --no-ver'ify'`, and `git -c "core.hooksPath=/dev/null" commit` all passed — strip_literals deleted the quoted spans while the shell strips those same quotes before git parses argv, so the flag reached git unblocked. Detect the bypass token on a QUOTE-COLLAPSED view of the command (quote characters removed, content kept — exactly what argv sees), while still gating on a real `git commit`/`git push` executable resolved from the span-stripped view (so `echo "git commit --no-verify"` stays a false-positive no-op). Trade-off is fail-closed: a literal --no-verify token inside a commit message is blocked (documented in README). Adds regression tests for all three bypass forms plus quoted-`-n` and single-quoted core.hooksPath. Also fails open with a visible stderr notice when jq is absent (no silent disable).
…wlist anchoring - Fail-open visibility: each guard now prints a one-line stderr notice and exits 0 when jq is absent, instead of silently disabling. - Telemetry redaction: when a path cannot be made repo-relative, data.file is reduced to its basename so no absolute path (embedding the developer's username) lands in a telemetry envelope. - Allowlist anchoring: the secret-pattern-detection dependency-cache exemptions (node_modules, .venv) are anchored to a path-segment boundary, so a directory merely CONTAINING the name (evil_node_modules/, .venv-backup/) is scanned rather than exempted. - README: document lefthook-only env-manager coverage (husky/pre-commit not matched; --no-verify / core.hooksPath checks are manager-agnostic), the jq fail-open behavior, and the quote-transparent fail-closed trade-off. Adds secret-suite regression tests for the jq guard, allowlist anchoring (real segment exempt vs substring impostor scanned), and telemetry redaction.
Round-1 hardened flag DETECTION but left the executable/subcommand GATE on the span-stripped view, so quoting or escaping the git or commit token still defeated it. Five argv-verified bypasses passed: `"git" commit --no-verify`, `git "commit" --no-verify`, `g'i't commit --no-verify`, `git commit --no-\verify`, `"git" -c core.hooksPath=/dev/null commit`. Replace the flattened-string regex with an argv-faithful parser: split the command into top-level segments on UNQUOTED control operators (quote-aware, so an operator inside quotes does not split), tokenize each segment into argv words honoring '…', "…" and backslash escapes, then gate on the parsed executable (basenames to git) and its subcommand (commit/push after any -c k=v / global options). Bypass tests run over the same argv words. Because quoted whitespace stays within a word, a --no-verify inside a quoted -m value is a message word, not a flag — now correctly ALLOWED (the round-1 fail-closed FP is gone). Adds regression tests for all 5 bypass forms (exit 2) and the negatives that must stay exit 0 (quoted-arg echo, operator-in-quotes, command-not-found "git commit" form, flag-in-message-value). README updated to the argv-faithful claim.
…bundle -n, wrappers, case-fold, linear parser Round-2 closed the quoting class but left decidable argv-slot gaps. Replace the gate with a full argv-grammar parser and close them: - git global options that consume an argument (-C, --git-dir, --work-tree, --namespace, --super-prefix, --config-env, --attr-source, --exec-path) are modeled as two-word consumers, so `git -C . commit --no-verify` blocks. - Executable detection scans every argv word for a git basename (not a single leading slot), so wrappers with options pass through transparently: `env -i git …`, `nice git …`, `timeout 5 git …`, `sudo -u x git …`. - -n is detected in a leading short-option bundle only before the first argument-taking short (m/F/c/C/t/u/S/G): `-nm msg` / `-vn` block, `-mn` (m takes value "n") does not. - Executable match is case-insensitive + `.exe`-stripped on Windows/MSYS only (OS-gated like hook-utils normalize_path): `GIT` / `git.exe` block on Windows, stay case-sensitive on POSIX. - Decidable expansion subset closed in the tokenizer: `$'…'` ANSI-C words are decoded and backslash-newline continuations collapsed before segmenting. - Parser is single-pass linear (char array read once) with a 16 KB length cap that blocks fail-closed above it (obfuscation guard). README narrows the claim to the true surface and documents the residual: does NOT evaluate $VAR / $(…) / $IFS — a friction guard, not a sandbox. Adds regression tests for every form (61 cases, all prior negatives/positives kept).
Bugbot couldn't run - usage limit reachedBugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit. A user or team admin can review and increase usage limits in the Cursor dashboard. (requestId: serverGenReqId_9ab9cdd4-727b-4162-aada-f0018f87294b) |
|
Claude finished @kyle-sexton's task in 5m 51s —— View job PR Review: feat: add guardrails plugin (bundled safety hooks)
|
There was a problem hiding this comment.
Stale comment
Risk: medium. Not approving: Cursor Bugbot completed with status skipping (usage limit reached) and did not produce a clean review, so automated review is incomplete. Human review is needed; no reviewers were assigned because the only human repo contributor is the PR author.
Sent by Cursor Approval Agent: Pull Request Router and Approver
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 48f73b2ff0
ℹ️ 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".
Merge origin/main; keep marketplace entries for markdown-formatter, bash-lint, biome-format, ruff-format, eol-normalizer, desktop-notification, and guardrails. - Scope hardcoded-path-check to CLAUDE_PROJECT_DIR so outside-repo writes are not blocked. - Match git only at the segment command position (after env prefixes and known wrappers) in block-no-verify so diagnostic commands like echo git commit --trailer "Co-authored-by: Cursor <cursoragent@cursor.com>" --no-verify are allowed. Co-Authored-By: Claude <noreply@anthropic.com>
Bugbot couldn't run - usage limit reachedBugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit. A user or team admin can review and increase usage limits in the Cursor dashboard. (requestId: serverGenReqId_609a93c7-f739-47bf-8f69-4258caeb796b) |
|
Claude finished @kyle-sexton's task in 3m 15s —— View job PR Review: feat: add guardrails plugin (bundled safety hooks)
The latest commit ( What was fixedCodex P2 — Codex P2 — Bugs still open (from the prior Claude review at 48f73b2)1.
|
| Finding | Status |
|---|---|
resolve_git_index() command-position guard (Codex P2) |
✅ Fixed |
hardcoded-path-check scope guard (Codex P2) |
✅ Fixed |
| LEFTHOOK false positive on commit message values | ❌ Still open |
core.hooksPath false positive on commit message values |
❌ Still open |
| README "four PreToolUse guards" vs PostToolUse reality | ❌ Still open |
The two false-positive bugs are correctness issues that will block legitimate agent commits. Neither has a regression test. The architecture and the rest of the implementation remain solid.
There was a problem hiding this comment.
Stale comment
Risk: medium. Not approving: Cursor Bugbot completed with status skipping (usage limit reached) and did not produce a clean review, so automated review is incomplete. Human review is needed; no reviewers were assigned because the only human repo contributor is the PR author.
Sent by Cursor Approval Agent: Pull Request Router and Approver
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 4aca74b814
ℹ️ 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".
Co-Authored-By: Claude <noreply@anthropic.com> Co-authored-by: Cursor <cursoragent@cursor.com>
Bugbot couldn't run - usage limit reachedBugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit. A user or team admin can review and increase usage limits in the Cursor dashboard. (requestId: serverGenReqId_13d23f46-7f8f-46ad-b896-73c1514f17cb) |
There was a problem hiding this comment.
Stale comment
Risk: medium. Not approving: Cursor Bugbot completed with status skipping (usage limit reached) and did not produce a clean review, so automated review is incomplete. Human review is needed; no reviewers were assigned because the only human repo contributor is the PR author.
Sent by Cursor Approval Agent: Pull Request Router and Approver
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: d0f200ca4c
ℹ️ 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".
Keep marketplace entries for both guardrails and powershell-format after merging main. Harden block-no-verify (push --no-verify, shell prefixes, timeout options, scoped hooksPath checks) and normalize PROJECT_DIR trailing slashes in secret-pattern-detection. Co-authored-by: Cursor <cursoragent@cursor.com>
Bugbot couldn't run - usage limit reachedBugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit. A user or team admin can review and increase usage limits in the Cursor dashboard. (requestId: serverGenReqId_13a1a70b-caa5-41c1-8d6b-7f8f58a2bd8a) |
There was a problem hiding this comment.
Stale comment
Risk: medium. Not approving: Cursor Bugbot completed with status skipping (usage limit reached) and did not produce a clean review, so automated review is incomplete. Human review is needed; no reviewers were assigned because the only human repo contributor is the PR author.
Sent by Cursor Approval Agent: Pull Request Router and Approver
Resolve marketplace.json to include all main plugins plus guardrails. Add guardrails catalog row to README alongside actionlint from main. Co-authored-by: Cursor <cursoragent@cursor.com>
Bugbot couldn't run - usage limit reachedBugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit. A user or team admin can review and increase usage limits in the Cursor dashboard. (requestId: serverGenReqId_172da0fc-12ee-4a05-916c-e5636152f985) |
|
Claude finished @kyle-sexton's task in 3m 21s —— View job PR Review: feat: add guardrails plugin (bundled safety hooks)
Good progress since the prior reviews. Four of the five previously-identified issues are now closed; one correctness bug and one documentation inconsistency remain open. What's been fixed since the last Claude review
Remaining open bug1.
|
| Finding | Status |
|---|---|
resolve_git_index() command-position guard |
✅ Fixed |
hardcoded-path-check project scope guard |
✅ Fixed |
core.hooksPath false positive on commit message values |
✅ Fixed |
Timeout option parsing (-s KILL 5 git …) |
✅ Fixed |
Shell keywords/builtins (command git …, if git …) bypass |
✅ Fixed |
secret-pattern-detection trailing-slash scope guard |
✅ Fixed |
| LEFTHOOK false positive on commit message values | ❌ Still open |
| README "four PreToolUse guards" vs PostToolUse reality | ❌ Still open |
The architecture, the resolve_git_index wrapper handling, the -c/--config scoping for core.hooksPath, and the expanded test suite are all solid. The LEFTHOOK false positive is the one remaining correctness issue that would block legitimate agent commits.
There was a problem hiding this comment.
Stale comment
Risk: medium. Not approving: Cursor Bugbot completed with status skipping (usage limit reached) and did not produce a clean review, so automated review is incomplete. Human review is needed; no reviewers were assigned because the only human repo contributor is the PR author.
Sent by Cursor Approval Agent: Pull Request Router and Approver
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 8d4b6debce
ℹ️ 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".
Co-authored-by: Cursor <cursoragent@cursor.com>
Bugbot couldn't run - usage limit reachedBugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit. A user or team admin can review and increase usage limits in the Cursor dashboard. (requestId: serverGenReqId_04a26e83-8bb8-42b3-8101-5f5b6e78e752) |
PR Review: feat: add guardrails plugin (bundled safety hooks)
|
There was a problem hiding this comment.
Stale comment
Risk: medium. Not approving: Cursor Bugbot completed with status skipping (usage limit reached) and did not produce a clean review, so automated review is incomplete. Human review is needed; no reviewers were assigned because the only human repo contributor is the PR author.
Sent by Cursor Approval Agent: Pull Request Router and Approver
Co-Authored-By: Claude <noreply@anthropic.com> Co-authored-by: Cursor <cursoragent@cursor.com>
Bugbot couldn't run - usage limit reachedBugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit. A user or team admin can review and increase usage limits in the Cursor dashboard. (requestId: serverGenReqId_61a5e165-51c1-43e2-9512-cbc5637d5725) |
|
Claude finished @kyle-sexton's task in 3m 26s —— View job PR Review: feat: add guardrails plugin (bundled safety hooks)
Great progress. Every correctness bug and documentation issue from prior Claude and Codex review rounds is now closed — with one exception. What's fixed since the last review (commits
|
| Finding | Status |
|---|---|
LEFTHOOK false positive on commit message values (k < nseg → k < gi) |
✅ Fixed |
git commit -m --no-verify false block (skip -m consumed value before checking --no-verify) |
✅ Fixed |
nice -n 10 git commit --no-verify bypass (nice option parsing added to resolve_git_index) |
✅ Fixed |
time -p git commit --no-verify bypass (time split from keyword list, -p now consumed) |
✅ Fixed |
nice inner case statement missing *) break ;; default — shellcheck SC2249 (latest commit) |
✅ Fixed |
All prior bugs (resolve_git_index, core.hooksPath, secret-pattern-detection trailing slash, hardcoded-path-check scope) |
✅ Fixed (prior rounds) |
The SC2249 fix in commit 6955900 is correct: the *) break ;; default in the nice|nohup option-parsing while loop's inner case is unreachable (the while condition already filters to -* tokens), but shellcheck requires the explicit default and the fix is safe.
One remaining issue
README line 3 still contradicts the guard table
plugins/guardrails/README.md L3
A Claude Code plugin bundling four PreToolUse safety guards
The guard table at line 15 correctly identifies cli-flag-verify as PostToolUse · Write | Edit. The opening sentence contradicts it — this has been flagged in every prior review round.
One-line fix:
A Claude Code plugin bundling **three blocking PreToolUse guards and one advisory PostToolUse guard** that catch
Summary
The implementation is solid. The argv-grammar-faithful parser (resolve_git_index, the Form 1/2 scans, the tokenizer) is correct across quoting, escaping, wrappers, short-option bundles, and edge cases — verified by the 163-assertion test suite. All blocking/fail-open semantics are correctly chosen per context. The one outstanding item is a documentation-only inconsistency in the plugin README opening line.
There was a problem hiding this comment.
Stale comment
Risk: medium. Not approving: Cursor Bugbot completed with status skipping (usage limit reached) and did not produce a clean review, so automated review is incomplete. Human review is needed; no reviewers were assigned because the only human repo contributor is the PR author.
Sent by Cursor Approval Agent: Pull Request Router and Approver
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 6955900657
ℹ️ 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".
Second-round Codex findings on #42: handle env split-string operands by splicing the split words back into the resolver scan (results returned via RESOLVED_WORDS/RESOLVED_GI globals so the caller matches the rewritten argv), treat eval as a transparent prefix, and stop verify-cli-flag.sh option parsing at the first positional so a target flag named --quiet or --verbose is verified instead of consumed.
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
Bugbot couldn't run - usage limit reachedBugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit. A user or team admin can review and increase usage limits in the Cursor dashboard. (requestId: serverGenReqId_08bc6f8c-7820-49fd-b3b9-104a9790ca40) |
|
Claude finished @kyle-sexton's task in 3m 57s —— View job PR Review: feat: add guardrails plugin (bundled safety hooks)
Good progress — every correctness bug from prior review rounds is now closed. The latest commit addressed the three remaining Codex findings. One documentation inconsistency remains. What's fixed in
|
| Finding | Status |
|---|---|
LEFTHOOK false positive on commit message (k < gi upper bound) |
✅ Fixed (d7b95aa), test at line 122 |
git commit -m --no-verify (skip -m value before checking --no-verify) |
✅ Fixed (d7b95aa) |
core.hooksPath scoped to -c/--config args only |
✅ Fixed (0b62c39) |
resolve_git_index command-position guard |
✅ Fixed (4aca74b) |
hardcoded-path-check project scope guard |
✅ Fixed (4aca74b) |
secret-pattern-detection trailing-slash scope guard |
✅ Fixed (0b62c39) |
Shell keywords/builtins transparent (command, if, time -p) |
✅ Fixed (d7b95aa) |
Timeout option parsing (-s KILL 5 git …) |
✅ Fixed (d7b95aa) |
| SC2249 shellcheck default case | ✅ Fixed (6955900) |
One remaining issue
README line 3 contradicts the guard table — documentation
A Claude Code plugin bundling four PreToolUse safety guards
cli-flag-verify is PostToolUse, as the guard table at line 15 correctly shows. This one-word fix has been flagged in every prior review round. The table's description ("The three blocking guards feed their stderr message back to Claude…") at line 17 already uses the correct framing.
Suggested replacement:
A Claude Code plugin bundling **three blocking PreToolUse guards and one advisory PostToolUse guard** that catch
Summary
| Finding | Status |
|---|---|
env -S / --split-string bypass |
✅ Fixed (b76d1bb) |
eval bypass (unquoted form) |
✅ Fixed (b76d1bb) |
verify-cli-flag.sh consuming target flags as verifier options |
✅ Fixed (b76d1bb) |
| LEFTHOOK false positive on commit message values | ✅ Fixed (d7b95aa) |
| All prior bugs from earlier review rounds | ✅ Fixed |
| README "four PreToolUse guards" vs PostToolUse reality | ❌ Still open (docs only) |
The implementation is correct. The argv parser (resolve_git_index, Form 1/2 scans, tokenizer), wrapper handling, env -S splicing, and verifier option scoping are all sound. The single remaining item is a one-line documentation fix in the README opening sentence.
There was a problem hiding this comment.
Risk: medium. Not approving: Cursor Bugbot completed with status skipping (usage limit reached) and did not produce a clean review, so automated review is incomplete. Human review is needed; no reviewers were assigned because the only human repo contributor is the PR author.
Sent by Cursor Approval Agent: Pull Request Router and Approver
#218) ## Summary `code-tidying:tidy`'s Phase H ("Ship") told the agent to create the PR by calling `gh pr create` directly — a bare bypass of `source-control:pull-request`'s canonical create flow, which resolves issue-linkage (with an issue-existence check before shipping a `Closes #N` keyword), assembles the PR body injection-safely (quoted heredoc + parameter-expansion concat, a defense against shell injection through prompt-derived text), and gates PR creation on a valid closing keyword or explicit opt-out before calling `gh pr create` itself. Closes decisions **#3** and **#42** of the melodic-software issue/PR consistency initiative's Decisions Log (`https://claude.ai/code/artifact/232ecdce-8316-4880-8c0a-dc3c7dcf3a63`): - **#3 (formatting-tidy-canonical-pr-gate-bypass)** — "YES, fix — but not via direct skill-to-skill coupling... minimize coupling between `code-tidying:tidy` and `source-control:pull-request`... Likely shape: a shared script/check both skills call, or a convention-level requirement enforced by a common gate." This PR fixes the bypass without a hard cross-plugin dependency (below). - **#42 (crosscut-skill-content-sync-mechanism)** — "Single shared reference doc, all authoring skills import it." Applied here for its first concrete instance (see Design rationale). ## What changed Phase H now applies the same optional-plugin graceful-degrade pattern `tidy` already uses elsewhere in its own SKILL.md (Phase C for `discovery`, Phase D for `work-items`): - **If `source-control` is installed:** invoke `/pull-request create`, supplying tidy's own title/body-section content. Its stage-and-commit step (§2.3) is a no-op here since Phase E already committed the tidyings atomically — the call goes straight to rebase-check, issue-linkage resolution, and gated PR creation. - **If `source-control` isn't installed:** the same invariants apply inline — verify an issue exists before writing `Closes #N`, assemble the body via quoted heredoc + concat (never an unquoted `<<EOF`), and refuse `gh pr create` until the body carries a valid closing keyword or an explicit `Refs #N` / `No related issue:` opt-out. ## Design rationale (decision #42, recorded as an assumption — no user available to confirm interactively this session) Considered extracting the safety invariants into a new shared reference doc (mirroring the `docs/PLUGIN-ARTIFACT-PROTOCOL.md` → per-plugin `reference/artifact-protocol.md` precedent, drift-checked by `validate-plugin-contracts.mjs` and this session's new `scripts/check-cross-plugin-source-drift.sh`). Rejected for now: every invariant worth stating is already authoritative in `pull-request/reference/create.md`, so a copied doc would be pure duplication with drift risk — and with exactly one non-`source-control` consumer today, it wouldn't even register as a cluster for the new drift checker (which requires 2+ plugin copies). A prose convention pointer is lighter, carries no duplication to drift, and degrades gracefully if `source-control` is absent — which a hard-copied doc wouldn't buy any safety over. This is decision #42's design choice for its *first application*, not a retrofit of every authoring skill in the marketplace — no other skill is touched by this PR. Rolling the same pattern out to other PR-creating skills (if any surface a similar bypass) is deferred, unscoped follow-up work, tracked in the Decisions Log rather than guessed at here. ## Test plan - `bash scripts/validate-plugins.sh` — all plugin manifests + strict catalog validation pass. - `node scripts/validate-plugin-contracts.mjs` — 16 setup skills + 1342 plugin files checked, clean. - `npx markdownlint-cli2 plugins/code-tidying/skills/tidy/SKILL.md` — 0 errors. - Confirmed no other stale bare-`gh pr create` references remain under `plugins/code-tidying/`. - No existing test file exercises `tidy`'s `SKILL.md` prose directly (only `open-pr-count.test.sh`, unrelated to Phase H) — nothing to update there.
…is active (#1039) ## Summary `hardcoded-path-check.sh` scanned unconditionally when `CLAUDE_PROJECT_DIR` was unset — the scope guard's explicit fail-open branch — contradicting the README "Project scoping" contract ("only police files under `$CLAUDE_PROJECT_DIR`"). The gitignore escape hatch was gated on the same variable, so in exactly that case the one documented per-file exemption was unreachable short of the global kill switch. Real incident (handoff-inbox producer session): forced `~/` rewrites onto a machine-local `~/.gitconfig` edited from a no-project session. The hook now **skips entirely when no project is active**: a no-project target is machine-local, not the portable repo artifact this guard protects. Deliberately different from `secret-pattern-detection`, which scans even without a resolvable root (secrets are dangerous anywhere; hardcoded paths only harm portable artifacts). Dead downstream no-project branches (gitignore-gate condition, `PROJECT_ROOT` fallback) simplified away. README consumer-seam bullets updated so doc and code agree. History check: the fail-open dates to the plugin's initial commit (#42) with no recorded rationale beyond the inline comment; CHANGELOG 0.9.8 treated a similar prefilter fail-open as a bug. ## Tests - Red-first: two new no-project skip cases (Write + Edit, incident shape) failed on the unfixed hook (PASS=40 FAIL=4), green after (PASS=44 FAIL=0). - Detection/allow cases now set `CLAUDE_PROJECT_DIR` explicitly — scanning is a within-project behavior; exemption-path tests (`.claude/hooks`, `.claude/projects`, gitignore seam) set an enclosing project so each exemption stays exercised rather than masked by the new skip. - `shellcheck` clean; `check-changelog-parity --check-bump main` and `check-changed-skills` pass. ## Versioning guardrails 0.10.0 → **0.10.1** (patch — behavior fix, no new mechanism) + CHANGELOG entry. ## Fresh-docs citation https://code.claude.com/docs/en/hooks — `${CLAUDE_PROJECT_DIR}`: "the project root", exported as an env var on spawned hook processes; no guarantee of presence in no-project sessions. ## Related - Closes #1038 - Source: handoff-inbox item `20260722-035839-guardrails-hardcoded-path-check-failopen-scope` (F2 of that item recorded as working-as-intended decision note on #1038, no pattern change) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude Fable 5 <noreply@anthropic.com>



Bundles four PreToolUse safety guards into one concern-plugin with per-hook HOOK_*_ENABLED kill switches: secret-pattern-detection, hardcoded-path-check, cli-flag-verify, block-no-verify. Bundles path-detection + cli-flag-verify libs via ${CLAUDE_PLUGIN_ROOT}. block-no-verify rewritten to an argv-grammar-faithful gate (sees through quoting, escaping, wrappers, git global options, short-bundles, Windows case) with an honest README residual on shell-expansion bypass. 163 assertions; includes a machine-specific-paths CI exclude for the bundled pattern lib. Clean-tier wave Phase 2.
Note
High Risk
New PreToolUse blocks can stop legitimate writes/commits (secrets, paths, git bypass) and the git-bypass guard is bypassable via shell expansion; mis-tuned allowlists or missing jq change effective protection.
Overview
Adds a new
guardrailsmarketplace plugin that wires four independently toggleable hooks (HOOK_*_ENABLED) into PreToolUse / PostToolUse: blocking secret-pattern writes, hardcoded machine paths, and git hook-bypass Bash commands, plus advisory CLI-flag verification after edits.Blocking guards exit 2 with stderr guidance; they scope to
$CLAUDE_PROJECT_DIR, honor allowlists /git check-ignore, fail open without jq (with a visible notice), and emit opt-in hook-telemetry envelopes that carry category labels only—never secrets, full commands, or matched paths.block-no-verifyis rewritten as an argv-grammar-faithful parser (quoting, wrappers,git -C,core.hooksPath, lefthook env vars, 16KB fail-closed cap); README documents residual shell-expansion bypass.Bundled
lib/path-detectionandlib/verification/verify-cli-flag.shsupport path scanning and subcommand-aware--helpchecks; CI excludes the pattern lib from the machine-specific-paths lane so embedded regex bodies do not self-trip.Catalog, README, hook-telemetry schemas/examples, and extensive
*.test.shcontract suites document and lock behavior.Reviewed by Cursor Bugbot for commit b76d1bb. Bugbot is set up for automated code reviews on this repo. Configure here.