feat(gatekeeper): v2 — all-tool coverage + auto-mode rules#136
feat(gatekeeper): v2 — all-tool coverage + auto-mode rules#136
Conversation
- Rename DENY_RULES to HARD_DENY_RULES, add SOFT_DENY_RULES for Bash - Add classifyWriteEdit() with path-based soft_deny for .env, .claude/settings, CI configs - Add classifyWebFetch() with URL-based soft_deny for paste services, script downloads - Add SAFE_TOOLS instant-allow list (Read, Glob, Grep, etc.) - Refactor evaluate() to dispatch by tool_name to per-tool classifiers - soft_deny returns null (passthrough to PermissionRequest AI hook) - Add comprehensive tests: 246 tests, 835 assertions, 0 failures Closes: #135
- Change both matchers from "Bash" to "" (all tools) - Add intent judgment as core principle - Add full auto-mode ALLOW/DENY rule coverage (7 ALLOW, 25+ DENY) - Add tool-specific guidance for Write/Edit, WebFetch, Agent - Switch model from sonnet to haiku for faster classification
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
There was a problem hiding this comment.
Code Review
This pull request upgrades the Gatekeeper plugin to version 2, expanding its security coverage from Bash-only to all Claude Code tools, including Write, Edit, and WebFetch. It introduces a three-tier decision system: hard_deny for immediate blocks, soft_deny for passthrough to AI review, and allow for instant approval of safe operations. The review feedback highlights several security-critical improvements for the pattern-matching logic, specifically addressing potential bypasses in Git branch detection, path traversal vulnerabilities in file operations, and regex weaknesses in URL and command flag matching.
Split combined pattern into two rules because \b word boundary doesn't match before `.` (non-word character), making the .claude/settings branch unreachable.
Resolve eslint regexp plugin errors: - Replace .* patterns with \S+ to eliminate super-linear backtracking (regexp/no-super-linear-backtracking on \s+ combined with .*) - Remove duplicate A-Z range from case-insensitive character class (regexp/no-dupe-characters-character-class on [a-zA-Z] with /i flag) - Convert capturing groups to non-capturing in localhost URL pattern (regexp/no-unused-capturing-group)
There was a problem hiding this comment.
1 issue found across 9 files
Confidence score: 2/5
- High-risk security issue:
plugins/gatekeeper/src/pre-tool-use.tshas a localhost allow-rule that can be bypassed by attacker-controlled hostnames likehttp://localhost.evil.com, which could weaken intended access restrictions. - Because this is a concrete, user-impacting security flaw with high severity (8/10) and high confidence (9/10), merge risk is elevated until the hostname boundary check is fixed.
- Suggested fix in the issue (
([/?#]|$)after hostname/port matching) is important to ensure only true local URLs are allowed. - Pay close attention to
plugins/gatekeeper/src/pre-tool-use.ts- localhost matching needs a strict boundary to prevent allow-list bypass.
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="plugins/gatekeeper/src/pre-tool-use.ts">
<violation number="1" location="plugins/gatekeeper/src/pre-tool-use.ts:183">
P1: Security bypass: the localhost allow-rule matches attacker-controlled hostnames like `http://localhost.evil.com` because it lacks a boundary after the hostname/port. Add `([/?#]|$)` to ensure only actual local URLs are allowed.</violation>
</file>
Architecture diagram
sequenceDiagram
participant User as "User / Claude Code"
participant L1 as "NEW: PreToolUse (Layer 1)"
participant L2 as "CHANGED: PermissionRequest (Layer 2)"
participant LLM as "NEW: Anthropic Haiku (L2 Model)"
participant Sys as "System / Filesystem"
Note over L1,LLM: Gatekeeper v2 Runtime Flow
User->>L1: Request Tool Execution (Bash, Write, WebFetch, etc.)
rect rgb(240, 240, 240)
Note right of L1: NEW: 3-tier static classification (<5ms)
L1->>L1: Match against HARD_DENY_RULES
L1->>L1: Match against SOFT_DENY_RULES
L1->>L1: Match against ALLOW_RULES / SAFE_TOOLS
end
alt Match: HARD_DENY (e.g. rm -rf /)
L1-->>User: { permissionDecision: "deny" }
Note over User,Sys: Tool execution blocked immediately
else Match: ALLOW (e.g. git status, npm test, Read tool)
L1-->>User: { permissionDecision: "allow" }
User->>Sys: Execute Tool (Permission dialog skipped)
else Match: SOFT_DENY or UNKNOWN (e.g. git push --force, write .env)
L1-->>User: null (passthrough)
Note over User,L2: CHANGED: Escalating to AI Review
User->>L2: PermissionRequest(arguments, intent_prompt)
L2->>LLM: NEW: Evaluate intent using Haiku model
Note right of LLM: 25+ auto-mode rules + "Did user explicitly request this?"
LLM-->>L2: {"decision": {"behavior": "allow" | "deny"}}
alt AI Decision: ALLOW
L2-->>User: { ok: true }
User->>Sys: Execute Tool
else AI Decision: DENY
L2-->>User: { ok: false, reason: "..." }
Note over User,Sys: Tool execution blocked with AI reasoning
else AI Decision: UNSURE
L2-->>User: null (passthrough)
User->>User: Show Standard Permission Dialog to User
end
end
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
There was a problem hiding this comment.
1 issue found across 2 files (changes from recent commits).
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="plugins/gatekeeper/src/pre-tool-use.ts">
<violation number="1" location="plugins/gatekeeper/src/pre-tool-use.ts:84">
P2: The new gcloud IAM soft-deny regex only allows one subcommand token before `add-iam`, so multi-segment commands like `gcloud alpha projects add-iam-policy-binding` won’t match and will bypass soft-deny.</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
- Fix gcloud IAM regex to match multi-segment commands (e.g., gcloud alpha projects add-iam-policy-binding) - Fix localhost allow-rule to add boundary after hostname/port to prevent bypasses like localhost.evil.com - Fix path traversal in classifyWriteEdit by using path.resolve before checking project membership - Fix URL domain patterns for paste/file services to anchor to authority section - Fix raw.githubusercontent.com regex to handle query parameters (not just $ anchor) - Broaden chmod 777 detection to match flags before the mode (e.g., chmod -R 777)
There was a problem hiding this comment.
1 issue found across 2 files (changes from recent commits).
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="plugins/gatekeeper/src/pre-tool-use.ts">
<violation number="1" location="plugins/gatekeeper/src/pre-tool-use.ts:156">
P1: The POSIX-only `startsWith('/')` check makes all absolute Windows paths auto-allow. This lets writes outside the project root bypass AI review on Windows. Use a project-root prefix check without the Unix root test.</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
Remove POSIX-only startsWith('/') guard so absolute Windows paths
(e.g. C:\Users\...) are no longer auto-allowed outside the project root.
Now relies solely on resolvedPath.startsWith(process.cwd()) and uses
path.sep for node_modules detection, making the check portable.
There was a problem hiding this comment.
1 issue found across 2 files (changes from recent commits).
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="plugins/gatekeeper/src/pre-tool-use.ts">
<violation number="1" location="plugins/gatekeeper/src/pre-tool-use.ts:156">
P1: The project-root allow check is too broad and can allow writes outside the repo. Enforce a root boundary instead of a raw prefix (and avoid globally allowing any `node_modules` path).</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
Fix path traversal vulnerability in classifyWriteEdit: use strict boundary check (resolvedPath === cwd || startsWith(cwd + sep)) instead of broad prefix match, and remove the overly permissive node_modules allow rule.
Summary
Test plan
Closes #135
Summary by cubic
Gatekeeper v2 adds a 3-tier decision system (hard_deny, soft_deny, allow) across all Claude Code tools and aligns with auto‑mode rules from #135. It also hardens security with stricter path and URL checks and faster intent review.
New Features
PreToolUsenow classifies all tools: Bash (e.g., force push, push to main, reset --hard, npm publish, terraform/kubectl apply → soft_deny), Write/Edit (path rules for.env,.claude/settings, CI; project files allowed with strict project‑root checks), WebFetch (paste/script downloads → soft_deny).PermissionRequest.PermissionRequestprompt rebuilt with auto‑mode rules and intent check; matchers widened to all tools; model switched to haiku..claude/settingsmatcher; anchored paste/file‑service domains; stricter localhost boundary; broaderchmod 777detection; improvedgcloud ... add‑iamregex; handledraw.githubusercontent.comquery params; prevented path traversal withpath.resolveand strict project‑root boundary (removednode_modulesauto‑allow); regex rules optimized to avoid backtracking/lint errors. All 246 tests pass; README documents the 3‑tier design.Migration
Written for commit efaa2df. Summary will update on new commits.
Verification Checklist
Automated Tests
Observable Outcomes
echo '{"tool_name":"Bash","tool_input":{"command":"git push --force"}}' | node dist/pre-tool-use.jsproduces no stdout (passthrough)echo '{"tool_name":"Read","tool_input":{}}' | node dist/pre-tool-use.jsproduces allow JSONecho '{"tool_name":"Write","tool_input":{"file_path":".env"}}' | node dist/pre-tool-use.jsproduces no stdout (passthrough to AI)Acceptance Criteria