Skip to content

feat(gatekeeper): v2 — all-tool coverage + auto-mode rules#136

Merged
amondnet merged 13 commits intomainfrom
amondnet/scientific-stop
Mar 31, 2026
Merged

feat(gatekeeper): v2 — all-tool coverage + auto-mode rules#136
amondnet merged 13 commits intomainfrom
amondnet/scientific-stop

Conversation

@amondnet
Copy link
Copy Markdown
Contributor

@amondnet amondnet commented Mar 31, 2026

Summary

  • Implement 3-tier decision system (hard_deny / soft_deny / allow) in PreToolUse
  • Extend from Bash-only to all Claude Code tools (Write, Edit, WebFetch, safe tools)
  • Rewrite PermissionRequest AI prompt with full auto-mode coverage (7 ALLOW, 25+ DENY rules)
  • Switch Layer 2 model from sonnet to haiku for faster classification
  • Rewrite README.md with new architecture documentation

Test plan

  • All 246 tests pass (835 assertions), 0 failures
  • Existing Bash DENY/ALLOW tests pass unchanged (regression check)
  • New soft_deny tests for git push --force, npm publish, kubectl apply
  • New Write/Edit tests for .env, .claude/settings, project paths
  • New WebFetch tests for paste services, localhost
  • New safe tools allowlist tests (Read, Glob, Grep, etc.)
  • dist bundle rebuilt (15.47 KB)

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

    • PreToolUse now 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).
    • Safe tools (Read, Glob, Grep, etc.) are allowed; unknown tools fall through to PermissionRequest.
    • PermissionRequest prompt rebuilt with auto‑mode rules and intent check; matchers widened to all tools; model switched to haiku.
    • Security hardening: fixed .claude/settings matcher; anchored paste/file‑service domains; stricter localhost boundary; broader chmod 777 detection; improved gcloud ... add‑iam regex; handled raw.githubusercontent.com query params; prevented path traversal with path.resolve and strict project‑root boundary (removed node_modules auto‑allow); regex rules optimized to avoid backtracking/lint errors. All 246 tests pass; README documents the 3‑tier design.
  • Migration

    • Gatekeeper now evaluates all tools by default; no config changes required.
    • Actions like pushing to main or publishing will prompt for review due to new soft_deny rules.

Written for commit efaa2df. Summary will update on new commits.

Verification Checklist

Automated Tests

  • All existing Bash DENY/ALLOW tests pass unchanged (regression check)
  • soft_deny Bash commands (git push --force, npm publish, kubectl apply) return null
  • hard_deny Bash commands (rm -rf /, mkfs) return deny decision
  • Write/Edit to .env returns null (soft_deny → AI review)
  • Write/Edit to project-relative path returns allow decision
  • WebFetch to paste services returns null (soft_deny → AI review)
  • Read, Glob, Grep return allow decision (safe tools)
  • Unknown tools return null (passthrough to AI)

Observable Outcomes

  • echo '{"tool_name":"Bash","tool_input":{"command":"git push --force"}}' | node dist/pre-tool-use.js produces no stdout (passthrough)
  • echo '{"tool_name":"Read","tool_input":{}}' | node dist/pre-tool-use.js produces allow JSON
  • echo '{"tool_name":"Write","tool_input":{"file_path":".env"}}' | node dist/pre-tool-use.js produces no stdout (passthrough to AI)

Acceptance Criteria

  • AC-1: hard_deny commands blocked immediately with stderr
  • AC-2: soft_deny commands pass through to PermissionRequest AI
  • AC-3: Write/Edit to .env/.claude/settings triggers soft_deny
  • AC-4: Write/Edit to project paths instantly allowed
  • AC-5: Safe tools instantly allowed
  • AC-6: Unknown tools pass through (fail-open)
  • AC-7: AI prompt judges user intent
  • AC-8: All existing tests pass
  • AC-9: New tests cover all classifications
  • AC-10: README reflects new architecture

- 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
@vercel
Copy link
Copy Markdown

vercel Bot commented Mar 31, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
claude-code-plugins Ready Ready Preview, Comment Mar 31, 2026 3:39pm

Request Review

Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment thread plugins/gatekeeper/src/pre-tool-use.ts Outdated
Comment thread plugins/gatekeeper/src/pre-tool-use.ts Outdated
Comment thread plugins/gatekeeper/src/pre-tool-use.ts Outdated
Comment thread plugins/gatekeeper/src/pre-tool-use.ts Outdated
Comment thread plugins/gatekeeper/src/pre-tool-use.ts Outdated
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)
Copy link
Copy Markdown

@cubic-dev-ai cubic-dev-ai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1 issue found across 9 files

Confidence score: 2/5

  • High-risk security issue: plugins/gatekeeper/src/pre-tool-use.ts has a localhost allow-rule that can be bypassed by attacker-controlled hostnames like http://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
Loading

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

Comment thread plugins/gatekeeper/src/pre-tool-use.ts Outdated
Copy link
Copy Markdown

@cubic-dev-ai cubic-dev-ai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment thread plugins/gatekeeper/src/pre-tool-use.ts Outdated
@amondnet amondnet self-assigned this Mar 31, 2026
- 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)
Copy link
Copy Markdown

@cubic-dev-ai cubic-dev-ai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment thread plugins/gatekeeper/src/pre-tool-use.ts Outdated
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.
Copy link
Copy Markdown

@cubic-dev-ai cubic-dev-ai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment thread plugins/gatekeeper/src/pre-tool-use.ts Outdated
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.
@amondnet amondnet merged commit fe1cb78 into main Mar 31, 2026
7 checks passed
@amondnet amondnet deleted the amondnet/scientific-stop branch March 31, 2026 15:42
@pleaeai-bot pleaeai-bot Bot mentioned this pull request Mar 31, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Gatekeeper v2: Extend to all tools + improve AI prompt with auto-mode rules

1 participant