各レベルが追加するブロック
一部の重要な保護は、選択した安全レベルに依存しません。root とホームディレクトリの再帰削除、Git メタデータの保護、正規の
policy.json の保護は、破壊的コマンドのマスタースイッチも、ルール単位の off の override も無視します。Git コマンド
次の Git 操作は、未コミットの作業を破棄する、復旧に必要な履歴を壊す、共有された状態を書き換える、といった理由でブロックします。
強制フラグと、ブランチの作成・リセットのフラグを両方使ってブランチを変更する Git コマンド(
git checkout -Bf、git switch -Cf --discard-changes など)は、ブランチの強制リセットとみなしてブロックします。
Git の SSH 関連の環境変数による上書き
Git は、ネットワーク操作中に任意のプログラムを実行するためにGIT_SSH_COMMAND、GIT_SSH、GIT_SSH_VARIANT を使えます。CC Safety Net は、これらの上書きをネットワーク系のサブコマンドと組み合わせた場合にブロックします。任意のコマンドを実行できてしまうためです。
ファイルシステムコマンド
rm -rf の対象は、次の順に照合し、最初に一致した分類を採用します。
- 未対応の Windows UNC またはデバイス名前空間の対象
- root またはホーム
- 保護対象の Git メタデータ
- 一時パス
- 動的な対象
- 設定済みの allow path
- カレントディレクトリがホームの場合
- カレントディレクトリそのもの
- カレントディレクトリ配下
- カレントディレクトリ外
rm が有効でないかぎり許可します。安全な書き方は、許可されるコマンドを参照してください。
破壊的コマンドをシェル関数に隠しても結果は変わりません。関数を呼び出した位置で、呼び出し側のカレントディレクトリを使って本体を解析します。呼び出しは、引用符付きの形式('cleanup')、time/! を前置した形式、eval 経由でも解決します。bash のキーワード形式 function cleanup { ... } と function cleanup() { ... } も定義として解釈するため、cleanup() { ... } と同じ扱いになります。呼び出されない定義は何も実行しません。呼び出しの解決とスコープについては、POSIX シェル関数を参照してください。
引用符のない glob は、この照合を行う前にカレントディレクトリを基準に展開されます。カレントディレクトリがホームディレクトリの場合、rm -rf * と rm -rf ./* はどちらもホームを対象とする分類になり、すべてのレベルで rm.recursive-force-root-or-home としてブロックされます。一方、引用符付きの rm -rf '*' は glob ではなく、* という名前のファイル 1 つを指します。ホームではこれも rm.recursive-force-home-cwd によってブロックされ、それ以外の場所では通常のリテラル対象として扱われます。
Git メタデータと動的な対象は、設定済みの allow path より先に分類されます。そのため、destructive_command_protection.allow_paths の項目によって Git メタデータの保護が緩和されることはなく、検証できない対象にも適用されません。
デバイスとディスクの破壊
CC Safety Net は、どの安全レベルでもdd、mkfs、shred を解析します。これらは致命的な操作のルールではないため、破壊的コマンドのマスタースイッチやルール単位の off の override を適用できます。
dd のルールが働くのは、デバイスへの書き込みだけです。デバイスからファイルへの読み出し(dd if=/dev/sda of=./backup.img)や、ファイルとして存在するイメージに対する mkfs.ext4 disk.img は許可します。この 3 つのルールは、ラッパーや他のコマンド経由の実行にも適用されます。bash -c "dd if=/dev/zero of=/dev/sda"、sudo mkfs.ext4 /dev/sda、env dd of=/dev/sda if=/dev/zero、eval "shred secret" は、いずれもブロックされます。
Git メタデータ
最も近い祖先.git エントリを解決する対象は、実行ディレクトリと、プロジェクトのルール設定を決める設定ディレクトリの 2 つです。それぞれについて、そのエントリ、解決後の Git ディレクトリ、hooks サブツリーを保護します。両者は通常同じディレクトリですが、エージェントがプロジェクトの外でコマンドを実行する場合(Amp の shell_command.dir や OpenCode の workdir)は異なります。そのときは両方の Git 管理領域を保護します。この確認は設定読み込み前に実行するため、無効化できません。
シンボリックリンクになっている
.git ディレクトリと hooks ディレクトリは、表記上のパスと正規化後のパスのどちらでも保護します。存在しない Git ディレクトリを指す .git の参照ファイルもブロックします。POSIX シェルの末尾の * glob はドットで始まるエントリに一致しないため、リポジトリルートでの rm -rf ./* は .git を対象にしません。ただし、rm -rf .git/worktrees/* と PowerShell のワイルドカードは対象になります。
範囲の目安:このガードが対象とするのは、実行ディレクトリと設定ディレクトリから見て、それぞれ最も近い上位の Git 管理領域です。どちらかのディレクトリの下に入れ子になった別のリポジトリや、解決された対象に含まれない Git 内部のパスは対象外です。
保護対象の policy.json
保護対象のポリシーファイルを変更・削除する操作は、常にブロックします。保護対象は 2 つです。1 つはユーザーポリシーのファイル(~/.cc-safety-net/policy.json、または変数が設定されている場合は $CC_SAFETY_NET_HOME/policy.json)、もう 1 つは実行ディレクトリと設定ディレクトリのそれぞれから解決したプロジェクトポリシーのファイル(.cc-safety-net/policy.json)です。このチェックは設定を読み込む前に実行されるため、保護対象のポリシー側からは無効にできません。この拒否には安全レベルも設定の状態も付きません。理由の文字列は次のとおりです。
.cc-safety-net ディレクトリまでで、それより上は対象外です。そのため、プロジェクトルートでの rm -rf . は、このガードではなく破壊的コマンドのルール本来の理由を返します。形式は次のとおりです。
- 直接の書き込み、編集、patch の対象。
- シェルのオペランドとしての完全一致、および書き込みリダイレクト。
- 対応する環境変数による指定、相対パス、既存のシンボリックリンク経由の別名。
- 保護対象のディレクトリに対する再帰的な
rm。 - 保護対象のファイルまたはディレクトリを移動元とする
mv。
[, cat, file, grep, head, jq, less, ls, more, rg, sed, stat, tail, test, wc は許可します。
エージェントによる cc-safety-net policy apply
policy apply はこのガードが保護するファイルを書き換えるため、同じ段階が設定を読み込む前に、hard_stop の intent で拒否します。提案を適用できるのはユーザーだけで、これを解除するフラグはありません。理由の文字列は次のとおりです。
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 とその別名、ファイル系コマンドレットの Get-Content、Set-Content、Add-Content、Copy-Item、Move-Item と別名 gc、cat、type、cp、mv、および既存のシェル共通ルールです。汎用の PowerShell パーサーではありません。以下の削除ルールは powershell と auto のシェルモードで適用されます。posix モードでは意図的に適用しませんが、git.reset-hard や rm.recursive-force-root-or-home のようなシェル共通のルールは引き続き適用されます。機密パスの保護は、$HOME、$env:USERPROFILE、$env:HOME、~ のいずれかの先頭部分に、どちらのパス区切りでリテラルの末尾部分を連結した形式を解決します。そのため Get-Content $HOME\.ssh\id_rsa はブロックされます。それ以外の方法で組み立てたパス、たとえば文字列連結、部分式、Join-Path を使ったものは評価しません。
別名と省略されたパラメーターも解決するため、
ri . -r -fo もブロックします。呼び出し演算子を使った形式(& Remove-Item ...、& { ... }、. { ... })、リテラル文字列を渡す iex / Invoke-Expression、$(...) の部分式も解析します。行コメント(#)とブロックコメント(入れ子を含む <# ... #>)は無視しますが、その後ろにある実際のコマンドは引き続きブロックします。形式が不正なブロックコメントや部分式、深さの上限を超えたものは fail closed になります。
-WhatIf を付けた場合、PowerShell は実際には何も削除しないため、ブロックの対象から外れます。-WhatIf、-WhatIf:$true、省略形の -wi を付ければ、通常はブロックされる Remove-Item . -Recurse -Force も許可されます。明示的に -WhatIf:$false を指定した場合は、再びブロックされます。なお、上の strict 限定の行には例外が 1 つあります。Remove-Item $HOME -Recurse -Force は動的な対象ではなく root/ホームの対象として分類されるため、standard でもブロックされます。機密パス
機密パスの保護は、コマンド解析より前に実行される別のガードステージです。対応する コマンド、パス、検索(grep/glob)、patch の各形式に適用され、未知のツールについても、その任意のテキストをシェルコマンドとして扱うことなく検査します。 書き込みだけでなく、組み込みの機密ファイル群の読み取りもブロックします。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"、node -e 'require("child_process").execSync("cat .env")' は、いずれもブロックされます。
コマンドの形の抽出は、上限を設けたうえで構造的に行います。シェル構文の構造的な上限を超えた場合は fail closed になり、パースが不正な場合は例外になります。設定済みの deny path は組み込みルールより先に照合され、standard モードの緩和によって外れることはありません。
すべてのルール ID とその分類、保護対象のパス、Coding CLI の 2 つの階層、照合の順序、除外条件は、シークレット保護リファレンスを参照してください。機能全体は secret_protection.enabled: false で無効にでき、個別のルールは secret_protection.overrides で有効・無効を切り替えられます。ポリシーを参照してください。
シェルラッパーとインタープリターの 1 行コード
bash -c や sh -c のようなシェルのインタープリターで包まれたコマンドもブロックします。入れ子になったラッパーは最大 10 階層まで再帰的に解析し、その深さを超えるコマンドは通さずに拒否します。
インタープリターの 1 行コードに埋め込まれた破壊的なコードは、既定で検出してブロックします。CC Safety Net は、インタープリターの
-c や -e フラグに渡されたコードを取り出し、埋め込まれた破壊的操作をスキャンします。そのため、エージェントが Python や Node の呼び出しに os.system("rm -rf /") を紛れ込ませても、hook はすり抜けられません。
解析対象のインタープリターは、
python、python2、python3、node、ruby、perl です。ブロックの原因になるのは埋め込まれた破壊的コマンドであり、1 行コードという形式そのものは既定では許可されます。
生成されたコマンドの eval と source
eval "$(CMD)"、source <(CMD)、. <(CMD) は、CMD が実行時に出力した内容をそのままシェルに渡します。strict と paranoid では、この種の動的なシェルの実行元をすべて拒否します。standard が許可するのは、完全にリテラルなローカルの生成コマンド 1 つという狭い形だけで、それ以外はブロックします。この形についてはstandard のみの許可を参照してください。
生成コマンドの本体は他のコマンドと同じように解析します。そのため
eval "$(rm -rf /)" は rm でブロックし、source <(git reset --hard) は reset でブロックします。
インタープリターの 1 行コードをすべてブロックする
内容にかかわらずインタープリターの 1 行コードをブロックしたい場合は、CC_SAFETY_NET_PARANOID_INTERPRETERS=1 を設定します。危険なコードが含まれていなくても、python -c、node -e、ruby -e、perl -e のすべての 1 行コードがブロックされます。python -c "print(1)" は standard と strict では許可されますが、ここではブロックされます。モードを参照してください。
strict と paranoid の heredoc
fail closed の機能が有効な場合、heredoc を含むコマンドは、次の条件をすべて満たすときにだけ許可されます。
- コマンドに含まれる heredoc が 1 つだけであること。
- その heredoc が標準入力に接続されていること。
- 展開されない heredoc であること。デリミタが引用符で囲まれているか、引用符がなくても本文に
$、バッククォート、バックスラッシュが含まれないことを指します。 - 他に入力リダイレクトがないこと。
- 読み取り側が、リテラルで書かれた
cat、tee、git apply、git commit、gh pr create、gh issue createのいずれかであること。
$、バッククォート、バックスラッシュのいずれかが含まれる場合は、次の理由で拒否されます。
解析できないコマンドテキスト
シェルのパーサーがコマンドをトークンに分割できない場合(引用符が閉じていない場合など)、その後の挙動は安全レベルによって変わります。
ヒューリスティックなスキャンが確認するのは、
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 の引数として引用符で囲んでもスキャンには引っかかりません。
スキャンはさらに、シェルへパイプしたダウンロード、つまりリモートインストールのワンライナーを探します。リモート取得コマンド(curl、wget、fetch、aria2c、http、https、xh、xhs、nc、ncat、netcat)を sh、bash、zsh、dash、ksh のいずれかにパイプしていれば、raw-text.dangerous-command で拒否します。シェル名にパスが付いていても(| /bin/sh)、sudo、env、command、builtin のラッパーを挟んでいても(| sudo sh、| env VAR=1 sh)同じです。コマンドの途中やパイプの前後にバックスラッシュ改行を入れても隠せません。このパターンも、テキストが echo または rg で始まる場合は対象外になるため、echo で表示するインストール行はデータのままです。同じパイプラインが実際のコマンドとして解析できる場合は、代わりに構造から「shell execution source cannot be verified safely」の理由で拒否します。テキストスキャンは、heredoc の本文のようにパーサーが中まで降りないテキストで、これを捕捉します。
explain コマンドを使うと、CC Safety Net が特定のコマンドをブロックまたは許可した理由を正確にたどれます。指定できるフラグは CLI コマンド、JSON のスキーマは explain トレースを参照してください。