Skip to main content
OrchestKit v6.7.1 — 67 skills, 38 agents, 77 hooks with Opus 4.6 support
OrchestKit

Security Gates and File Guards

How OrchestKit blocks dangerous commands, protects sensitive files, and auto-approves safe operations -- the defense-in-depth hooks that run before every tool use.

Defense in Depth

OrchestKit implements security at multiple layers. No single hook is responsible for all safety -- instead, hooks compose to form a defense-in-depth chain:

PermissionRequest  -->  PreToolUse  -->  Tool Executes  -->  PostToolUse
      |                     |                                     |
  auto-approve          file-guard                          secret-redactor
  safe commands     dangerous-command-blocker               audit-logger
  project writes    security-pattern-validator               error-handler
                    architecture-change-detector
                    multi-instance-lock

If any hook in the chain returns { continue: false }, the operation is blocked and Claude receives the stopReason explaining why.

Dangerous Command Blocker

Hook: pretool/bash/dangerous-command-blocker Event: PreToolUse (Bash) Behavior: Blocks commands that match known destructive patterns.

What It Blocks

The blocker checks against three categories of dangerous patterns:

These are matched as literal substrings in the normalized (lowercased, whitespace-collapsed) command:

PatternCategory
rm -rf /Filesystem destruction
rm -rf ~Filesystem destruction
rm -fr /Filesystem destruction (alternate flag order)
rm -fr ~Filesystem destruction
mv /* /dev/nullFilesystem destruction
> /dev/sdaDevice wiping
mkfs.Device wiping (filesystem format)
dd if=/dev/zero of=/dev/Device wiping
dd if=/dev/random of=/dev/Device wiping
chmod -R 777 /Permission abuse
:(){:|:&};:Fork bomb
git reset --hardDestructive git (data loss)
git clean -fdDestructive git (data loss)
drop databaseDatabase destruction
drop schemaDatabase destruction
truncate tableDatabase destruction

These are matched using regular expressions for patterns that require flexible matching:

PatternWhat It Catches
/|\s*(sh|bash|zsh|dash)\b/iPiping to shell: curl url | bash, wget url | sh
/git\s+push\s+.*(-f|--force)\b/iForce push: git push --force, git push -f origin main

How Normalization Prevents Bypass

Before pattern matching, commands are normalized to prevent evasion techniques:

function normalizeCommand(command: string): string {
  return command
    .replace(/\\\s*[\r\n]+/g, ' ')  // Remove line continuations (rm -rf \<newline>/)
    .replace(/\n/g, ' ')             // Replace newlines with spaces
    .replace(/\s+/g, ' ')            // Collapse whitespace
    .trim();
}

This means rm \<newline>-rf / is caught just as reliably as rm -rf /.

When a Command Is Blocked

The user sees a clear denial message:

{
  "continue": false,
  "stopReason": "Command matches dangerous pattern: rm -rf /\n\nThis command could cause severe system damage and has been blocked.",
  "hookSpecificOutput": {
    "hookEventName": "PreToolUse",
    "permissionDecision": "deny",
    "permissionDecisionReason": "Command matches dangerous pattern: rm -rf /"
  }
}

Every denial is also logged to permission-feedback.log for audit:

2026-01-23T10:15:30.123Z | deny | Dangerous pattern: rm -rf / | tool=Bash | session=abc123

File Guard

Hook: pretool/write-edit/file-guard Event: PreToolUse (Write|Edit) Behavior: Blocks writes to sensitive files, warns on config files, enforces file size limits on code files.

Protected Files (Always Blocked)

PatternFiles Matched
/\.env$/.env
/\.env\.local$/.env.local
/\.env\.production$/.env.production
/credentials\.json$/credentials.json
/secrets\.json$/secrets.json
/private\.key$/private.key
/\.pem$/Any .pem file
/id_rsa$/SSH private key
/id_ed25519$/SSH private key (Ed25519)

Config Files (Warning Only)

These files trigger a warning log but are allowed through:

PatternFiles Matched
/package\.json$/package.json
/pyproject\.toml$/pyproject.toml
/tsconfig\.json$/tsconfig.json

The file guard resolves symlinks before checking patterns to prevent bypass attacks:

function resolveRealPath(filePath: string, projectDir: string): string {
  const absolutePath = isAbsolute(filePath)
    ? filePath
    : resolve(projectDir, filePath);

  // Follow symlinks if file exists
  if (existsSync(absolutePath)) {
    return realpathSync(absolutePath);  // Resolves symlinks
  }

  return absolutePath;
}

Without this, an attacker could create harmless.txt -> .env and write to the symlink to bypass the guard. The realpathSync call resolves the real target before pattern matching.

Denial Message

When a protected file is blocked:

Cannot modify protected file: .env

Resolved path: /Users/dev/project/.env
Matched pattern: /\.env$/

Protected files include:
- Environment files (.env, .env.local, .env.production)
- Credential files (credentials.json, secrets.json)
- Private keys (.pem, id_rsa, id_ed25519)

If you need to modify this file, do it manually outside Claude Code.

File Size Gate (Write Only)

The file guard enforces maximum line counts on code files to prevent bloated single-file modules. This runs on Write operations only (Edit doesn't carry full content).

Defaults:

File TypeLimitEnv Var Override
Source files (.py, .ts, .tsx, .js, .jsx, .go, .rs, .java)300 linesORCHESTKIT_MAX_FILE_LINES
Test files (.test.*, .spec.*, test_*, *_test.*, __tests__/)500 linesORCHESTKIT_MAX_TEST_FILE_LINES

Non-code files (.json, .md, .yaml, .css, .html, etc.) are never gated.

Bloat pattern detection -- when a file exceeds the line limit, the denial message includes structural signals if detected:

SignalTriggerGuidance
god-file>15 exportsSplit by domain
mixed-concernsTypes + logic in file >150 linesExtract types to types.ts
high-coupling>20 importsFile does too much, split by responsibility
multi-class>1 classOne class per file
multi-component>3 componentsOne component per file

Override per-project by setting env vars:

export ORCHESTKIT_MAX_FILE_LINES=500
export ORCHESTKIT_MAX_TEST_FILE_LINES=800

Permission Auto-Approve Hooks

OrchestKit includes three permission hooks that reduce friction for safe operations.

Auto-Approve Safe Bash

Hook: permission/auto-approve-safe-bash Event: PermissionRequest (Bash)

Auto-approves bash commands that are known to be read-only or safe:

CategoryApproved Patterns
Git read opsgit status, git log, git diff, git branch, git show, git fetch, git pull, git checkout
Package managersnpm list, npm test, npm run, pnpm audit, yarn outdated, poetry show
Docker read opsdocker ps, docker images, docker logs, docker-compose ps
Shell basicsls, pwd, echo, cat, head, tail, wc, find, which, env
GitHub CLIgh issue list, gh pr view, gh repo status, gh milestone
Testing/lintingpytest, npm run test, npm run lint, ruff check, mypy

Commands not matching any safe pattern fall through to manual user approval -- they are not denied, just not auto-approved.

Auto-Approve Project Writes

Hook: permission/auto-approve-project-writes Event: PermissionRequest (Write|Edit)

Auto-approves file writes that are within the project directory, with exclusions:

// Path containment check (prevents prefix attacks)
const relativePath = relative(normalizedProject, normalizedFile);
const isInsideProject = !relativePath.startsWith('..') && !isAbsolute(relativePath);

Excluded directories (require manual approval even within project):

  • node_modules/
  • .git/
  • dist/
  • build/
  • __pycache__/
  • .venv/ and venv/

The path containment check uses path.relative() instead of string prefix matching. This prevents the attack where /project-malicious/evil.js would match a project at /project via naive startsWith comparison.

Auto-Approve Read Operations

Read, Glob, and Grep operations have an empty hooks array in the PermissionRequest configuration, meaning Claude Code auto-approves them with zero overhead.

Learning Tracker

Hook: permission/learning-tracker Event: PermissionRequest (Bash)

Tracks which commands the user manually approves over time. This data feeds into pattern learning -- if you consistently approve docker build commands, the system notes this for potential future auto-approval.

Additional PreToolUse Safety Hooks

Beyond the command blocker and file guard, several other hooks provide safety at the PreToolUse stage:

Compound Command Validator

Hook: pretool/bash/compound-command-validator

Validates compound bash commands (piped, chained with && or ;) to ensure no individual segment contains dangerous patterns that might be hidden within a longer command.

Security Pattern Validator

Hook: pretool/Write/security-pattern-validator

Checks code being written for common security anti-patterns:

  • Hardcoded secrets or API keys
  • SQL injection vulnerabilities
  • Unsafe deserialization patterns

Architecture Change Detector

Hook: pretool/Write/architecture-change-detector

Detects when a write modifies a file that represents a significant architectural component. Injects context reminding Claude to consider backwards compatibility and documentation updates.

Multi-Instance Lock

Hook: pretool/write-edit/multi-instance-lock

When multiple Claude instances are running on the same project, this hook acquires a file lock before allowing writes. Uses atomic file operations (write to temp, then renameSync) to prevent race conditions.

Agent-Scoped Safety (Task hooks)

When spawning subagents via the Task tool, 5 additional hooks validate the operation:

HookPurpose
agent/block-writesPrevents certain agents from writing outside their scope
agent/migration-safety-checkValidates database migrations before execution
agent/security-command-auditAudits commands issued by security-sensitive agents
agent/ci-safety-checkValidates CI/CD pipeline modifications
agent/deployment-safety-checkGuards against risky deployment operations

PostToolUse Safety

Secret Redactor

Hook: skill/redact-secrets Event: PostToolUse (Bash)

After a bash command completes, this hook scans the output for patterns that look like secrets (API keys, tokens, passwords) and redacts them before they appear in the conversation context.

Unified Error Handler

Hook: posttool/unified-error-handler Event: PostToolUse (Bash|Write|Edit|Task|Skill|NotebookEdit)

Categorizes tool failures and provides actionable recovery suggestions. Tracks error patterns across the session to detect systemic issues.

Customizing Safety Hooks

Hook Overrides

You can disable specific hooks per-project by creating .claude/hook-overrides.json:

{
  "disabled": [
    "pretool/Write/docstring-enforcer"
  ],
  "timeouts": {
    "pretool/bash/license-compliance": 5
  }
}

Never disable security-critical hooks like dangerous-command-blocker or file-guard. These exist to prevent catastrophic damage and should remain active at all times.

Permission Mode: dontAsk

Claude Code 2.1.25 introduced permissionMode: 'dontAsk' for automated workflows. When this mode is active, quality gates emit warnings instead of blocking:

import { isDontAskMode } from './lib/guards.js';

export function myQualityGate(input: HookInput): HookResult {
  if (isDontAskMode(input)) {
    // Warn instead of block in automated mode
    return outputWarning('Code quality issue detected');
  }
  return outputBlock('Code quality issue -- fix before proceeding');
}

Security-critical hooks (dangerous command blocker, file guard) always block regardless of permission mode.

Audit Trail

All permission decisions are logged to ~/.claude/logs/ork/permission-feedback.log:

2026-01-23T10:15:30.123Z | allow | Matches safe pattern: /^git status/ | tool=Bash | session=abc123
2026-01-23T10:15:31.456Z | deny  | Protected file blocked: .env | tool=Write | session=abc123
2026-01-23T10:15:32.789Z | warn  | Config file modification: package.json | tool=Edit | session=abc123

The log rotates automatically at 100 KB. General hook activity is logged to hooks.log with rotation at 200 KB.

Edit on GitHub

Last updated on