{"id":51551329,"url":"https://github.com/inoX-Network/claude-code-safety-guard","last_synced_at":"2026-07-12T14:01:17.582Z","repository":{"id":348113846,"uuid":"1196519936","full_name":"inoX-Network/claude-code-safety-guard","owner":"inoX-Network","description":"3-level override system for Claude Code - prevents destructive system operations. Born from a real incident.","archived":false,"fork":false,"pushed_at":"2026-06-29T20:58:23.000Z","size":193,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-06-29T22:25:08.871Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/inoX-Network.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":"THREAT-MODEL.md","audit":null,"citation":null,"codeowners":null,"security":"security-rules.example.json","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-03-30T19:25:34.000Z","updated_at":"2026-06-29T20:58:27.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/inoX-Network/claude-code-safety-guard","commit_stats":null,"previous_names":["inox-network/claude-code-safety-guard"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/inoX-Network/claude-code-safety-guard","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/inoX-Network%2Fclaude-code-safety-guard","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/inoX-Network%2Fclaude-code-safety-guard/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/inoX-Network%2Fclaude-code-safety-guard/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/inoX-Network%2Fclaude-code-safety-guard/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/inoX-Network","download_url":"https://codeload.github.com/inoX-Network/claude-code-safety-guard/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/inoX-Network%2Fclaude-code-safety-guard/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":35393398,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-07-12T02:00:06.386Z","response_time":87,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2026-07-10T00:00:52.211Z","updated_at":"2026-07-12T14:01:17.575Z","avatar_url":"https://github.com/inoX-Network.png","language":"Python","funding_links":[],"categories":["Security"],"sub_categories":[],"readme":"# Safety Guard — for Claude Code \u0026amp; opencode\n\n[![Born from a real incident](https://img.shields.io/badge/born%20from-a%20real%20incident-red)](https://github.com/anthropics/claude-code/issues/39283)\n[![Works with](https://img.shields.io/badge/works%20with-Claude%20Code%20%2B%20opencode-success)](opencode/README.md)\n[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)\n\nA **deterministic tool-call guard** for AI coding agents. It sees every tool call *before* it runs and blocks the catastrophic ones — `rm -rf /`, reading `~/.ssh`, writing `/etc`, force-push to `main`, credential exfiltration — with a **3-level, agent-scoped override** for when you genuinely need elevated rights, and a self-protection layer so the AI can never disarm its own guard. Same input, same verdict, every time. No LLM in the loop: it's the net for when the model's judgment (or the permission prompt) fails or gets subverted by prompt injection.\n\n- **Claude Code:** runs as a `PreToolUse` hook.\n- **opencode:** runs as a plugin that bridges to the same guard — see [opencode/README.md](opencode/README.md).\n\n\u003e **Born from a real incident.** This started after [Claude Code executed a destructive `chown -R` on `/etc/`](https://github.com/anthropics/claude-code/issues/39283) — a multi-hour recovery. The built-in permission system wasn't enough. This is defense-in-depth.\n\n\u003e **Hardened by self-audit.** The guard was put through a white-box audit of its *own* code, which found five real bypasses (credential reads via `python3 -c`, `${IFS}` obfuscation, a fail-open on a missing rules file, …). All five are fixed, each with a regression test. What it deliberately does **not** try to do is documented honestly in [THREAT-MODEL.md](THREAT-MODEL.md) — it's a bouncer inside the wall, not the wall itself.\n\n### Quickstart\n\n```bash\n# Claude Code: point a PreToolUse hook at command-guard.py (full guide in INSTALL.md)\ncp security-rules.example.json ~/.claude/safety-guard/security-rules.json\n# then register hooks/command-guard.py as a PreToolUse hook in ~/.claude/settings.json\n\n# opencode: drop the plugin in and reuse the same rules — see opencode/README.md\n```\n\n\u003e **Install:** A full, from-scratch setup guide lives in [INSTALL.md](INSTALL.md). This README explains *what* the guard does and *why*; INSTALL.md explains *how* to deploy it.\n\n\u003e Built and maintained by [inoX-Network](https://inox-network.de). Bug reports — especially new bypasses — are very welcome.\n\n---\n\n## What's new in v2\n\nIf you used the earlier version, here is what changed:\n\n- **Six tool matchers instead of two.** The hook now guards `Bash`, `Read`, `Write`, `Edit`, `MultiEdit`, and `NotebookEdit`. The write tools are checked against protected paths **and** self-protection — they were previously a blind spot.\n- **Real level differentiation.** The hook now evaluates `override_level` 0/1/2/3 individually. It is no longer \"any override active → everything allowed\". Paths and sudo are unlocked *per level*.\n- **Agent scoping.** Subagents do **not** inherit the coordinator's or main session's override. Each agent needs its own override file bound to its `agent_id`, or it runs at level 0.\n- **Self-protection.** The hook, the rules file, the rules document, the active override directory and the `bin/` scripts can never be written by the AI — no override lifts this.\n- **Approval channel.** The AI can no longer write itself an override. It writes a *proposal*; only the owner activates it via an owner-exclusive script invoked through `!`.\n- **Dev mode.** A supervised, time-boxed way for the owner to let the AI edit the hook *sources* — without opening up settings, the override dir, or the rules.\n- **Fail-closed (breaking change).** When the rules file or input is missing/unparsable, the hook now behaves **conservatively** (blocks / discards the override) instead of allowing through.\n- **Audit log.** Every decision is written to a JSONL audit log with secret redaction.\n- **`expires_at` timestamps.** Overrides now end on an ISO-8601 expiry (or when the owner removes the file). The old \"the instance deletes its own file\" model is gone — the AI cannot delete active override files.\n\n---\n\n## Features\n\n- **Blocked patterns** — `rm -rf /`, `mkfs`, `chmod 777`, fork bombs, pipe-to-shell, recursive `chown`/`chmod`/`chgrp` on system paths. **Always** blocked, even with an active override.\n- **Git safety** — `git reset --hard`, force-push (incl. `--force-with-lease`), `commit --no-verify`, `commit --amend`, `git add -A` / `git add .`, and writing `git config` are **always** blocked. Force-push to `main`/`master` has its own dedicated rule on top.\n- **Self-protection** — The AI cannot write the hook, the rules file, the rules document, the active override directory, or the `bin/` scripts — via Bash **or** Write/Edit. No override lifts this.\n- **Owner-only commands** — The approval and dev-mode scripts are hard-blocked for AI Bash calls, so the AI cannot grant itself rights.\n- **Protected paths** — Level-dependent write protection for `~/.ssh`, `~/.gnupg`, `/etc/shadow`, `/boot`, `/usr/bin`, etc.\n- **Credential \u0026 `.env` read protection** — The Read tool cannot reach private keys, cloud credentials, or `.env` files without a level-1+ override. Public keys and SSH config stay open.\n- **3-level, agent-scoped override system** — Scoped, explicit, auditable, and per-instance.\n- **Audit log** — Every allow/block decision is logged (JSONL) with secret redaction.\n- **Desktop notifications** — Optional heads-up on package installs.\n- **Prompt injection detection** — Warns (doesn't block) when suspicious keywords appear in a command.\n\n---\n\n## How it works\n\n```\ntool call ──► PreToolUse hook ──► command-guard.py ──► reads agent_id + tool from stdin\n                                       │\n        ┌──────────────────┬──────────┴───────────┬──────────────────────────┐\n        ▼                  ▼                       ▼                          ▼\n      Read              Write/Edit/             Bash                       other\n                        MultiEdit/                                         tools\n        │               NotebookEdit             │                          │\n        ▼                  │                      ▼                       EXIT 0\n  env / credential         ▼            ALWAYS, no override:               (allow)\n  read protection    self-protection?   blocked_patterns · owner_only ·\n        │            env write? path?   force-push · git-safety ·\n        │                  │            self-protection\n        ▼                  ▼                      │\n   allow / block      allow / block               ▼\n                                        load override for THIS context\n                                        (agent_id → no inheritance)\n                                                   │\n                                                   ▼\n                                        level-dependent: protected paths,\n                                        sudo allowlist, notify, injection\n                                                   │\n                                                   ▼\n                                            allow (EXIT 0) / block (EXIT 2)\n```\n\nEvery branch ends by writing an audit line. `EXIT 0` allows the tool call, `EXIT 2` blocks it.\n\n### Bash checks, in order\n\nThe first group runs **always** — an active override never weakens it:\n\n1. **Blocked patterns** (`rm -rf /`, `mkfs`, recursive `chown` on system paths, …)\n2. **Owner-only commands** (`grant-override`, `hook-dev-mode`)\n3. **Force-push** to `main`/`master`\n4. **Git-safety** ops (`reset --hard`, `--no-verify`, `--amend`, `add -A`/`.`, writing `config`)\n5. **Self-protection** — write access to the security system's own files\n\nOnly after those does the hook load the override for the calling context and run the **level-dependent** checks:\n\n6. **Protected paths** — level 0: none; level 1: only explicitly granted paths; level 2+: all protected paths (single ops — recursive-on-system stays hard-blocked above)\n7. **Sudo allowlist** — base allowlist plus `additional_sudo` grants; level 2+ (or `additional_sudo: \"all\"`) allows all sudo\n8. **Confirmation** desktop notification, then **prompt-injection** warning\n\n### Read checks\n\n1. **`.env` files** require a level-1+ override\n2. **Always-blocked reads** (`/etc/shadow`, `/etc/gshadow`) — no override possible\n3. **Always-allowed** files (public keys, `~/.ssh/config`, `known_hosts`, `authorized_keys`)\n4. **Override-required** files (private keys, cloud credentials) — need a level-1+ override\n\n### Write / Edit / MultiEdit / NotebookEdit checks\n\n1. **Self-protection** — no override lifts it (only dev mode unlocks the hook *sources*)\n2. **`.env` write** — requires a level-1+ override\n3. **Protected paths** — same level logic as the Bash path check\n\n---\n\n## The 3 levels\n\nA static blocklist isn't enough — sometimes you legitimately need extra `sudo` (system maintenance) or even recursive recovery operations. Instead of disabling the guard, the override system grants **scoped, explicit, auditable** permissions per task.\n\n### Level 1 — EXTENDED (deployment, configuration)\n\n- **Allowed:** write to explicitly granted paths (`grants.allowed_paths`, path-boundary-exact); extra sudo commands (`grants.additional_sudo`); single-file ops on normally protected paths.\n- **Not allowed:** recursive operations on protected paths; operations on system paths (`/usr/`, `/lib/`, `/bin/`, `/sbin/`); `chown`/`chmod` on `/etc/` beyond explicitly named files.\n- **Explanation duty:** WHAT + WHY.\n\n### Level 2 — FULL (system maintenance, security fixes)\n\n- **Additionally allowed:** write access to **all** normally protected paths; **all** sudo commands; single-file ops on system paths (e.g. `/etc/ssh/sshd_config`).\n- **Not allowed:** recursive operations on system paths — never.\n- **Approval friction:** `--confirm FULL` required when granting.\n- **Explanation duty:** WHAT + WHY + RISK + concrete ROLLBACK command.\n\n### Level 3 — CRITICAL (emergencies, maximum risk)\n\n- **Additionally allowed:** recursive operations on non-system paths that could reach system paths via bind mounts.\n- **Mandatory preconditions:** a snapshot created beforehand and its ID documented (`--snapshot \u003cID\u003e`); `--confirm CRITICAL` **and** `--snapshot` when granting; a maximum runtime (`--minutes`, default 120, max 1440); double confirmation; foreground only, no background agent.\n- **Explanation duty:** full briefing before *every* command; wait for an explicit \"Continue\".\n\nAt all levels, the always-blocked group (blocked patterns, git-safety, self-protection, owner-only) stays in force — level 3 included.\n\n---\n\n## Agent scoping\n\nThe hook reads `agent_id` from the PreToolUse stdin and uses it to pick the matching override file:\n\n- **Main session / coordinator** → override files **without** an `agent_id` field. These require a mandatory `expires_at` (an override without expiry is discarded).\n- **Subagent** → only override files named `agent-\u003cagent_id\u003e.json` whose `agent_id` matches **exactly**.\n\n**No inheritance:** a subagent never sees the coordinator's override, and the main session never sees an agent-bound one. Without a matching, confirmed, unexpired override, a context runs at **level 0**. When several valid overrides match, the **highest** `override_level` wins.\n\n---\n\n## The approval channel\n\nThe AI **cannot** grant itself an override. The active override directory is self-protected, so the AI cannot write there. The flow is:\n\n1. **The AI detects a need** (something was blocked, or it knows it will be) and determines the lowest sufficient level and the minimal scope.\n2. **The AI writes a proposal** (`confirmed: false`) into the pending directory `~/.claude/.sudo-overrides-pending/` — which is deliberately **not** self-protected.\n3. **The AI hands the owner a ready-to-copy command** and explains level + scope:\n   ```\n   ! ~/.claude/bin/grant-override \u003cid\u003e --minutes N [--confirm LABEL] [--snapshot ID]\n   ```\n4. **The owner runs it** (their consent). Because `grant-override` is an owner-only command, only the owner's `!` reaches it. The script sets `confirmed: true`, `label`, `expires_at`, `granted_at`, `granted_by`, then moves the file into the active directory `~/.claude/.sudo-overrides/`.\n5. **The hook reads the activated file** and grants the scope until `expires_at`.\n\n`\u003cid\u003e` is tolerant: a file name or agent id, with/without `.json`, with/without the `agent-` prefix. `--minutes` must be between 1 and 1440 (default 120).\n\n---\n\n## Dev mode\n\nSometimes the owner wants the AI to edit the hook *sources* themselves (the hook, the rules file). Dev mode is the supervised, time-boxed way to do that:\n\n```\n! ~/.claude/bin/hook-dev-mode on \u003cminutes\u003e   # default 30, max 240\n! ~/.claude/bin/hook-dev-mode off\n! ~/.claude/bin/hook-dev-mode status\n```\n\nDev mode releases **only** the hook source files (`~/.claude/hooks` and the live `security-rules.json`). The active override directory, `settings.json`, `bin/`, `CLAUDE.md` and `rules/` stay hard-protected **even in dev mode** — otherwise the AI could write itself an override while editing the hook. `hook-dev-mode` is owner-only, so only the owner's `!` reaches it. **Fail-closed:** if the flag file's `expires_at` is missing/expired or the flag is unparsable, dev mode is treated as OFF.\n\n---\n\n## Self-protection\n\nThese paths can never be written by AI tool calls — neither via Bash nor via Write/Edit. **No override lifts this** (only the owner via `!`, or, for the hook sources only, an active dev mode):\n\n| Protected path | What it is |\n|----------------|------------|\n| `~/.claude/.sudo-overrides` | active override directory |\n| `~/.claude/bin` | approval \u0026 dev-mode scripts |\n| `~/.claude/hooks` | the hook sources (dev-mode-unlockable) |\n| `~/.claude/safety-guard/security-rules.json` | the live rules (dev-mode-unlockable) |\n| `~/.claude/.hook-dev-mode` | the dev-mode flag file |\n| `~/.claude/settings.json`, `~/.claude/settings.local.json` | Claude Code settings |\n| `~/.claude/CLAUDE.md` | AI context file |\n| `~/.claude/rules` | the rules document(s) |\n\nThe list is hardcoded in the hook (not in the JSON rules) on purpose: if it lived in the rules file, the protection list could be edited through itself. The pending directory `~/.claude/.sudo-overrides-pending` is **deliberately not** protected — the AI must be able to drop proposals there.\n\n---\n\n## Audit log\n\nEvery decision (allow or block, by Bash/Read/Write/Edit/… or by the owner scripts) is appended as one JSON line to:\n\n```\n~/.claude/.agent-audit/actions.jsonl\n```\n\nFields: `ts`, `session_id`, `actor` (the `agent_id`, or `main` for the main session), `agent_type`, `tool`, `target`, `decision`, `reason`, `level`. Before the `target` (the command or file path) is logged it runs through **secret redaction** — passwords, tokens, `key=value` secrets, `--password`/`--token` flags and `Authorization:` headers become `[REDACTED]`, and the field is truncated to 600 characters. Logging failures never block the guard.\n\n---\n\n## Credential \u0026 `.env` read protection\n\nThe hook intercepts Read (and `.env` writes) and applies tiers:\n\n| Tier | Behavior | Examples | Override |\n|------|----------|----------|----------|\n| **Always allowed** | no restriction | `~/.ssh/*.pub`, `~/.ssh/config`, `~/.ssh/known_hosts`, `~/.ssh/authorized_keys` | not needed |\n| **Override required** | blocked without level 1+ | `~/.ssh/id_*`, `~/.aws/credentials`, `~/.aws/config`, `~/.npmrc`, `~/.docker/config.json`, `~/.gnupg/` | level 1+ |\n| **`.env` files** | read **and** write require level 1+ | `.env`, `.env.local`, `.env.development`, `.env.production` (matched by basename, any directory) | level 1+ |\n| **Always blocked** | cannot be read | `/etc/shadow`, `/etc/gshadow` | none |\n\nWithout this, a prompt-injection or confused-deputy attack could trick the AI into reading your private key or `.env` and exfiltrating it via a later command.\n\n---\n\n## What's always blocked\n\nNo override unlocks any of these:\n\n| Category | Examples / patterns |\n|----------|---------------------|\n| Catastrophic `rm` | `rm -rf /`, `rm -rf ~`, `rm -rf /*`, `rm -rf .`, `rm -rf $HOME` |\n| Permission destruction | `chmod 777`, `chmod -R 777` |\n| Drive overwrite | `mkfs`, `dd if=… of=/dev/{sd,nvme,hd}`, `\u003e /dev/sd` |\n| Recursive on system paths | `chown -R` / `chmod -R` / `chgrp -R` on `/etc`, `/usr`, `/var`, `/lib`, `/bin`, `/sbin`, `/boot` — the exact pattern from [the incident](https://github.com/anthropics/claude-code/issues/39283) |\n| Remote-code execution | `curl … \\| sh`, `wget … \\| bash`, `eval … base64`, `python -c … import os … system` |\n| Fork bomb | `:(){ :\\|:\u0026 };:` |\n| Git safety | `git reset --hard`, force-push (`-f` / `--force` / `--force-with-lease`), `commit --no-verify`, `commit --amend`, `git add -A` / `git add .`, writing `git config` |\n| Force-push to primary | `git push --force` / `-f` / `--force-with-lease` to `main` / `master` (dedicated rule) |\n| Self-protection paths | the hook, rules file, rules doc, active override dir, `bin/` (see above) |\n| Owner-only commands | `grant-override`, `hook-dev-mode` (AI Bash calls) |\n| Credential reads | `/etc/shadow`, `/etc/gshadow` |\n\n---\n\n## Configuration reference\n\nThe hook reads its rules from `~/.claude/safety-guard/security-rules.json` (see [security-rules.example.json](security-rules.example.json) for a complete starting point). Top-level keys:\n\n| Key | Type | Description |\n|-----|------|-------------|\n| `blocked_patterns` | `string[]` | Always-blocked patterns. Each entry is treated as a regex if it contains regex metacharacters, otherwise as a literal substring. A literal pipe must be written `\\|`. |\n| `owner_only_commands` | `string[]` | Commands only the owner may run via `!`; hard-blocked for AI Bash. |\n| `blocked_git_ops` | `string[]` | Always-blocked git operations (regex). |\n| `blocked_paths_write` | `string[]` | Paths protected from writes (supports `~`); level-dependent. |\n| `allowed_sudo` | `string[]` | Base allowlist of commands permitted after `sudo`. |\n| `require_confirmation` | `string[]` | Substrings that trigger a desktop notification. |\n| `protected_reads.always_allowed` | `string[]` | Read tool may always access (supports `*` globs). |\n| `protected_reads.require_override_1` | `string[]` | Read needs a level-1+ override. |\n| `protected_reads.always_blocked_reads` | `string[]` | Never readable, no override. |\n| `protected_reads.env_files_require_override_1` | `string[]` | `.env` filenames whose read **and** write need level 1+. |\n| `blocked_bash_patterns_force_push` | `string[]` | Regexes blocking force-push on `main`/`master`. |\n| `prompt_injection_keywords` | `string[]` | Keywords that emit a stderr warning (no block). |\n\n\u003e The self-protection path list is **not** in this file — it is hardcoded in the hook so it cannot be edited through itself.\n\n### Override file format\n\nA proposal you write into the pending directory:\n\n| Field | Type | Set by | Description |\n|-------|------|--------|-------------|\n| `override_level` | `1 \\| 2 \\| 3` | you | Permission level (must be an int, not bool). |\n| `task` | `string` | you | Non-empty description of what it's for. |\n| `project` | `string \\| null` | you | Optional associated project. |\n| `confirmed` | `boolean` | you `false` → script `true` | The hook only honors `true`. |\n| `agent_id` | `string` | you | **Subagent only** — must equal the subagent's `agent_id`. Omit entirely for the main session. |\n| `expires_at` | ISO-8601 | the script (`--minutes`) | Mandatory for main-session overrides; optional but honored for agent overrides. |\n| `label` | `string` | the script | `EXTENDED` / `FULL` / `CRITICAL`. |\n| `granted_at`, `granted_by` | string | the script | Audit metadata. |\n| `snapshot_id` | `string` | the script (`--snapshot`) | Level 3 only. |\n| `grants.additional_sudo` | `string[] \\| \"all\"` | you | Extra sudo commands (base allowlist still applies). |\n| `grants.allowed_paths` | `string[]` | you | Level-1 write paths, path-boundary-exact. |\n| `grants.recursive_operations` | `boolean` | you | Informational. |\n| `grants.system_paths` | `boolean` | you | Informational — **not evaluated** by the hook; only the level controls system-path access. |\n\n---\n\n## FAQ\n\n**Q: Does this replace Claude Code's built-in permission system?**\nA: No. It runs *alongside* it as an extra layer that catches dangerous patterns before they reach the permission prompt.\n\n**Q: Can the AI grant itself an override?**\nA: No. It can only write a *proposal* into the pending directory. Activation requires the owner to run the owner-only `grant-override` script via `!`, which bypasses the guard. The active override directory is self-protected.\n\n**Q: What happens if the rules file is missing or broken?**\nA: The hook is **fail-closed**. A missing rules file means no Bash rules can be evaluated; an unparsable override or input is discarded rather than trusted. (This is a breaking change from the earlier fail-open behavior.)\n\n**Q: Do subagents inherit my session's override?**\nA: No. Overrides are scoped by `agent_id`. A subagent needs its own `agent-\u003cagent_id\u003e.json` override, or it runs at level 0.\n\n**Q: How does an override end?**\nA: When its `expires_at` passes, or when the owner removes the file via `! rm`. The AI cannot delete active override files.\n\n**Q: Why is force-push to main/master always blocked?**\nA: Rewriting history on a primary branch is almost always a mistake when done by an AI agent. Do it manually outside Claude Code if you truly need to.\n\n**Q: Can the AI read my private SSH key or `.env`?**\nA: Not without an explicit level-1+ override. Public keys (`*.pub`) and `~/.ssh/config` are always allowed. This now also covers the Bash path — `cat`/`base64`/`cp`-source/`dd if=`/`xxd`/`head` on a protected path are blocked, not just the Read tool. It also covers **directory-level exfiltration**: handing a whole credential directory to a recursive reader (`tar`/`zip`/`rsync`/`gpg`/`scp`/`grep -r ~/.ssh` …) is blocked even though no individual key file is named, while metadata-only commands (`ls`/`find`/`stat` on the directory) stay allowed. The Bash check resolves **direct** path references; variable indirection (`X=key; cat $X`) and interpreter string literals (`python -c \"open(...)\"`) stay outside its scope — the same inherent limit as `blocked_patterns`. It's defense-in-depth covering the realistic attack path, not a watertight guarantee.\n\n**Q: Does prompt-injection detection block anything?**\nA: No — it only writes a warning to stderr. It's a heads-up, not a hard block.\n\n---\n\n## Contributing\n\nIssues and PRs welcome. If you've been bitten by a similar incident and have patterns to add to the blocklist, please share them.\n\nBefore opening a PR, run the test suite (no install needed — the tests isolate their own runtime state):\n\n```bash\npython3 tests/test_command_guard.py\npython3 tests/test_freigabe_e2e.py\n```\n\nSee [INSTALL.md](INSTALL.md#e-verify-its-armed) for how to verify a live installation.\n\n## License\n\n[MIT](LICENSE)\n\n---\n\n*Born from a real incident. Built to prevent the next one.*\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FinoX-Network%2Fclaude-code-safety-guard","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FinoX-Network%2Fclaude-code-safety-guard","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FinoX-Network%2Fclaude-code-safety-guard/lists"}