|
| 1 | +# Plan: Gatekeeper v2 — All-Tool Coverage + Auto-Mode Rules |
| 2 | + |
| 3 | +> Track: gatekeeper-v2-20260331 |
| 4 | +> Spec: [spec.md](./spec.md) |
| 5 | +
|
| 6 | +## Overview |
| 7 | + |
| 8 | +- **Source**: pleaseai/claude-code-plugins#135 |
| 9 | +- **Issue**: #135 |
| 10 | +- **Created**: 2026-03-31 |
| 11 | +- **Approach**: Modular Classifier — extend existing pattern-matching architecture with 3-tier decisions and per-tool classifiers |
| 12 | + |
| 13 | +## Purpose |
| 14 | + |
| 15 | +After this change, Claude Code users with Gatekeeper installed will have comprehensive security coverage across all tools (not just Bash). They can verify it works by observing that Write/Edit to `.env` triggers an AI review, while Read/Glob are instantly allowed, and `rm -rf /` is still hard-blocked. |
| 16 | + |
| 17 | +## Context |
| 18 | + |
| 19 | +Gatekeeper v1 only covers Bash commands, leaving Write, Edit, WebFetch, Agent and other tools unprotected. All denials are treated equally — there's no distinction between absolutely dangerous commands (rm -rf /) and commands that are dangerous but sometimes intentionally requested (git push --force). Analysis of Claude Code's built-in auto-mode classifier (`yoloClassifier.ts`) reveals a comprehensive 25+ rule set for DENY decisions and 7 ALLOW rules that Gatekeeper should match for full coverage. |
| 20 | + |
| 21 | +The issue comment provides a detailed gap analysis showing current coverage at ~40% for SOFT_DENY rules and ~57% for ALLOW rules. The goal is 100% coverage of auto-mode defaults through a combination of Layer 1 static rules and Layer 2 AI prompt improvements. |
| 22 | + |
| 23 | +Key constraints: Layer 1 must remain <5ms (no AI calls), existing Bash DENY/ALLOW behavior must not regress, and the `g` flag must not be used on RegExp patterns. |
| 24 | + |
| 25 | +## Architecture Decision |
| 26 | + |
| 27 | +**Chosen approach: Modular Classifier Pattern** |
| 28 | + |
| 29 | +The existing `pre-tool-use.ts` architecture (DENY_RULES → ALLOW_RULES → passthrough) extends naturally to a 3-tier system. Rather than a full rewrite, we rename `DENY_RULES` to `HARD_DENY_RULES`, add `SOFT_DENY_RULES`, and introduce per-tool classifier functions that dispatch from the main `evaluate()` function based on `tool_name`. The `chain-parser.ts` remains unchanged as it handles only shell command parsing. |
| 30 | + |
| 31 | +For soft_deny, returning `null` from the PreToolUse hook causes Claude Code to proceed to the PermissionRequest hook, where the AI agent evaluates user intent. This matches the issue's proposed flow exactly. |
| 32 | + |
| 33 | +The PermissionRequest prompt is rewritten with the full auto-mode rule set from the issue comment, structured as Core Principle (intent judgment) → ALLOW rules → hard DENY rules → soft DENY rules → tool-specific guidance. |
| 34 | + |
| 35 | +## Tasks |
| 36 | + |
| 37 | +- [ ] T001 Refactor DENY_RULES to HARD_DENY_RULES and add SOFT_DENY_RULES for Bash (file: plugins/gatekeeper/src/pre-tool-use.ts) |
| 38 | +- [ ] T002 Add Write/Edit classifier with path-based rules (file: plugins/gatekeeper/src/pre-tool-use.ts) (depends on T001) |
| 39 | +- [ ] T003 Add WebFetch classifier with URL-based rules (file: plugins/gatekeeper/src/pre-tool-use.ts) (depends on T001) |
| 40 | +- [ ] T004 Add safe tools instant-allow list and refactor evaluate() dispatcher (file: plugins/gatekeeper/src/pre-tool-use.ts) (depends on T001) |
| 41 | +- [ ] T005 Add tests for 3-tier Bash decisions (hard_deny, soft_deny, allow) (file: plugins/gatekeeper/src/pre-tool-use.test.ts) (depends on T001) |
| 42 | +- [ ] T006 [P] Add tests for Write/Edit classifier (file: plugins/gatekeeper/src/pre-tool-use.test.ts) (depends on T002) |
| 43 | +- [ ] T007 [P] Add tests for WebFetch classifier (file: plugins/gatekeeper/src/pre-tool-use.test.ts) (depends on T003) |
| 44 | +- [ ] T008 [P] Add tests for safe tools allowlist and unknown tool passthrough (file: plugins/gatekeeper/src/pre-tool-use.test.ts) (depends on T004) |
| 45 | +- [ ] T009 Rewrite PermissionRequest prompt with full auto-mode coverage and intent judgment (file: plugins/gatekeeper/hooks/hooks.json) (depends on T004) |
| 46 | +- [ ] T010 Update hook matchers from Bash to empty string and evaluate model change (file: plugins/gatekeeper/hooks/hooks.json) (depends on T009) |
| 47 | +- [ ] T011 Rewrite README.md with 3-tier architecture, all-tool coverage tables, and soft_deny documentation (file: plugins/gatekeeper/README.md) (depends on T010) |
| 48 | +- [ ] T012 Build dist bundle and verify all tests pass (depends on T005, T006, T007, T008, T010) |
| 49 | + |
| 50 | +## Key Files |
| 51 | + |
| 52 | +### Modify |
| 53 | + |
| 54 | +- `plugins/gatekeeper/src/pre-tool-use.ts` — Main hook logic: add 3-tier system, per-tool classifiers, safe tools allowlist |
| 55 | +- `plugins/gatekeeper/src/pre-tool-use.test.ts` — Tests: add soft_deny, Write/Edit, WebFetch, safe tools test suites |
| 56 | +- `plugins/gatekeeper/hooks/hooks.json` — Hook config: update matchers, rewrite PermissionRequest prompt, evaluate model |
| 57 | +- `plugins/gatekeeper/README.md` — Documentation: full rewrite with 3-tier architecture |
| 58 | + |
| 59 | +### Reuse (unchanged) |
| 60 | + |
| 61 | +- `plugins/gatekeeper/src/chain-parser.ts` — Shell parser: no changes needed |
| 62 | +- `plugins/gatekeeper/package.json` — Build config: no changes needed |
| 63 | +- `plugins/gatekeeper/CLAUDE.md` — Dev instructions: no changes needed |
| 64 | + |
| 65 | +## Verification |
| 66 | + |
| 67 | +### Automated Tests |
| 68 | + |
| 69 | +- [ ] All existing Bash DENY/ALLOW tests pass unchanged (regression check) |
| 70 | +- [ ] soft_deny Bash commands (git push --force, npm publish, kubectl apply) return null |
| 71 | +- [ ] hard_deny Bash commands (rm -rf /, mkfs) return deny decision |
| 72 | +- [ ] Write/Edit to .env returns null (soft_deny → AI review) |
| 73 | +- [ ] Write/Edit to project-relative path returns allow decision |
| 74 | +- [ ] WebFetch to paste services returns null (soft_deny → AI review) |
| 75 | +- [ ] Read, Glob, Grep return allow decision (safe tools) |
| 76 | +- [ ] Unknown tools return null (passthrough to AI) |
| 77 | + |
| 78 | +### Observable Outcomes |
| 79 | + |
| 80 | +- Running `echo '{"tool_name":"Bash","tool_input":{"command":"git push --force"}}' | node dist/pre-tool-use.js` produces no stdout (passthrough) |
| 81 | +- Running `echo '{"tool_name":"Read","tool_input":{}}' | node dist/pre-tool-use.js` produces allow JSON |
| 82 | +- Running `echo '{"tool_name":"Write","tool_input":{"file_path":".env"}}' | node dist/pre-tool-use.js` produces no stdout (passthrough to AI) |
| 83 | + |
| 84 | +### Acceptance Criteria Check |
| 85 | + |
| 86 | +- [ ] AC-1: hard_deny commands blocked immediately with stderr |
| 87 | +- [ ] AC-2: soft_deny commands pass through to PermissionRequest AI |
| 88 | +- [ ] AC-3: Write/Edit to .env/.claude/settings triggers soft_deny |
| 89 | +- [ ] AC-4: Write/Edit to project paths instantly allowed |
| 90 | +- [ ] AC-5: Safe tools instantly allowed |
| 91 | +- [ ] AC-6: Unknown tools pass through (fail-open) |
| 92 | +- [ ] AC-7: AI prompt judges user intent |
| 93 | +- [ ] AC-8: All existing tests pass |
| 94 | +- [ ] AC-9: New tests cover all classifications |
| 95 | +- [ ] AC-10: README reflects new architecture |
| 96 | + |
| 97 | +## Decision Log |
| 98 | + |
| 99 | +- Decision: Modular Classifier pattern — extend existing architecture rather than rewrite |
| 100 | + Rationale: Minimizes risk, preserves existing test coverage, clear migration path from 2-tier to 3-tier |
| 101 | + Date/Author: 2026-03-31 / Claude |
| 102 | + |
| 103 | +- Decision: soft_deny returns null (passthrough) rather than a new decision type |
| 104 | + Rationale: Claude Code hook protocol already supports null = passthrough to next hook. No SDK changes needed. |
| 105 | + Date/Author: 2026-03-31 / Claude |
0 commit comments