Standard mode is best-effort for adversarial or dynamic input, and several items below are relaxations that strict or paranoid mode closes. Where that is the case, this page says so.
Command-analysis limits
Behavior inside binaries
If a command does not visibly contain a destructive operation, command-string analysis cannot infer it.some-tool --task destructive-cleanup looks benign to CC Safety Net because it cannot inspect what an arbitrary binary does at runtime. Use sandboxing for that threat class.
Unconfigured or opaque command proxies
A tool that runs a shell command on your behalf (for examplertk git reset --hard) is analyzed only when you declare it as a transparent wrapper:
rtk git reset --hard as they would to git reset --hard. There are no built-in defaults. Configure only wrappers you trust. Reserved commands (git, busybox, the built-in analyzed commands, shell wrappers, and interpreters) cannot be registered as wrappers.
The residual limit is narrower than “every proxy”:
- A proxy that is not listed in
transparent_wrappersis not unwrapped. - A proxy that rewrites or hides its child command, rather than executing a visible one, cannot be unwrapped even when it is configured.
transparent_wrappers lives in rule.json, so a scope whose rule.json is unreadable loses that scope’s wrappers until it is fixed. See Configuration recovery.
eval and dynamic execution
Commands that construct and execute code at runtime cannot be fully analyzed, because the executed string is not present in the command text at analysis time:bash -c and interpreter code arguments, but if the code is fetched from a remote source or assembled from variables, the destructive payload is invisible to command-string analysis. This is a fundamental limit of the approach.
Parser limits
Interpreter long-form flags
CC Safety Net extracts the code passed to an interpreter’s-c or -e flag (for python, python2, python3, node, ruby, perl) and scans it for embedded destructive operations. The long-form equivalents (--eval, --execute, --print, --require) and the attached =value form (--eval='code') are not recognized in every code path. When the long form is used, the code argument may not be extracted for recursive analysis.
If interpreter bypass is a concern in your environment, enable paranoid interpreters mode (CC_SAFETY_NET_PARANOID_INTERPRETERS=1), which blocks all interpreter one-liners outright regardless of content.
Attached short-option values
Bundled short flags (for example-rf) are correctly split into { -r, -f }. However, the attached value form for short options (for example -Cfoo where foo is the value of -C) is handled inconsistently across analyzers. Blocking a flag like -f may produce a false positive when it is actually part of an attached option value like -Cfoo.
This is most relevant when writing precise custom rules. When in doubt, use strict or paranoid mode for stronger protection.
Symlink TOCTOU risk
rm -rf target classification resolves symlinks to their canonical targets before deciding whether a path is dangerous. There is an unavoidable time-of-check-to-time-of-use (TOCTOU) window between when the analyzer resolves a path and when the shell actually executes the rm. A symlink could be repointed in that window.
This is inherent to any pre-execution command analysis tool; fully closing it would require running inside the kernel or intercepting syscalls, which is out of scope for a hook. The paranoid rm mode (CC_SAFETY_NET_PARANOID_RM=1) provides a stricter stance for operators who want to block more aggressively.
Out-of-scope protections
These are boundaries CC Safety Net does not attempt to cover. Most of them need containment rather than semantic analysis, which is a different layer. See where CC Safety Net cannot help for the sandboxing counterpart of each one.Sensitive-path coverage is bounded, not a general read boundary
CC Safety Net does protect sensitive paths. The built-in sensitive set includes.env variants, ~/.ssh/id_*, ~/.aws, ~/.kube/config, coding-CLI credential stores, and more. Reads of these paths are blocked, as are user-configured deny paths and all of their descendants. Protection applies across supported command, path, search, and patch shapes, including unknown-tool fallback inspection.
Coding-CLI coverage comes in two tiers. The credential tier (auth tokens and credential stores) is on by default. The config tier (settings and MCP config files, which agents edit as routine work) ships off and needs an explicit "on" override in secret_protection.overrides. One consequence worth knowing: Antigravity’s only rule (secret.cli.antigravity) lives in the opt-in config tier, so Antigravity has no on-by-default credential rule. The full tier list, with the paths each rule protects, is in the Secret protection reference.
The residual limit is that this is a bounded pattern set over supported shapes, not a general read boundary:
- A credential in a file whose name and extension are not on the pattern list is not recognized.
- Standard mode allows standalone metadata-only checks of built-in sensitive paths (for example
test -f ~/.ssh/id_rsa,find ~/.ssh -type f,ls -la ~/.ssh, orstat .env). Strict and paranoid block that discovery. Configured deny paths are never relaxed in any mode. - Setting
secret_protection.enabledtofalseinpolicy.jsonturns the whole stage off, deny paths included.
No complete filesystem containment
CC Safety Net denies a tool call before it runs; it does not enforce filesystem permissions. The block message is guidance to the agent, not a claim of complete filesystem enforcement. Catastrophic protections (recursive deletion of root or home, the protected Git metadata set, andpolicy.json in both scopes) are always enforced, but when you need complete protection rather than best-effort interception, use a trusted write broker, operating-system permissions, a sandbox, or equivalent runtime enforcement.
No network layer
Runtime evaluation performs no network requests, and nothing in the guard pipeline inspects or filters egress. Data exfiltration over the network, domain allowlisting, and container boundaries are outside the product. Those are exactly what sandboxing’s network restrictions address.General attack prevention
Blocking prompt-injection-driven exfiltration, authenticating users, and enforcing container boundaries are all out of scope. CC Safety Net assumes the agent’s command string is untrusted, but it does not detect hidden behavior inside arbitrary binaries.Custom rule configuration is not tamper-resistant
Tamper resistance coverspolicy.json in both scopes: the user file, and the project file resolved from the execution directory and from the configuration directory. rule.json and rulebooks are not protected. Every rulebook is a live file the runtime rereads on each tool call, so an agent that can write in the rules directory changes what is enforced on the next one.
Removing a source entry from rule.json is no longer the only ungated change. Nothing records what rule update wrote into a vendored rulebook.json, so an edit to a rule’s match or reason inside that file leaves no evidence of the edit. At load the runtime checks the file against the schema and checks that its name matches the source, then enforces whatever it finds. The edit stands until the next rule update overwrites the file. Protecting rule.json is a deferred product decision.
The project policy.json has one deliberate bound. Its protected directory chain stops at its own .cc-safety-net directory, while the user file’s chain covers its directory and every ancestor up to the filesystem root. Walking the project chain further would claim the project directory and every ancestor above it. Those are exactly the paths the destructive-command rules target, and policy protection runs first, so rm -rf . and find . -delete would report this guard’s generic reason in place of their own.
Hermes Agent and OpenClaw coverage boundaries
The Hermes Agent and OpenClaw integrations cover a defined set of tools and execution hosts. Everything outside that set either gets no decision or is blocked outright. The boundaries are listed here.Hermes Agent sees a fixed tool list
The managed Hermes plugin registers a singlepre_tool_call hook and forwards exactly four tools to analysis: patch, read_file, terminal, and write_file. A call to any other Hermes tool is not forwarded and gets no decision.
A !command you type yourself also stays outside: bang-shell input does not raise pre_tool_call, so it goes to Hermes’ own command guard instead. CC Safety Net sees model-generated tool calls only, not every subprocess Hermes starts. If Hermes never loads the plugin, nothing blocks. Only npx cc-safety-net doctor reports a plugin that is present but not enabled.
OpenClaw covers exec on the local Gateway host
The OpenClaw plugin registersbefore_tool_call for the canonical exec tool only. apply_patch and OpenClaw’s read/write/edit file tools are not protected. An exec event with a toolKind discriminator also gets no decision. This excludes tools that share the exec name, such as Code Mode’s JavaScript exec, whose command field is not a proven shell-command mapping.
Execution hosts split three ways:
- An
execcall with nohost,host: "auto", orhost: "gateway"is analyzed as a local Gateway call, with paths resolved against the agent workspace. - An explicit
host: "sandbox"orhost: "node"is blocked as unsupported rather than analyzed, because no test proves a correct path mapping for those hosts. - The configured
tools.exec.hostdefault never changes the analysis: the plugin reads only the call’s ownhostparameter, so a call with nohostis analyzed as a local Gateway call even when the default routes it tonodeorsandbox.
auto case: when a sandbox runtime is active, an auto call runs in the sandbox filesystem while path rules are still evaluated against the Gateway workspace. Command rules are unaffected; path rules can be evaluated against the wrong filesystem.
The Codex-native relay is untested and therefore unclaimed: the live end-to-end tests drive OpenClaw’s own agent runtime, so they prove nothing about a Codex-native shell, patch, or MCP call.
Windows is not supported for either integration
Both integrations assume the POSIX state layout. The hosts resolve relocated state throughHERMES_HOME for Hermes, and through OPENCLAW_STATE_DIR followed by the directory of OPENCLAW_CONFIG_PATH for OpenClaw. They fall back to ~/.hermes and ~/.openclaw. The Windows defaults are not supported. Install and detection target the POSIX path on Windows, so the Hermes install writes the plugin to a directory Hermes does not read. OpenClaw’s own CLI installs correctly, but doctor misreports the state.
Codex coverage boundary
Codex’s unified exec path, the default shell path on macOS and Linux, sends aPreToolUse payload when a command opens a session, but none for write_stdin. Only the command that opened the session is evaluated. Text the model then types into the already-running interactive session reaches the shell without inspection and without an audit entry.
This gap cannot be closed from the hook side: the host emits no event for that call, so there is nothing to analyze. If injection into a running session is a concern in your environment, use sandboxing as the containment layer.
Grok Build is a fail-open host
Grok Build hooks are fail-open by design, and the host exposes nofailClosed knob. Only an explicit deny on stdout blocks a tool call. A hook that crashes, times out, or emits malformed output lets the call proceed.
The adapter still emits an explicit deny for its own fail-closed outcomes, such as truncated tool input or an unusable working directory. On this host it cannot block a call when the failure leaves the adapter with no output at all. If that residual risk matters in your environment, use sandboxing as the containment layer.
Resolve stale integration caches
OpenCode stale cache
OpenCode’s plugin installer can keep serving a stale cached version ofcc-safety-net after a new release is published. If updates do not take effect, clear the cache and reinstall. See the OpenCode installation steps. Run npx cc-safety-net doctor to confirm the detected plugin version.
Stale npx cache (Antigravity CLI, Cursor, Grok Build, Hermes Agent, Kimi Code)
The Antigravity CLI, Cursor, Grok Build, and Kimi Code hooks and the Hermes Agent plugin run throughnpx -y cc-safety-net, so npx’s own cache can also keep serving an old release. Installing any of those five targets first removes every npx cache entry that contains cc-safety-net (the _npx/*/node_modules/cc-safety-net entries). A plain reinstall then picks up the new release without manual cache clearing. Installs for other targets do not touch this cache.
Stale bunx cache
bunx keeps its own per-package cache under the OS temp dir, so abunx cc-safety-net run can serve an old release too. Every cc-safety-net update run clears your cc-safety-net entries there, even when no integration is installed. Exactly one entry survives: a bunx-launched update does not delete the entry it is running from, because removing files in use fails on Windows. That entry re-resolves through bun’s own manifest TTL instead.
What to do if a destructive command slipped through
First, confirm whether you hit a documented boundary or something unexpected:explain command shows the full step-by-step analysis of how CC Safety Net evaluated that specific command, including the effective safety level, so you can tell a documented standard-mode relaxation from a real gap. For the wider symptom-by-symptom walkthrough, start at Troubleshooting.
Then route the report:
- A command shape the rules do not block yet is a coverage gap. It is a public bug. Open a GitHub issue describing the command shape, not a ready-to-paste payload.
- Secret leakage, a write outside the intended directory, and supply-chain or package-integrity problems take the private disclosure path instead.