π―π΅ ζ₯ζ¬θͺη README γ―γγ‘γ
A reusable set of hooks, scripts, and guardrails for running Claude Code safely and autonomously β including under auto mode β in private repositories.
The following harness features are currently built in:
- Secret commit prevention
git commit --no-verifyprevention- Command execution via allowlist
- Bulk
git addprevention (.,-A,--all) - Destructive git guard (force push, push to
main/master,gh pr merge --admin) - Tool-call audit log (allowed calls + auto-mode classifier denials)
The following three skills are configured so that Claude Code can invoke them automatically:
/lock-settings: Register files that should be locked from modification/report-hook-block: Open an Issue when command restrictions are too strict/report-doc-conflict: Open an Issue when markdown files contradict each other
This harness is built on the following design principles:
- Development Automation: Claude Code handles tasks autonomously, including under auto mode
- Secret Protection: Secrets must never be read or pushed
- Never-events, not perfection: High friction on catastrophic actions (force push, push to
main, admin merge, secret reads); low friction elsewhere - Defense in depth: Every never-event is covered by at least one mechanism independent of the auto-mode classifier
- Thorough Reviews: PR reviews are performed carefully by humans
- Private Repository: Designed for use in private repositories (PRs serve as the last line of defense; the design does not guarantee 100% prevention of secret commits and pushes)
See ADR-001 for the full rationale behind the layered defense model.
This harness lets Claude Code use a wide range of shell commands β but strictly on an allowlist basis, paired with OS-level sandboxing and hook-based checks. Nothing runs unless you explicitly permit it, and every permitted command must have its exact usage defined.
The harness provides multiple layers of protection so Claude Code can operate under auto mode with minimal manual intervention:
- Enforced command allowlist β The core design. When you authorize a command in
settings.json, the test suite (settings_allowlist_sync.bats) will fail unless you also define exactly how that command may be used in the allowlist (command-allowlist.sh). Permitted usage is filtered with regular expressions. Compound commands (cmd1 && cmd2 | cmd3) are split and each segment validated independently so you cannot chain an approved command with an unapproved one. - Destructive git guard β Blocks force push (
--force,-f,--force-with-lease,+refspec), direct push tomain/master(explicit or via current branch),gh pr merge --admin, and bulkgit add ./-A/--all. Fails closed on shell wrappers (bash -c,eval) carrying destructive tokens. - Secret leak prevention β Git pre-commit hook scans staged diffs for API keys, tokens, and credentials. A Claude Code PreToolUse hook blocks commits with sensitive filenames staged. Permissions
denyand sandboxdenyReadblock reads of common secret locations (.env,*.pem,*.key,~/.aws,~/.kube,~/.netrc, etc.) before Claude Code can even stage them for leak. - Hook bypass prevention β Blocks
--no-verifyand similar flags that would skip safety checks. - File protection β Security-critical files (hooks, settings,
scripts/install-hooks.sh,.github/workflows/**,tests/hooks/**) are locked via permissiondenyrules and OS-level sandboxdenyWriteglobs. - Audit log β A
PostToolUsehook appends every allowed tool call (command / file path / URL) todocs/logs/audit/YYYY-MM-DD.jsonl(gitignored); aPermissionDeniedhook records auto-mode classifier denials. File contents and command output are not captured, to bound secret leakage into the log. - PR-based review workflow β All changes go through pull requests with automated Claude Code review.
git clone https://github.com/<your-org>/cc_harness.git my-project
cd my-projectCLAUDE.md is the file Claude Code reads to understand your project. Open it and fill in the placeholder sections β project overview, tech stack, and any project-specific conventions. The harness-related sections (hooks, git workflow, error messages) are already set up and can be kept as-is or adjusted to fit your needs.
# macOS
brew install jq bats-core
# Debian / Ubuntu
sudo apt-get update && sudo apt-get install -y jq bats./scripts/install-hooks.shbats tests/hooks/Create a feature branch and start working. The harness hooks and CI workflows will protect you from there.
.claude/
hooks/ Claude Code hook scripts
lib/
shell-parse.sh Shared parser: split compound commands on unquoted operators
block-no-verify.sh PreToolUse: block --no-verify flag on git commit
block-add-all.sh PreToolUse: block `git add .`, `-A`, `--all`
block-dangerous-git.sh PreToolUse: block force push, push to main/master, `gh pr merge --admin`
command-allowlist.sh PreToolUse: regex-based command allowlist with compound splitting
prevent-secret-commit.sh PreToolUse: block commits with sensitive filenames staged
log-tool-call.sh PostToolUse: append allowed tool calls to the audit log
log-permission-denied.sh PermissionDenied: append classifier denials to the audit log
skills/ Claude Code skill definitions
lock-settings/ Lock security-critical files from modification
report-doc-conflict/ Report contradictory documentation as GitHub Issues
report-hook-block/ Report overly restrictive hook blocks as GitHub Issues
settings.json Permissions, sandbox rules, and hook registration
.githooks/
pre-commit Scans staged diffs for secret patterns
.github/workflows/
claude.yml Claude Code agent (responds to @claude mentions)
claude-code-review.yml Automated PR review via Claude Code
hook-tests.yml CI for hook test suite
scripts/
install-hooks.sh Configures git core.hooksPath
tests/hooks/ Bats-core test suite for all hooks
docs/adr/ Architecture Decision Records
001-auto-mode-harness-defense-in-depth.md β layered defense for auto mode
docs/logs/audit/ Gitignored JSONL log of tool calls (written by PostToolUse hook)
CLAUDE.md Project instructions for Claude Code (rewrite for your project)
This is the central mechanism of the harness. The workflow is:
- Authorize a command β Add
Bash(<command>:*)topermissions.allowinsettings.json. This tells Claude Code it may use the command. - Define allowed usage β Add the command prefix to
GOVERNED_PREFIXESand regex patterns for its permitted forms toALLOWED_PATTERNSincommand-allowlist.sh. This controls how the command can be used. - Tests enforce the link β
settings_allowlist_sync.batsverifies that every authorized command has a governed prefix and at least one allowed pattern. If you add a permission without defining its usage, tests fail.
For example, to allow gh pr create but not gh pr merge --admin:
# settings.json β authorize the "gh pr" command family
"Bash(gh pr:*)"
# command-allowlist.sh β define exactly which forms are allowed
'^gh pr (create|checkout|comment|edit|ready)( |$)'
Compound commands (cmd1 && cmd2 | cmd3) are automatically split, and each segment is validated independently. This prevents bypassing the allowlist by chaining an approved command with an unapproved one.
Four layers, from softest to hardest (per ADR-001):
| Layer | Mechanism | Role |
|---|---|---|
| 1 | autoMode.environment (prose in settings.local.json) |
Tells the auto-mode classifier what's trusted. Not enforceable β prompt injection can reframe intent. |
| 2 | permissions.allow / ask / deny |
Hard rules on tool calls. deny is evaluated before the classifier and cannot be bypassed by conversation. |
| 3 | sandbox.filesystem (denyRead / denyWrite / network allowlist) |
OS-level enforcement via Seatbelt / bubblewrap. Applies to every Bash subprocess, not only Claude's built-in tools. |
| 4 | PreToolUse / PostToolUse / PermissionDenied hooks | Regex-based checks and observability. Can parse argument content that permission-rule globs cannot. |
Every "never-event" (force push, push to main, gh pr merge --admin, reads of secret paths, edits to locked files) is covered by at least one mechanism independent of the auto-mode classifier.
Per-concern mapping for this repo:
| What it guards | Mechanism |
|---|---|
| Every Bash command Claude Code runs | PreToolUse: command-allowlist.sh, block-no-verify.sh, block-add-all.sh, block-dangerous-git.sh, prevent-secret-commit.sh |
Every git commit (by any user or agent) |
Git pre-commit: diff content scan for secret patterns |
| Security-critical files | permissions.deny + sandbox denyWrite globs |
| Secret file reads | permissions.deny + sandbox denyRead globs |
| Observability | PostToolUse + PermissionDenied β docs/logs/audit/YYYY-MM-DD.jsonl |
- hook-tests.yml β Runs
bats tests/hooks/on PRs that touch hooks, settings, or tests. - claude-code-review.yml β Automated code review on every PR using Claude Code.
- claude.yml β Claude Code agent that responds to
@claudementions in issues and PRs.