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-string 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 — 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 exactly as they would to git reset --hard. There are no built-in defaults — configure only wrappers you intentionally 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 edge cases
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
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. Reads of the built-in sensitive set —.env and its variants, ~/.ssh/id_*, ~/.aws, ~/.kube/config, coding-CLI credential stores, and more — 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_rsaorfind ~/.ssh -type f). 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, and the canonical userpolicy.json) 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 covers the canonical userpolicy.json only. rule.json, rulebooks, lockfiles, and caches are best-effort against agent modification: in the ready state an agent can already remove a rulebook entry from rule.json, and removal is not drift, so nothing gates it. Protecting rule.json is a deferred product decision.
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. And 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.
Execution hosts split three ways:
- An
execcall with nohost,host: "auto", orhost: "gateway"is analysed as a local Gateway call, with paths resolved against the agent workspace. - An explicit
host: "sandbox"orhost: "node"is blocked as unsupported rather than analysed, 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 analysed 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. Relocated state is handled the way the hosts resolve it —HERMES_HOME for Hermes, then OPENCLAW_STATE_DIR and the directory of OPENCLAW_CONFIG_PATH for OpenClaw, falling back to ~/.hermes and ~/.openclaw. The Windows defaults are not: install and detection target the POSIX path there, so the Hermes install writes the plugin to a directory Hermes does not read, and OpenClaw’s own CLI installs correctly while doctor misreports the state.
Platform-specific issues
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, Hermes Agent, Kimi Code)
The Antigravity CLI, Cursor, 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 four targets first removes every npx cache entry that contains cc-safety-net (the _npx/*/node_modules/cc-safety-net entries), so a plain reinstall picks up the new release — no manual cache clearing needed. Installs for other targets do not touch this cache.
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.