> ## 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.

# 被阻止的命令及其原因

> CC Safety Net 阻止的 Git、rm、find、设备、PowerShell、cmd 和敏感路径命令的完整参考，以及每项规则所需的安全级别。

CC Safety Net 阻止这些命令，因为它们可能永久破坏数据，包括未提交的更改、stash、远程历史或整个磁盘。下表涵盖默认内置规则，[自定义规则](/docs/zh-Hans/configuration/custom-rules)可以在此基础上扩展。

默认情况下，未标记的行在三个安全级别下都会阻止。标记为 **Strict** 或 **Paranoid** 的行需要对应级别或能力。每个规则 ID 以及关闭非灾难性规则的方法见[破坏性命令规则参考](/docs/zh-Hans/reference/destructive-rules)。启用各级别的方法见[模式](/docs/zh-Hans/configuration/modes)。

本页是行为矩阵，说明*阻止什么*以及各条边界的位置。它不是架构页面：有序防护流程见[架构](/docs/zh-Hans/guides/architecture)，分类器如何得到判定见[分析引擎](/docs/zh-Hans/guides/analysis-engine)，可通过的安全变体见[被允许的命令](/docs/zh-Hans/reference/allowed-commands)。

## 每个级别增加的阻止

| 级别 | 增加的阻止 |
| - | - |
| [`standard`](/docs/zh-Hans/configuration/modes#default-mode) | 阻止所有未标记项。`echo 'unterminated` 这类看似安全的无法解析文本可以通过；含有可识别破坏性模式的无法解析文本仍会被启发式扫描阻止；[解析器直接拒绝的 shell 语法](/docs/zh-Hans/guides/analysis-engine#shell-syntax-the-parser-refuses)在这一级别同样会被阻止。动态 `rm -rf` 目标**不会**一律阻止。 |
| [`strict`](/docs/zh-Hans/configuration/modes#strict-mode) | `standard` 阻止的全部内容，再加上无法解析的命令、无法验证的破坏性目标、[受支持范围之外的 heredoc](#strict-和-paranoid-中的-heredoc)，以及对内置敏感路径的纯元数据发现。 |
| [`paranoid`](/docs/zh-Hans/configuration/modes#paranoid-mode) | `strict` 阻止的全部内容，再加上即使位于当前工作目录内的非临时递归删除（`rm -r`、`rm -rf`、`find -delete`、`Remove-Item -Recurse -Force`、`cmd /c rmdir /s`），以及所有解释器单行命令，无论其内容。 |

<Note>部分灾难性保护不依赖所选安全级别：根目录和主目录的递归删除保护、Git 元数据保护和规范 `policy.json` 保护会忽略主破坏性命令开关和单条规则的 `off` 覆盖。</Note>

<Note>下面有两类拒绝在 Claude Code 的 hook 上不是阻止，而是权限提示：匹配 `raw-text.dangerous-command`，以及原因为 `shell execution source cannot be verified safely.` 的拒绝。这只发生在 fail-closed 能力关闭（也就是 `standard` 级别）、且权限模式为 `default`、`acceptEdits` 或 `plan` 时。`strict`、`paranoid` 以及其他所有宿主都会拒绝。仍然保持拒绝的两种情况见 [Claude Code 何时改为询问而不是拒绝](/docs/zh-Hans/guides/integration-architecture#when-claude-code-asks-instead-of-denying)。</Note>

<span id="git-commands" />

## Git 命令

| 命令模式 | 阻止原因 |
| - | - |
| `git checkout -- <files>` | 永久丢弃未提交的更改 |
| `git checkout <path>` | 不带 `--` 分隔符的同一种恢复，例如 `git checkout .`、`git checkout src/`、`git checkout src/app.ts` |
| `git checkout --force` / `-f` | 强制丢弃未提交的更改 |
| `git checkout <ref> -- <path>` | 用 ref 中的版本覆盖工作树 |
| `git checkout <ref> <path>` | Git 区分 ref 和 pathspec 时可能覆盖工作树 |
| `git checkout --pathspec-from-file` | 可以从文件列表覆盖多个文件 |
| `git restore <files>` | 丢弃未提交的更改；仅取消暂存请使用 `--staged` |
| `git restore --worktree` / `-W` | 明确丢弃工作树中的更改 |
| `git switch --discard-changes` | 切换分支时丢弃未提交的更改 |
| `git switch --force` / `-f` | 强制切换并丢弃未提交的更改 |
| `git reset --hard` | 删除所有未提交的更改 |
| `git reset --merge` | 可能丢失未提交的更改 |
| `git clean -f` / `--force` | 永久删除未跟踪文件 |
| `git rm --force` / `-f` | 从工作树中删除已跟踪的文件。`--cached` 与 `--dry-run` / `-n` 不会被阻止，不加 force 的 `git rm` 也允许，因为 git 自身的安全检查会把它限制在可从 `HEAD` 恢复的内容上 |
| `git push --force` / `-f` | 破坏远程历史；请改用 `--force-with-lease` |
| `git branch -D` | 不检查合并状态就强制删除分支 |
| `git rebase --abort` | 丢弃 rebase 冲突解决结果 |
| `git merge --abort` | 丢弃 merge 冲突解决结果 |
| `git tag -d` / `--delete` | 永久删除 tag |
| `git reflog delete` | 删除恢复历史 |
| `git stash drop` | 永久删除已 stash 的更改 |
| `git stash clear` | 删除所有已 stash 的更改 |
| `git worktree remove --force` | 不检查是否有更改就强制删除 worktree |

只有一个位置参数的 `git checkout <operand>` 同样按规则 `git.checkout-double-dash` 视为路径恢复，条件是：该参数写成路径形式（`.`、`..`、`./…`、`../…`、绝对路径、盘符路径、末尾带斜杠，或以 `:` 开头的 magic pathspec）、包含 `*`、`?` 或 `[`，或者指向解析后 git 工作目录中已存在的条目。`main`、`feature/x` 这类纯分支名仍然允许，`-b`、`-B`、`--orphan`、`-d` / `--detach`、`-t` / `--track` 也不会被阻止。分析过程不启动 git 子进程，也不解析 ref，因此与已有文件或目录同名的分支会被阻止，提示信息指向 `git switch`。

同时带强制标志和创建/重置标志的 Git 分支变更命令（例如 `git checkout -Bf`、`git switch -Cf --discard-changes`）会被视为强制分支重置并阻止。

<span id="git-ssh-environment-overrides" />

### Git SSH 环境覆盖

Git 接受 `GIT_SSH_COMMAND` 和 `GIT_SSH` 以在网络操作期间运行任意程序，并接受 `GIT_SSH_VARIANT` 来改变调用该程序的方式。规则 `git.ssh-env` 会阻止在命令中设置了这些变量（或通过 `-c` 或 `GIT_CONFIG_*` 设置了 `core.sshCommand`）并运行网络子命令（`clone`、`fetch`、`pull`、`push`、`ls-remote`、`submodule`）的命令：

| 被阻止的模式 | 阻止原因 |
| - | - |
| `GIT_SSH_COMMAND=... git clone` | SSH 覆盖可在网络操作期间执行任意命令 |
| `GIT_SSH=... git fetch` / `pull` / `push` | 同上，适用于 fetch、pull 和 push |
| `GIT_SSH_VARIANT=... git ls-remote` / `submodule` | 改变 Git 传给 SSH 程序的参数约定 |
| `git -c core.sshCommand=... push` | 同一覆盖，通过 Git 配置而不是环境变量设置 |

只有在命令行中设置的覆盖才算数。在代理启动之前由 shell 配置文件导出的 `GIT_SSH_COMMAND` 是你自己的选择，不会阻止 `git fetch`、`git pull` 或 `git push`。

该规则不是灾难性规则，因此可以像其他内置规则一样关闭。参见[允许某条内置规则](/docs/zh-Hans/reference/destructive-rules#allow-a-built-in-rule)。

<span id="filesystem-commands" />

## 文件系统命令

| 命令模式 | 阻止原因 | 级别 |
| - | - | - |
| `rm -rf /`、`~`、`$HOME` | 删除根目录或主目录极其危险 | 全部，灾难性 |
| `rm -rf .git` 和受保护的 hooks | 破坏仓库历史和 hooks | 全部，灾难性 |
| `rm -rf .`（cwd 本身） | 删除整个工作目录会移除你当前所在的工作区 | 全部 |
| cwd 以外的 `rm -rf` | 递归删除 cwd 之外的绝对路径、父级路径或非临时路径 | 全部 |
| 主目录为 cwd 时运行 `rm -rf` | 以主目录为递归删除的起点并不安全，请先切换到项目目录 | 全部 |
| `rm -rf "$target"` 等动态目标 | 由变量、反引号或替换拼出的目标无法在执行前验证 | **Strict**、paranoid |
| `rm -rf ./cache`（cwd 内的非临时路径） | 即使在工作目录内，也限制递归删除 | **Paranoid**（[`CC_SAFETY_NET_PARANOID_RM=1`](/docs/zh-Hans/configuration/modes#paranoid-rm-check)） |
| `cleanup() { rm -rf ../outside; }; cleanup` | shell 函数的正文会在调用位置分析；只定义而不调用不会执行任何操作 | 全部 |
| `find . -delete`，或 cwd 外的非临时起点 | 永久删除符合条件的文件；可先用 `-print` 预览 | 全部 |
| `find ./cache -delete`（cwd 内的非临时起点） | `find -delete` 在 cwd 内的放行取决于 `rm.recursive-force-paranoid`，因此凡是 `rm -rf ./cache` 被阻止的场合，它都以 `find.delete` 阻止 | **Paranoid**（[`CC_SAFETY_NET_PARANOID_RM=1`](/docs/zh-Hans/configuration/modes#paranoid-rm-check)） |
| `find .git -delete` | 选择受保护的 Git 元数据进行删除 | 全部，灾难性 |
| `find -exec rm -rf ...` | 在 `find` 的 `-exec` 块中执行递归强制删除 | 全部 |
| `xargs rm -rf` | 动态输入使目标不可预测 | 全部 |
| `xargs <shell> -c` | 可以执行任意命令 | 全部 |
| `parallel rm -rf` | 动态输入使目标不可预测 | 全部 |
| `parallel <shell> -c` | 可以执行任意命令 | 全部 |

递归 `rm` 的目标，无论是 `rm -rf`，还是不带 `-f` 的 `rm -r`、`rm -R`、`rm --recursive`，都使用以下首个匹配分类顺序：不受支持的 Windows UNC 或设备目标、根目录或主目录、受保护的 Git 元数据、临时路径、动态目标、配置的允许路径、主目录作为工作目录、工作目录本身、工作目录内部、工作目录外部。临时路径在所有级别都允许。当前工作目录内的其他路径在 paranoid `rm` 未启用时允许。上表中 `rm -rf` 的各行同样适用于 `rm -r`，规则 ID 相同，因此 `rm -r ../outside` 以 `rm.recursive-force-outside-cwd` 阻止。安全变体见[被允许的命令](/docs/zh-Hans/reference/allowed-commands)。

`find -delete` 也用同一套分类判定每个起点。解析符号链接后，所有起点都位于工作目录内时允许；起点为 `.`、起点是工作目录外的非临时路径、工作目录是主目录，或使用了 `-L`、`-f`、`-follow` 时，以 `find.delete` 阻止。`find -exec rm -rf`、`xargs rm -rf` 和 `parallel rm -rf` 规则要求同时有 `-r` 和 `-f`。没有 `-f` 时，引擎仍会对能看到的目标分类，因此 `xargs rm -r ../other` 和 `parallel rm -r ::: ../other` 以 `rm.recursive-force-outside-cwd` 阻止。加引号的 `parallel` 命令会经由 shell 执行，因此 `parallel 'rm -rf {}' ::: ../other` 与不加引号的形式一样被阻止。详见[分析引擎](/docs/zh-Hans/guides/analysis-engine#dynamic-target-analyzers-for-find-xargs-and-parallel)。

把破坏性命令藏在 shell 函数中不会绕过这些规则。函数正文会在调用处按调用者的 cwd 分析；调用也可通过引号（`'cleanup'`）、`time`/`!` 前缀和 `eval` 解析。bash 关键字写法 `function cleanup { ... }` 和 `function cleanup() { ... }` 同样会解读为定义，因此行为与 `cleanup() { ... }` 相同。只定义但从未调用的函数，其正文不会运行。调用解析和作用域见 [POSIX shell 函数](/docs/zh-Hans/guides/analysis-engine#posix-shell-functions)。

<Warning>
  `standard` 不会一律阻止动态 `rm -rf` 目标。`rm -rf "$target"` 在 `standard` 中允许，只有启用 `fail_closed` 能力后，也就是在 [`strict`](/docs/zh-Hans/configuration/modes#strict-mode) 或 [`paranoid`](/docs/zh-Hans/configuration/modes#paranoid-mode) 中，才会被阻止。`standard` 对对抗性或动态生成的命令文本只提供尽力保护；命令可能来自提示注入或其他不可信来源时，请使用 `strict` 或 `paranoid`。
</Warning>

未加引号的 glob 会先相对工作目录解析，然后才进入上面这套分类。cwd 位于主目录时，`rm -rf *` 和 `rm -rf ./*` 都归类为主目录目标，在所有级别都以 `rm.recursive-force-root-or-home` 阻止。加引号的 `rm -rf '*'` 不是 glob，它指的是文件名就叫 `*` 的那一个文件；在主目录下它同样会被阻止，规则是 `rm.recursive-force-home-cwd`，在其他位置则是普通的字面目标。

Git 元数据和动态目标都会在配置的允许路径之前分类，因此 `destructive_command_protection.allow_paths` 中的项绝不会放宽 Git 元数据保护，也绝不会应用于无法验证的目标。

<span id="device-and-disk-destruction" />

## 设备和磁盘破坏

CC Safety Net 在每个安全级别分析 `dd`、`mkfs`/`mke2fs`/`newfs` 这几类格式化命令、`shred`、`diskutil`、`wipefs`、`sgdisk`、`parted`，以及写向磁盘设备的输出重定向。这些规则不是灾难性规则，因此主破坏性命令开关和单条规则的 `off` 覆盖仍然适用。

| 命令模式 | 阻止原因 |
| - | - |
| `dd ... of=/dev/...` | 用 `dd` 直接写入设备路径可能破坏磁盘或分区。重新排序和带引号的操作数也涵盖在内（`dd of=/dev/nvme0n1 if=/dev/zero`、`of="/dev/sda"`） |
| `mkfs /dev/...`、任何 `mkfs.*` 变体、`mke2fs /dev/...`，以及作用于 `/dev/...` 的 `newfs`/`newfs_*` | 格式化设备会清除其中的全部内容（`mkfs.ext4 /dev/sda1`、`mkfs -t ext4 /dev/sdb`、`mke2fs -t ext4 /dev/sde`、`newfs_hfs /dev/disk2s1`） |
| 带擦除或分区子命令的 `diskutil` | `eraseDisk`、`eraseVolume`、`reformat`、`partitionDisk`、`zeroDisk`、`randomDisk` 和 `secureErase` 都会破坏目标。子命令匹配不区分大小写，目标写成什么形式都算，所以 `diskutil erasedisk APFS X disk4` 不带 `/dev/` 路径也会被阻止 |
| 作用于 `/dev/...` 目标的 `wipefs -a` 或 `wipefs -o` | 擦掉文件系统签名会让磁盘无法读取（`wipefs -a /dev/sdb`、`wipefs --all /dev/sdb`、`wipefs -af /dev/sdb`、`wipefs --offset=0x438 /dev/sdb`） |
| 作用于 `/dev/...` 目标并带写入类选项的 `sgdisk` | 写入分区表可能丢掉全部分区（`sgdisk -Z /dev/sdc`、`sgdisk --zap-all /dev/sdd`、`sgdisk -n 1:0:0 /dev/sdd`）。`-Zo`、`-pZ` 这类合并写法的短选项会被阻止，因为其中并非每个字母都是只读选项 |
| 作用于 `/dev/...` 目标并带 `mklabel`、`mktable`、`mkpart`、`rm` 或 `resizepart` 的 `parted` | 这些子命令都会重写分区表（`parted /dev/sde mklabel gpt`、`parted -s /dev/sdf rm 3`）。匹配不区分大小写 |
| 目标写成字面磁盘设备路径的输出重定向 | `echo x > /dev/sda` 与 `dd of=/dev/sda` 一样会覆盖磁盘。`>`、`>\|`、`>>`、`<>`、`>&` 都涵盖在内，所以 `printf x >> /dev/disk4` 和 `echo x &> /dev/sdb` 同样被阻止 |
| `shred <target>` | `shred` 会永久破坏交给它的任何目标，因此 `shred --help` 和 `shred --version` 也会被阻止 |

重定向规则按一份磁盘设备名清单匹配目标：`/dev/disk4` 和 `/dev/rdisk4`、`/dev/sda`、`/dev/hda`、`/dev/vda`、`/dev/xvda`、`/dev/nvme0n1`、`/dev/mmcblk0`、`/dev/md0`、`/dev/dm-0`、`/dev/loop0`、`/dev/mapper/` 下的任意路径，以及 `/dev/disk/` 下的任意路径。清单之外的 `/dev/` 路径（例如 `/dev/null` 或 `/dev/tty`）与普通重定向目标一样处理。

只有对设备的*写入*会触发 `dd` 规则。从设备读取到文件（`dd if=/dev/sda of=./backup.img`）是允许的，对文件镜像运行 `mkfs.ext4 disk.img` 同样允许。这些规则也适用于包装器和载体：`bash -c "dd if=/dev/zero of=/dev/sda"`、`sudo mkfs.ext4 /dev/sda`、`env dd of=/dev/sda if=/dev/zero`、`nice wipefs -a /dev/sdb`、`timeout 5 sgdisk -Z /dev/sdc`、`sh -c "echo x > /dev/sda"` 和 `eval "shred secret"` 都会被阻止。

`dd` 写入 `/dev/null`、`/dev/zero` 或 `/dev/full` 不会破坏任何东西，因此允许；读速测试 `dd if=big.bin of=/dev/null bs=1M` 可以照常运行。其他 `/dev/` 目标仍然被阻止，包括 `of=/dev/nullx` 这类只差一点的相近名字。该豁免只适用于解析后的 `dd` 规则。在无法解析的文本中，启发式扫描仍会匹配 `dd of=/dev/null` 并以 `raw-text.dangerous-command` 拒绝。

<span id="git-metadata" />

## Git 元数据

CC Safety Net 会解析两个目录最近的祖先 `.git` 条目：一个是执行目录，一个是决定项目规则配置的配置目录。这些条目、解析出的 Git 目录及其 `hooks` 子树都受保护。这两个目录通常是同一个；智能体在项目之外执行命令时才会不同，例如 Amp 的 `shell_command.dir` 或 OpenCode 的 `workdir`。这时两个 Git 控制面都受保护。即使你位于工作目录内，此保护也会生效。它在加载任何配置之前判定，因此不带安全级别，策略也无法将其关闭。

| 被阻止的形式 | 说明 |
| - | - |
| `rm .git`、`rm -rf .git`、`rm .*` | 直接删除控制面条目 |
| `rm -rf <resolved gitDir>`、`rm -rf <commonDir>` | 对于 **linked worktree** 或 submodule，`.git` 是标记文件；它所指向的解析后 `gitDir` 和 `commonDir` 也受保护 |
| `rm -rf <root>/.git/modules/<submodule>` | submodule 的 Git 目录 |
| `rm -rf <dirname(gitDir)>/*` | 覆盖受保护目录的 glob |
| 源或目标为受保护路径的 `mv` | 移动途径 |
| `> .git` 和其他写入重定向 | 重定向途径 |
| `Write` 或 patch 的目标是 `.git/hooks/new-hook` | 写入工具、patch 和未知工具三条途径；只读工具除外 |
| `find .git -delete` | 即使将 `find.delete-git-metadata` 设置为 `off`，也始终是灾难性操作 |
| 仓库根目录下的 `find . -exec rm {} +` | `-exec` 或 `-execdir` 把 `{}` 交给 `rm` 或 `rmdir` 时，算作删除 `find` 返回的路径；把 `{}` 交给运行 `rm` 或 `rmdir` 的 shell `-c` 正文时同样如此 |

字面量的 `-name` 或 `-iname` 过滤条件可以解除仅由起点触发的阻止。CC Safety Net 会在受保护的元数据中查找同时匹配第一个动作之前全部过滤条件的条目，一个都找不到就放行这次删除。因此在仓库根目录下 `find . -name '*.pyc' -exec rm {} +` 可以通过；而 `.git` 中存在同名条目，所以 `find . -name '*.sample' -exec rm {} +` 和 `find . -name HEAD -exec rm {} +` 仍被阻止。`find . -name '*.pyc' -delete` 也被阻止，但规则是 `find.delete` 而不是 `find.delete-git-metadata`。这条例外范围很窄，扫描也是 fail-closed：元数据根是符号链接、目录不可读，或条目数超过 50,000 时都保持阻止。保持阻止的全部形式见[分析引擎](/docs/zh-Hans/guides/analysis-engine)。

符号链接的 `.git` 和 hooks 会同时按字面路径和规范路径保护，指向缺失 Git 目录的 `.git` 标记仍会阻止。POSIX shell 中末尾的 `*` 不匹配以点开头的条目，因此仓库根目录中的 `rm -rf ./*` 不涵盖 `.git`，但 `rm -rf .git/worktrees/*` 和 PowerShell 通配符会涵盖。

<Note>边界：此防护覆盖执行目录和配置目录各自最近的祖先 Git 控制面，不包括这两个目录下的其他嵌套仓库，也不包括解析集合之外的 Git 内部路径。</Note>

## 受保护的 `policy.json`

修改或删除受保护的策略文件始终被阻止。受保护的文件有两个：用户策略文件，默认路径是 `~/.cc-safety-net/policy.json`，设置 `CC_SAFETY_NET_HOME` 后为 `$CC_SAFETY_NET_HOME/policy.json`；以及项目策略文件 `.cc-safety-net/policy.json`，它分别从执行目录和配置目录解析。这项检查不采纳任何配置值，因此它所保护的策略永远无法禁用它，其拒绝也不带安全级别和配置状态。原因字符串固定为：

```text theme={"dark"}
This path contains the protected policy config and you must not modify or delete it.
```

匹配覆盖这两个文件，以及一组随范围而不同的目录。对于用户策略文件，覆盖它所在的目录以及该目录的各级祖先；对于项目策略文件，只覆盖它自身的 `.cc-safety-net` 目录，不再向上，因此项目根目录下的 `rm -rf .` 返回的仍是破坏性命令规则本来的原因，而不是这一条。涵盖以下形式：

* 直接的写入、编辑和 patch 目标。
* 精确的 shell 操作数和写入重定向。
* 受支持的环境变量、相对路径和现有符号链接别名。
* 对受保护目录的递归 `rm`。
* 以受保护的文件或目录为源的 `mv`。

只读的检查命令在允许列表中，可以通过：`[`、`cat`、`file`、`grep`、`head`、`jq`、`less`、`ls`、`more`、`rg`、`sed`、`stat`、`tail`、`test`、`wc`。

<Warning>这是范围很小的精确路径保护，不是命令模拟器。它会跟踪只做赋值的 shell 变量和显式的 `cd`，但不会展开 glob 或花括号，也不会推断计算得出的解释器路径、检查解释器正文、推断归档成员、模拟 `find` 操作或推断传输后的最终文件名。`cd` 的跟踪遵循 shell 作用域。子 shell、命令替换和进程替换中的 `cd` 只在该 shell 结束前有效。花括号组或被调用的函数体中的 `cd` 则影响命令的其余部分。`cd -` 会回到上一次 `cd` 离开的目录。`rule.json`、rulebook、同级文件和对策略目录的检查都不在此防护范围内。只有 `policy.json` 具备防篡改能力，两个范围都受保护。</Warning>

### 智能体运行 `cc-safety-net policy apply`

`policy apply` 会重写这道防护所保护的文件，因此同一阶段会在加载配置之前拒绝它，intent 为 `hard_stop`。只有你本人可以应用提案，没有任何选项能解除这一点。原因字符串固定为：

```text theme={"dark"}
Only the user may apply a policy proposal, because it rewrites the configuration CC Safety Net enforces. Ask them to run `cc-safety-net policy apply <file>` themselves in a terminal; you can run `cc-safety-net policy check <file>` to show them what it would change.
```

识别逻辑有意放宽匹配，覆盖直接调用 `cc-safety-net` 或 `ccsn`，以及 `npx`、`bunx`、`pnpx`、`pnpm dlx`、`yarn dlx`、`npm exec`、`pnpm exec`、`yarn exec`、`cc-safety-net@latest` 这类带版本的写法，还有用 `bun` 或 `node` 运行 `src/cli/cc-safety-net.ts` 或 `dist/bin/cc-safety-net.js`。把运行器选项放在目标之前也无法绕开，`-g` 和 `--global` 出现在任何位置都会被跳过。`policy check` 和其他所有子命令仍然允许。

## PowerShell `Remove-Item`

PowerShell 支持是一个保守子集：`Remove-Item` 及其别名、文件 cmdlet `Get-Content`、`Set-Content`、`Add-Content`、`Copy-Item`、`Move-Item` 及别名 `gc`、`cat`、`type`、`cp`、`mv`，以及现有的跨 shell 规则，而不是通用 PowerShell 解析器。下面的删除规则在 `powershell` 和 `auto` shell 模式中生效；`posix` 模式有意不应用这些规则，但仍保留 `git.reset-hard` 和 `rm.recursive-force-root-or-home` 等跨 shell 规则。例外是嵌套在 `powershell -Command` 或 `pwsh -Command` 中的脚本，它在任何 shell 模式下都按 PowerShell 分析，见[嵌套的 PowerShell 脚本](#nested-powershell-scripts)。敏感路径保护会解析 `$HOME`、`$env:USERPROFILE`、`$env:HOME` 或 `~` 前缀，加上用任一路径分隔符连接的字面量后缀，因此 `Get-Content $HOME\.ssh\id_rsa` 会被阻止；以其他方式拼装的路径，例如字符串拼接、子表达式或 `Join-Path`，不会被求值。

| 命令模式 | 阻止原因 | 级别 |
| - | - | - |
| `Remove-Item C:\`、`~`、`$HOME`、`${HOME}`、`$env:USERPROFILE`、`$env:HOME` | 删除根目录或主目录，包括末尾 `\`、`\*` 和 `/` 变体 | 全部，灾难性 |
| `Remove-Item .git -Recurse -Force` | 受保护的 Git 元数据；PowerShell 通配符*会*匹配以点开头的条目 | 全部，灾难性 |
| `Remove-Item . -Recurse -Force` | 当前目录本身，包括出现在 `--` 参数结束标记之后的形式 | 全部 |
| `Remove-Item ../other -Recurse -Force` | cwd 以外的路径，包括 `..\other`、`../{other}`、含有外部元素的逗号分隔数组，以及 Windows UNC/namespace 目标 | 全部 |
| `Set-Location $HOME; Remove-Item ./build -Recurse -Force` | cwd 为主目录时递归删除 | 全部 |
| `Remove-Item $target -Recurse -Force` | 无法验证的动态目标 | **Strict**、paranoid |
| `Get-ChildItem . -Recurse \| Remove-Item -Force` | 无法验证的管道输入 | **Strict**、paranoid |
| `Remove-Item -Recurse -Force -Path` | 缺少 `-Path` 的值，会分类为动态目标 | **Strict**、paranoid |
| `Remove-Item @params -Recurse -Force` | splat 传入的参数会分类为动态目标 | **Strict**、paranoid |
| `Remove-Item ./cache -Recurse -Force` | cwd 内的非临时路径 | **Paranoid**（[`CC_SAFETY_NET_PARANOID_RM=1`](/docs/zh-Hans/configuration/modes#paranoid-rm-check)） |

别名和缩写参数会被解析，因此 `ri . -r -fo` 同样阻止。调用运算符形式（`& Remove-Item ...`、`& { ... }`、`. { ... }`）、带字面量字符串的 `iex` / `Invoke-Expression` 和 `$(...)` 子表达式都会被分析。行注释（`#`）和块注释（`<# ... #>`，包括嵌套）会被忽略，但其后的真实命令仍会阻止；格式错误或超过深度限制的块注释和子表达式则按 fail closed 处理。

<Note>
  `-WhatIf` 会解除阻止，因为 PowerShell 并不会真正删除任何内容：`-WhatIf`、`-WhatIf:$true` 和 `-wi` 缩写都会允许原本被阻止的 `Remove-Item . -Recurse -Force`；显式 `-WhatIf:$false` 则重新阻止。上面仅 strict 的那些行有一个例外：`Remove-Item $HOME -Recurse -Force` 在 `standard` 中也会阻止，因为它分类为根目录/主目录目标，而不是动态目标。

  PowerShell 的变量名不区分大小写，上面根目录/主目录那一行中的主目录变量也是如此：`Remove-Item -Recurse -Force $home`、`$Home\*` 和 `${home}` 都分类为根目录/主目录目标，而不是动态目标。脚本前面的赋值不会改变这一点：PowerShell 的 `$HOME` 是只读的，因此 `$home = 'D:\temp'; Remove-Item -Recurse -Force $home` 仍然会删除主目录，规则也仍然阻止它。
</Note>

<span id="sensitive-paths" />

## 敏感路径

敏感路径保护是独立的防护阶段，在命令分析前运行。它涵盖受支持的 **command**、**path**、**search**（grep/glob）和 **patch** 形式，也会检查未知工具，但绝不会将未知工具的任意文本视为 shell 命令。

内置敏感集合不只阻止写入，也阻止读取：`cat .env`、`env cat .env`、`sudo command cat .env`、`strings id_rsa`、`xxd .env`、`base64 .env`、`dd if=.env`、`cat ~/.ssh/id_rsa`、`bash -c "cat .env"`、`pwsh -c 'Get-Content ~/.ssh/config'` 和 `node -e 'require("child_process").execSync("cat .env")'` 都会被阻止。

command 形式的提取是有界且结构化的：触及结构性 shell 语法限制时按 fail closed 处理，解析无效时抛出错误。配置的拒绝路径在内置规则之前匹配，且不受 `standard` 模式下任何放宽的影响。

完整目录（每个规则 ID 和系列、受保护路径、两个 Coding CLI 层级、匹配顺序和豁免）见[机密保护参考](/docs/zh-Hans/reference/secret-protection)。可用 `secret_protection.enabled: false` 禁用整个功能，也可通过 `secret_protection.overrides` 逐条开启或关闭规则，详情见[策略](/docs/zh-Hans/configuration/policy#secret-protection)。

<span id="shell-wrappers-and-interpreter-one-liners" />

## Shell 包装器和解释器单行命令

包在 `bash -c` 或 `sh -c` 等 shell 解释器中的命令同样会被阻止。CC Safety Net 最多递归分析 10 层嵌套包装器；超过该深度时会拒绝，而不会放行。

| 示例 | 结果 |
| - | - |
| `bash -c 'git reset --hard'` | 阻止 |
| `sh -lc 'rm -rf /'` | 阻止 |

分析 shell 的 `-c` 正文时会先绑定字面量位置参数，因此 `sh -c 'rm -rf "$1"' _ /` 以 `rm.recursive-force-root-or-home` 阻止，`sh -c 'rm -rf "$1"/.git' _ .` 以 `rm.git-metadata` 阻止。只要有一个参数不是字面量，正文就保持原样。绑定规则见[分析引擎](/docs/zh-Hans/guides/analysis-engine)。

`sudo`、`env`、`command`、`builtin` 这层包装按 basename 匹配，因此写成完整路径和写成命令名一样会被剥离。`/usr/bin/env git reset --hard`、`sudo /usr/bin/env git reset --hard` 和 `/usr/bin/env rm -rf ../outside` 都会被阻止。`/usr/bin/env -- git status` 仍然允许，因为 `git status` 本身是允许的。

你自己的[自定义规则](/docs/zh-Hans/configuration/custom-rules)同样在任意嵌套深度生效，因此针对 `rm` 或 `git` 的规则在 `bash -c`、`sh -c`、`eval` 和被调用的函数正文内部也会命中。

默认情况下，会检测并阻止解释器单行命令中嵌入的破坏性代码。CC Safety Net 提取传给解释器 `-c` 或 `-e` 标志的代码，并扫描嵌入的破坏性操作，因此智能体无法把 `os.system("rm -rf /")` 包在 Python 或 Node 调用里来绕过 hook。

| 示例 | 结果 |
| - | - |
| `python -c 'import os; os.system("rm -rf /")'` | 默认阻止（检测到嵌入的 `rm -rf`） |
| `node -e 'require("child_process").exec("git reset --hard")'` | 默认阻止 |

分析的解释器是 `python`、`python2`、`python3`、`node`、`ruby` 和 `perl`。触发阻止的是嵌入的破坏性命令，默认情况下不会只因单行命令形式而阻止。

<span id="command-prompt-bodies" />

### 命令提示符正文

CC Safety Net 在任何 shell 模式下都会读取 `cmd /c` 和 `cmd /k` 的正文。命令头可以是 `cmd` 或 `cmd.exe`，不区分大小写；Git Bash 中的 `//c` 写法同样算在内。这种读取是有界的文本检查，而不是 cmd 解析器。它先去掉一对外层引号，再按 `&` 和 `|` 拆分正文。在拆出的每一段中，它查找带 `/s` 开关的 `rmdir`、`rd`、`del` 或 `erase`；开关合写成 `/s/q`、命令前带 `@`，或位于 `if exist` 条件之后，也都算在内。找到的删除目标以外层工作目录为基准，由 `cmd.recursive-delete-*` 规则判定，路径最后一段的 `*.*` 视为 `*`。其余各段都作为外层 shell 的命令分析，因此 `cmd /c git reset --hard` 会以 `git.reset-hard` 阻止。

| 示例 | 结果 |
| - | - |
| `cmd /c 'rmdir /s /q C:\'`、`cmd /c "del /s /q C:\*.*"` | 以 `cmd.recursive-delete-root-or-home` 阻止 |
| `cmd /c 'if exist C:\ rmdir /s /q C:\'` | 以 `cmd.recursive-delete-root-or-home` 阻止 |
| `cmd /c '@rd /s /q ..\outside'` | 以 `cmd.recursive-delete-outside-cwd` 阻止 |
| `cmd /c "rmdir /s /q \"build output\""` | 以 `cmd.recursive-delete-escaped-quote` 阻止 |
| `cmd /c 'cd /d C:\Users && rmdir /s /q build'` | 作为无法验证的 shell 执行来源拒绝 |
| `cmd /c 'rd/s/q C:\'`、`cmd /c '(rmdir /s /q C:\)'` | 作为无法验证的 shell 执行来源拒绝 |
| `cmd /c 'if exist node_modules rmdir /s /q node_modules'` | 位于工作区内，允许 |
| `cmd /c 'del /q build.log'` | 不是递归删除，允许 |

**转义引号。** cmd 不把 `\"` 当作转义，所以用 `\"` 写出的目标可能被拆开，其中一段指向驱动器根目录。因此，含有递归删除写法的正文只要满足下列任一条件，就会以 `cmd.recursive-delete-escaped-quote` 阻止，与安全级别和权限模式无关：

* 正文含有 `\"`。在 PowerShell 下，命令中任一词里的 `\"` 也算。
* shell 模式为 `posix`，且正文含有任意 `"`。Git Bash 启动 cmd 时会把每个 `"` 重新转义为 `\"`。
* 删除目标是 `\\?\` 或 `\\.\` 命名空间路径。

阻止消息会要求把每个路径作为单独的带引号参数传入，或改用 `Remove-Item -LiteralPath`。

**无法验证的正文。** 如果没有目标规则先行阻止，含有递归删除写法的正文只要包含下列任一内容，就会以原因 `shell execution source cannot be verified safely.` 拒绝：

* `^`、`%` 或 `!`，cmd 用它们做转义和变量展开。
* 同时出现 `"` 与 `&` 或 `|`。拆分时不考虑引号，引号内的 `&` 会使部分目标被漏检。
* 任意位置的 `cd`、`chdir`、`pushd` 或 `popd` 词，包括 `cd..`、`cd\` 这类连写形式。
* 读取器无法解析的删除，例如 `rd/s/q`、带引号的命令名或括号分组。

在 standard 模式下，Claude Code 的 hook 可能把这一拒绝转为权限提示，条件见本页开头的说明。目标规则的拒绝优先，因此 `cmd /c 'rmdir /s /q C:\ & echo %DATE%'` 仍以 `cmd.recursive-delete-root-or-home` 阻止。

读取器按文本匹配，因此也可能阻止实际上不删除任何内容的命令。`cmd /c 'echo rmdir /s /q C:\'` 会被当作删除驱动器根目录而阻止。读取器不覆盖的情况见[已知限制](/docs/zh-Hans/guides/known-limitations#windows-command-prompt-and-nested-powershell)。

<span id="nested-powershell-scripts" />

### 嵌套的 PowerShell 脚本

CC Safety Net 在任何 shell 模式下都会读取 `powershell` 或 `pwsh` 命令（不区分大小写，带不带 `.exe` 均可），但只读取一种形态：

1. 零个或多个 `-NoProfile`（`-nop`）、`-NonInteractive`（`-noni`）、`-NoLogo`，以及带一个值的 `-ExecutionPolicy`（`-ep`）。
2. 接着是 `-Command` 或 `-c`。
3. 之后是全部为字面量的脚本词，不含外层 shell 会展开的内容，也不含 `--%`。

该脚本以外层工作目录为基准按 PowerShell 分析，`posix` 模式下也一样。`pwsh -NoProfile -c 'Remove-Item -Recurse -Force C:\'` 会以 `powershell.remove-item-recursive-force-root-or-home` 阻止。

其他形式一律不读取。如果其文本同时包含删除命令（`Remove-Item`、`ri`、`rm`、`rmdir`、`rd`、`del` 或 `erase`）和递归开关，就会以 `powershell.nested-recursive-delete-unread` 阻止，与安全级别和权限模式无关。递归开关指 `-r` 或比它更长的 `-Recurse`、`--recursive` 前缀，包含 `r` 的 `rm` 短标志组合（如 `-fr`），以及 `/s`。不读取的形式包括：`-WorkingDirectory`、`-NoExit` 等其他参数；以 `/` 或 Unicode 破折号开头的参数；`-File`；作为位置参数传入的脚本；由外层 shell 展开的词。不含递归删除的未读取形式不会被这条规则阻止，但其中的词仍适用其他规则，因此 `pwsh -NoExit -c git reset --hard` 会以 `git.reset-hard` 阻止。

在已读取的脚本之外，任何指向 `-EncodedCommand` 的词都会作为无法验证的 shell 执行来源被拒绝，包括 `-e`、`-enc` 等缩写、别名 `-ec`，以及以 `/` 或 Unicode 破折号开头的写法。用 `-File` 运行的脚本和从标准输入读取的脚本都不会被读取。

| 示例 | 结果 |
| - | - |
| `pwsh -NoProfile -c 'Remove-Item -Recurse -Force C:\'` | 以 `powershell.remove-item-recursive-force-root-or-home` 阻止 |
| `powershell -NoProfile -ExecutionPolicy Bypass -Command 'Remove-Item -Recurse -Force C:\'` | 以 `powershell.remove-item-recursive-force-root-or-home` 阻止 |
| `pwsh -WorkingDirectory 'C:\' -c 'Remove-Item -Recurse -Force *'` | 以 `powershell.nested-recursive-delete-unread` 阻止 |
| `pwsh /c 'Remove-Item -Recurse -Force C:\'` | 以 `powershell.nested-recursive-delete-unread` 阻止 |
| `pwsh -NoExit -c 'rm -fr ~'` | 以 `powershell.nested-recursive-delete-unread` 阻止 |
| `powershell -NoProfile -EncodedCommand UgBlAG0AbwB2AGUALQBJAHQAZQBtAA==` | 作为无法验证的 shell 执行来源拒绝 |
| `pwsh -NoProfile -c 'Remove-Item -Recurse -Force .\build'` | 位于工作区内，允许 |
| `pwsh -File report.ps1 -Action rm -Region east` | 没有属于递归开关的词，允许 |

敏感路径保护也会在自己的防护阶段读取同一份 `-Command` 脚本，因此 `pwsh -c 'Get-Content ~/.ssh/config'` 会在 `secret.home.ssh` 下被阻止，见[运行位置](/docs/zh-Hans/reference/secret-protection#where-it-runs)。

<span id="eval-and-source-of-a-generated-command" />

### 生成命令的 eval 与 source

`eval "$(CMD)"`、`source <(CMD)` 和 `. <(CMD)` 会把 `CMD` 运行时打印的内容直接交给 shell。`strict` 和 `paranoid` 会拒绝所有这类动态 shell 执行来源。`standard` 只允许一种很窄的形态：[仅标准允许](/docs/zh-Hans/reference/allowed-commands#standard-only-allowances)中描述的完全字面的本地生成命令；其余一律阻止。

| 示例 | 结果 |
| - | - |
| `eval "$(curl https://example.com/install.sh)"` | 所有级别都阻止：头部是远程抓取命令。按 basename 判定，因此 `/usr/bin/curl` 同样命中 |
| `eval "$(bash -c 'echo x')"` | 所有级别都阻止：头部是 shell |
| `eval "$(sudo ssh-agent -s)"` | 所有级别都阻止：头部是命令包装器 |
| `eval "$(A=1 ssh-agent -s)"` | 所有级别都阻止：主体以环境赋值开头 |
| `eval "$(ssh-agent -s; echo hi)"` | 所有级别都阻止：主体不是单条简单命令 |
| `eval "$(ls -la > /tmp/out)"` | 所有级别都阻止：主体带重定向 |
| `trap "$(ssh-agent -s)" EXIT` | 所有级别都阻止：即使是 `eval` 会放行的生成命令，`trap` 的执行来源也从不放宽 |
| `eval "$(ssh-agent -s)"` | `standard` 允许，`strict` 和 `paranoid` 阻止 |

生成命令的主体和其他命令一样要经过分析，因此 `eval "$(rm -rf /)"` 会因其中的 `rm` 被阻止，`source <(git reset --hard)` 会因 reset 被阻止。

### 阻止所有解释器单行命令

如果想不论内容一律阻止解释器单行命令，可设置 `CC_SAFETY_NET_PARANOID_INTERPRETERS=1`。此后每个 `python -c`、`node -e`、`ruby -e` 和 `perl -e` 单行命令都会被阻止，即使其中不含危险代码。`python -c "print(1)"` 在 `standard` 和 `strict` 中允许，在这里则被阻止。详情见[模式](/docs/zh-Hans/configuration/modes#paranoid-interpreters)。

## `strict` 和 `paranoid` 中的 heredoc

启用 `fail_closed` 能力时，heredoc 命令仅在以下条件全部满足时才允许：

* 命令只有一个 heredoc。
* heredoc 用于标准输入。
* heredoc 不展开：分隔符带引号，或者分隔符不带引号且正文中不含 `$`、反引号或反斜杠。
* 命令没有其他输入重定向。
* 消费者是字面量 `cat`、`tee`、`git apply`、`git commit`、`gh pr create` 或 `gh issue create`。

`fail_closed` 在 [`strict`](/docs/zh-Hans/configuration/modes#strict-mode) 和 [`paranoid`](/docs/zh-Hans/configuration/modes#paranoid-mode) 中启用。

分隔符不带引号且正文含有 `$`、反引号或反斜杠时，会使用以下拒绝消息：

```text theme={"dark"}
Unquoted heredoc input is not supported safely. Quote the delimiter or ask the user to verify.
```

其他不受支持的形式或消费者会使用以下消息：

```text theme={"dark"}
This heredoc form or stdin consumer is not supported safely. Use a quoted heredoc with a supported consumer (cat, tee, git apply, git commit, gh pr create, gh issue create), or ask the user to verify.
```

完整的 heredoc 契约（包括 `standard` 模式处理 heredoc 内容的方式）见[分析引擎](/docs/zh-Hans/guides/analysis-engine)。

## 无法解析的命令文本

shell 解析器无法对命令分词时（例如引号未闭合），行为取决于安全级别。

| 输入 | `standard` | `strict` 和 `paranoid` |
| - | - | - |
| `echo 'unterminated` | 允许，看似安全的无法解析文本可以通过 | 阻止，理由是“无法安全分析” |
| `git reset --hard 'unterminated` | 被启发式扫描**阻止** | 阻止 |
| `dd if=/dev/zero of=/dev/sda 'oops` | 被启发式扫描**阻止** | 阻止 |
| `echo ${ date; }` | **阻止**，解析器直接拒绝该构造 | 阻止 |

被 POSIX 解析器直接拒绝的构造会让解析变为无效。每个级别都会拒绝无效的解析，启发式扫描也不会运行，因此符合这些写法的无害命令同样会被拒绝。被拒绝的范围包括 `${ list; }` 函数替换、可能藏着替换的 `${…}` 中的引号或反斜杠、没有闭合花括号的 `${…}`，以及反引号主体中的反斜杠转义。确切的写法以及仍被允许的例子见[解析器拒绝的 shell 语法](/docs/zh-Hans/guides/analysis-engine#shell-syntax-the-parser-refuses)。

启发式扫描会查找 `rm -rf`、`git reset --hard`、`git reset --merge`、`git clean -f`、`git checkout --force`、`git checkout --`、`git push --force`、`git push --delete`、`git branch -D`、`git tag -d`、`git stash drop`、`git stash clear`、不带 `--staged` 的 `git restore`、`find -delete`、`dd of=/dev/`、`mkfs /dev/` 和 `shred <arg>`。匹配会以规则 `raw-text.dangerous-command` 拒绝。文本以 `echo ` 或 `rg ` 开头时会跳过 `find`、`dd`、`mkfs` 和 `shred` 模式，因此把这些字符串引用进 echo 或 ripgrep 调用不会触发扫描。

扫描还会查找把下载内容经管道交给 shell 的写法，也就是远程安装的一行命令。远程抓取命令（`curl`、`wget`、`fetch`、`aria2c`、`http`、`https`、`xh`、`xhs`、`nc`、`ncat`、`netcat`）经管道接到 `sh`、`bash`、`zsh`、`dash` 或 `ksh` 时，会以 `raw-text.dangerous-command` 拒绝。shell 名带路径前缀（`| /bin/sh`），或中间夹着 `sudo`、`env`、`command`、`builtin` 包装器（`| sudo sh`、`| env VAR=1 sh`），结果都一样。在命令内部或管道前后加反斜杠续行同样藏不住。文本以 `echo ` 或 `rg ` 开头时也会跳过这条模式，因此用 echo 打印的安装行仍是数据。同一条管道能被解析为真正的命令时，改为按结构拒绝，理由是 "shell execution source cannot be verified safely"。文本扫描负责在 heredoc 正文等解析器不会深入之处拦下它。

<Warning>资源耗尽限制独立于级别系统，并在包括 `standard` 在内的**每个**模式中拒绝：超出声明命令递归限制或结构命令校验限制一律拒绝，绝不放行。</Warning>

<Note>`explain` 命令可以精确跟踪 CC Safety Net 阻止或允许某条具体命令的原因。它的标志见 [CLI 命令](/docs/zh-Hans/reference/cli-commands#explain)，JSON schema 见 [Explain 跟踪](/docs/zh-Hans/reference/explain-trace)。</Note>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.