standard, strict, and paranoid. Each level is a preset that expands into the same three capabilities. A level is shorthand, not a separate code path.
Set the level with
safety.level in policy.json or the CC_SAFETY_NET_LEVEL environment variable. The higher value becomes the effective base level. The individual toggles on this page set one capability on top of that base. CC Safety Net reports any combination that does not match one of the three presets as the effective level custom.
Mode and debug flags use
CC_SAFETY_NET_* environment variables. Older SAFETY_NET_* names (without the CC_ prefix) are also accepted for strict, paranoid, paranoid-rm, paranoid-interpreters, and worktree toggles. See Environment for the full list and for the exact precedence between policy and environment, and Policy for safety.level and safety.overrides.Default mode
Default mode is thestandard safety level. You do not need environment variables to start, and CC Safety Net protects against the built-in destructive git and filesystem patterns. Start here unless you need the stricter checks below.
Some failures are handled the same way at every level:
- Invalid hook input JSON is always blocked (fail-closed), in every mode.
- If the analyzer itself throws an unexpected error, the command is always blocked (fail-closed) with a “failed closed” reason, in every mode.
- A command that exceeds the parser’s recursion or structural validation limits is always blocked, in every mode. These resource-exhaustion bounds are not relaxed by the standard level.
- If a command cannot be tokenized by the shell parser (for example, an unterminated quote), CC Safety Net runs a fallback text scan for known-dangerous patterns. If the scan matches, the command is blocked; if it does not match, the command is allowed through. So
echo 'unterminatedis allowed, whilegit reset --hard 'unterminatedis still blocked by the heuristic scan. - Dynamic recursive-delete targets are not categorically blocked in standard.
rm -rf "$target"is allowed here and blocked only once the fail-closed capability is on.
eval/source of a single fully literal local generator command such as eval "$(ssh-agent -s)". These are deliberate trade-offs: standard is best-effort against adversarial or dynamic input. Use strict or paranoid when commands may come from prompt injection or another adversarial context.
Standard never relaxes sensitive content access, user-configured deny paths and their descendants, or the catastrophic protections (root and home recursive deletion, Git metadata, and the canonical policy.json).
Strict mode (CC_SAFETY_NET_STRICT=1)
Strict mode turns on the fail_closed capability. It tightens five distinct things. Unparseable commands are the first:
- Unparseable commands are blocked. CC Safety Net denies any command the parser cannot split into tokens, even when the fallback text scan finds no dangerous pattern. The reason is “Command could not be safely analyzed (strict mode)”.
echo 'unterminatedis allowed in standard and blocked here. - Heredocs fail closed. A command containing a heredoc is denied unless it has exactly one unexpanded heredoc, on stdin, no other input redirection, and a consumer that is a literal
cat,tee,git apply,git commit,gh pr create, orgh issue create. A heredoc is unexpanded when the delimiter is quoted (<<'EOF'), or when the delimiter is unquoted and its body contains no$, backtick, or backslash. Sopython3 - <<'PY'and acat <<EOFwhose body mentions$HOMEare denied here, whilegit commit -F - <<'EOF'and a plain-textcat <<EOFstill pass. See Heredoc analysis for the exact gate. - Unverifiable destructive targets are blocked by the five rules listed below.
- Metadata-only sensitive-path discovery is blocked.
test -f ~/.ssh/id_rsaandfind ~/.ssh -type fare allowed in standard and denied in strict. - The standard-only inline-data relaxation is disabled. In standard, a sensitive path literal inside an interpreter’s inline code is inert data unless the code names a filesystem, command-execution, or eval marker. That covers every code interpreter except the POSIX shells, whose inline code the guard walks as shell instead. Strict removes the allowance and scans inside literal text as well. See Secret protection.
fail_closed is on:
Invalid hook input JSON, analyzer exceptions, and parser resource-limit failures already fail closed in every mode. Strict does not add those checks.
Enable the strict safety level
CC_SAFETY_NET_STRICT=1 sets the same capability and is still accepted:
When to use the strict safety level
Enable strict mode when commands can come from an adversarial or untrusted context. You can also use it when you want maximum protection and can accept occasional false positives for unusual command syntax.Individual strict-tier rules can be turned off through
destructive_command_protection.overrides in policy.json, and any strict-tier rule can be force-enabled under standard with an "on" override. Strict is still strict for fail-closed outcomes that have no destructive-command rule id, such as parser fail-closed and sensitive-path outcomes.Paranoid mode (CC_SAFETY_NET_PARANOID=1)
The paranoid level is strict plus two capabilities, paranoid_rm and paranoid_interpreters. These checks may disrupt some normal workflows, so they are opt-in. You can select the whole level or activate individual capabilities. A level with only one of them has the effective level custom.
rm check (CC_SAFETY_NET_PARANOID_RM=1)
By default, CC Safety Net allows rm -rf within the current working directory because it assumes that deletion inside your project root is intentional. The paranoid rm check blocks non-temp recursive forced removal even inside the current working directory. rm -rf ./cache matches rm.recursive-force-paranoid. The PowerShell equivalent Remove-Item ./cache -Recurse -Force matches powershell.remove-item-recursive-force-paranoid.
Temp targets and directories listed in destructive_command_protection.allow_paths are still allowed under this check.
Interpreter one-liners (CC_SAFETY_NET_PARANOID_INTERPRETERS=1)
Interpreter one-liners can hide destructive commands inside strings that are hard to inspect statically. Below paranoid, only a one-liner whose body contains a dangerous command is blocked (rule interpreter.dangerous-command). With this check enabled, every interpreter one-liner is blocked regardless of its content, via interpreter.one-liner-paranoid:
python -c '...'(alsopython3andpython2)node -e '...'ruby -e '...'perl -e '...'
python -c "print(1)" is allowed in standard and strict, and blocked here.
Enable the paranoid safety level
Enable both paranoid checks without fail-closed behavior
Enable individual paranoid checks
CC_SAFETY_NET_PARANOID=1 is equivalent to enabling both CC_SAFETY_NET_PARANOID_RM=1 and CC_SAFETY_NET_PARANOID_INTERPRETERS=1. It does not turn on fail_closed, so on top of the default standard level it produces the effective level custom rather than paranoid. Use CC_SAFETY_NET_LEVEL=paranoid (or safety.level: "paranoid") when you want the full preset.
Worktree mode (CC_SAFETY_NET_WORKTREE=1)
Linked Git worktrees can provide isolated workspaces. Worktree mode relaxes selected local-discard rules only when CC Safety Net confirms that the current working directory is inside a linked worktree.
Enable worktree mode
workflow.worktree_mode: true in policy.json. CC Safety Net combines the two settings as a logical OR. Either one enables worktree mode.
Commands allowed in a linked worktree
When worktree mode is active and the cwd is confirmed to be a linked worktree, the following commands are permitted:git restore <file>andgit restore --worktree <file>git checkout -- <file>,git checkout <ref> -- <file>,git checkout --force, and ambiguous multi-positional checkout formsgit reset --hardandgit reset --mergegit clean -f(and combined short flags like-fd)git switch --discard-changesandgit switch -f / --force
Commands blocked in linked worktrees
These commands affect shared refs or other worktrees and are never relaxed, regardless of worktree mode:git push --forceaffects the remotegit branch -Dforce-deletes a branch that is shared across worktreesgit stash drop/git stash clearchanges the stash shared across worktreesgit worktree remove --forcecould delete another worktree
Linked-worktree detection
Worktree detection is fail-closed: if CC Safety Net cannot positively identify the cwd as a linked worktree, the stricter default rules remain in effect. Specifically:- A linked worktree is identified by a
.gitfile (not a directory) whose resolved git directory contains acommondirfile. Main worktrees and submodules are not relaxed. - The cwd walk uses
realpathso symlinked paths resolve correctly. git -C <path>arguments are honored; unresolved targets keep the command blocked.- Relaxation is disabled if
--git-dir/--work-treeis passed, or ifGIT_DIR/GIT_WORK_TREE/GIT_COMMON_DIR/GIT_INDEX_FILEis set in the environment. - Certain local discards are never relaxed even inside a confirmed worktree: commands with dynamic arguments containing
$,*,?, or[; forced branch resets (git checkout -B/-Bforgit switch -C/-Cfwith-for--discard-changes);git cleanwith more than one-f; and any command using--recurse-submodules(or a recursive-submodule config).
Safety level summary
Worktree mode is independent of the level: it relaxes local-discard rules inside a confirmed linked worktree.
See Environment for every variable that selects a level or forces a capability, the legacy
SAFETY_NET_* aliases, and the full precedence rules between policy.json and the environment.