explain の出力を読む場合や、正確なカスタムルールを書く場合に必要な詳細を扱います。
分類器はガードの最後の段階です。その前に、上限付きのツール入力抽出、パーサー予算、ポリシーファイルと Git メタデータの保護、ポリシースナップショットの読み込み、機密パス保護がすでに完了しています。これらは順序付きガードステージで規定されています。上限付き解析と常時有効なポリシーファイルおよび Git メタデータのガードは、すべての安全レベルでフェイルクローズします。無効なポリシースナップショットには保護的な fallback を適用し、機密パス保護は解決済みポリシーに従います。破壊的コマンド分類器は、前段ですでに行われた判定を緩和できません。
ディスパッチフロー(セグメントへ分割し、環境変数の代入とラッパーを除去し、先頭コマンドを特定して、対応する解析器へ渡す)は、アーキテクチャに図示しています。このページでは、その続きとして、各解析器が受け取ったセグメントを処理する方法と、安全レベルによって境界が変わる位置を説明します。
安全レベルの境界
3 つの安全レベルは、3 つの機能を組み合わせたプリセットです。各境界を正しく理解すれば、どの操作がブロックされるかを予測できます。
3 つのプリセットのいずれにも一致しない機能の組み合わせは、有効レベル
custom として報告されます。
Standard
Standard は、認識できる破壊的コマンドをブロックします。意図的に敵対的入力への完全な対策とはしておらず、動的入力または敵対的入力に対しては best-effort です。- 安全に見える解析不能なテキストは許可します。
echo 'unterminatedは許可されます。 - 認識できる破壊的テキストは、解析不能でもブロックします。
git reset --hard 'unterminatedは raw-text heuristic scan によってブロックされます。このスキャンはrm -rf、git reset --hard/--merge、git clean -f、git checkout --force、git push --force/--delete、git branch -D、git tag -d、git stash drop/clear、git checkout --、git restore、find -delete、dd of=/dev/、mkfs /dev/、shred <arg>を認識します。 - quote されたリテラル代入内の危険なテキストは、使用時まで判定を延期します。
W='rm -rf ~'; echo "$W"は許可されます。代入自体は何も実行せず、引数位置で quote された展開は 1 つの argv word のままなので、コマンドとフラグに分割されません。quote されていない参照、コマンド位置での参照、置換内の参照、quote されていない heredoc 本文内の参照など、より危険な使用では代入時のブロックを維持します。値を shell へ渡す操作(eval "$W"、bash -c "$W"、echo "$W" | sh)も、shell の実行元を検証できないため拒否します。Strict では判定を延期しません。Standard だけで許可される操作を参照してください。 - 動的な
rm -rfの対象は、一律にはブロックしません。rm -rf "$target"は standard では許可し、fail-closed 機能が有効な場合だけブロックします。 - Standard は、動的な実行ファイル、置換で組み立てたガード対象のコマンド構造、その他の検証不能な再帰削除対象、組み込み機密パスに対する単独のメタデータ確認も意図的に許可します。
- Standard でも、機密情報の内容へのアクセス、設定済み deny path、壊滅的操作に対する保護は緩和しません。
Strict
Strict modeは fail-closed 機能を有効にします。解析不能な場合の扱いを厳しくするだけではありません。- 解析不能なコマンドをブロックします。
echo 'unterminatedは、コマンドを安全に解析できなかったという理由で拒否されます。 - メタデータだけを調べる機密パス探索をブロックします。
test -f ~/.ssh/id_rsaとfind ~/.ssh -type fは standard では許可され、strict ではブロックされます。 - Node と Bun の inline evaluation 内にある機密パスのリテラルを、実行されない診断データとして扱う standard 固有の緩和を無効にします。
- ヒアドキュメントはフェールクローズされます。 他のアナライザーがセグメント上で実行される前に、ヒアドキュメントを含むコマンドは、ヒアドキュメント分析 で説明されている狭いサポート対象ゲートを通過しない限り拒否されます。つまり、引用符で囲まれた標準入力上の 1 つのヒアドキュメント、他の入力リダイレクトなし、および 6 つのリテラル データ コンシューマの 1 つです。引用符で囲まれていない区切り文字は、「引用符で囲まれていないヒアドキュメント入力は安全にサポートされていません。区切り文字を引用するか、ユーザーに検証を依頼してください。」で拒否されます。他のすべての失敗は、「このヒアドキュメント フォームまたは標準入力コンシューマは安全にサポートされていません。サポートされているコンシューマ (cat、tee、git apply、git commit、gh pr create、gh issue create) で引用符で囲まれたヒアドキュメントを使用するか、ユーザーに検証を依頼してください。」と拒否されます。実際には、
python3 - <<'PY'と引用符で囲まれていない<<EOFは厳密かつ偏執的に拒否されます。 - 検証できない破壊的なターゲットはブロックされます。 5 つのルールがフェールクローズ機能でゲートされているため、標準では許可され、厳密ではブロックされます。
strict tier の各ルールは個別に無効化できます。ただし、破壊的コマンドのルール ID がない fail-closed 結果(パーサーの fail-closed 結果と機密パスの結果)には、strict の動作がそのまま適用されます。
Paranoid
Paranoid modeは、strict に 2 つの機能を追加します。- Paranoid
rmは、現在の作業ディレクトリの内側でも、一時ディレクトリ以外を対象にした再帰的な強制削除をブロックします。rm -rf ./cacheとRemove-Item ./cache -Recurse -Forceはどちらもブロックされます。一時ディレクトリと設定済み allow path は引き続き許可されます。 - Paranoid interpretersは、内容に関係なく、
python -c "print(1)"を含むすべてのインタープリターの 1 行コードをブロックします。
ルールごとのオーバーライドとレベル
壊滅的でないルールには、最初に master switchdestructive_command_protection、次に解決済みの preset capability、最後にルールごとの "on" / "off" override を適用します。strict tier または paranoid tier のルールは、"on" override を使って standard でも強制的に有効化できます。
壊滅的操作に対するルールは、master switch と "off" override の両方を無視します。 対象は rm.recursive-force-root-or-home、rm.git-metadata、powershell.remove-item-root-or-home、powershell.remove-item-recursive-force-root-or-home、powershell.remove-item-git-metadata、find.delete-git-metadata です。これらは常時有効なポリシーファイルガードと同じ扱いです。
シェルラッパーとインタープリターの 1 行コード
ラッパーとインタープリターへの再帰は最大 10 階層です。1 つのセグメントがブロックされると、コマンド全体を拒否します。 環境割り当てとラッパーがセグメントから削除された後、コマンド名が次の 2 つのセットに対してチェックされます。- シェルラッパー:
bash、sh、zsh、ksh、dash、fish、csh、tcsh。-cの後の引数を抽出し、再帰的に解析します。 - インタープリター:
python、python2、python3、node、ruby、perl。コード引数(-cの後、または node/ruby/perl では-eの後)を抽出し、埋め込まれた破壊的操作をスキャンします。
python -c 'import os; os.system("rm -rf /")' は、埋め込まれた rm -rf / が原因でブロックされます。1 行コードという形式だけではブロックしません。Paranoid interpreters modeでは、内容に関係なくすべての 1 行コードをブロックします。
busybox のディスパッチは特別に処理します。サブコマンドをコマンド位置へ移して再解析します。awk/gawk/mawk のプログラムでは、system() 呼び出しと backtick command substitution をスキャンします。
透明なラッパー
標準ラッパー(sudo、env、command、builtin)は常に除去します。transparent wrapper として宣言した proxy command も除去します。宣言方法と制約はカスタムルールを参照してください。ここでは、エンジンがラッパーを展開する方法を説明します。
展開時は、ラッパーのフラグと環境変数の代入より後にある最初の保護可能な子コマンド、または明示的な -- の直後にある token を探します。展開後は、組み込み解析とカスタムルールの両方を子コマンドに適用します。rtk git reset --hard は組み込みルールでブロックし、rtk docker system prune は対応するカスタムルールに一致します。保護可能な子コマンドがない場合は展開しません。
宣言されていない proxy と、見えている子コマンドをそのまま実行せずに書き換えるか隠す proxy は展開できません。検出できる可能性があるのは、最上位で実行する危険テキストの fallback scan だけです。
同じコマンドの各セグメントにわたって cwd を追跡します。リテラルの対象を持つ cd または pushd は、後続の rm と find の解析に使う有効な cwd を更新します。$ または backtick を含む動的な対象への cd は、cwd を不明にします。rm の解析では、cwd の基準がないものとして扱います。
既知の制限:インタープリターの長形式フラグ(
--eval、--execute、--require、値を付けた =value 形式)は、すべての code path で認識されるわけではありません。そのため、コード引数を抽出できない場合があります。既知の制限を参照してください。POSIX シェル関数
name() { ... } などの POSIX 関数定義は、実行コードではなく定義として解析します。CC Safety Net は、呼び出し元で有効な作業ディレクトリと shell state を使い、各呼び出し位置で関数本体を解析します。
cleanup() { rm -rf ../outside; } は関数を定義するだけなので許可されます。cleanup() { rm -rf ../outside; }; cleanup のように呼び出しを追加すると、関数本体のコマンドによってブロックされます。呼び出した本体内の state change は、後続処理へ引き継ぎます。例えば cleanup() { cd ..; }; cleanup && rm -rf build は、rm の基準が 1 つ上のディレクトリになるためブロックされます。
呼び出し解決はシェル独自のルールに従います。
- 呼び出しは、先頭の環境変数の代入(
X=1 cleanup)、timekeyword とその-poption および--terminator、!による否定を解決します。組み合わせたtime -p -- ! cleanupも対象です。名前を quote または escape した形式('cleanup'、"cleanup"、\cleanup)は alias expansion を抑制しますが、function lookup は抑制しないため、関数を呼び出します。 - 実際の shell で実行されない形式である
X=1 time cleanup、time "--" cleanup、space のない!cleanupは、呼び出しとして扱いません。keyword form が解決されないためです。 - 呼び出しより前にある最新の定義を優先します。これは shell の再定義 semantics と一致します。
- subshell(
( ... ))内の定義は外へ出ません。brace group({ ...; })は同じ shell 内で実行されるため、その定義は外でも有効です。 - 定義は、同じ shell で動く
evalとtrapからも参照できます。cleanup() { rm -rf ../outside; }; eval cleanupはブロックされます。一方、子 shell には継承されないため、sh -c cleanupは関数を解決しません。
f() { rm -rf "$1"; }; f ~ は動的な対象として扱います。standard では許可し、fail-closed 機能が有効な場合はブロックします。これは rm -rf "$X" と同じです。quote された代入の判定延期は、呼び出した関数本体内にも適用します。W='rm -rf ~'; f() { $W; }; f は、quote されていないコマンド位置で値を使うため、代入時のブロックを維持します。f() { echo "$W"; }; f は、quote された引数データとして許可します。
解析前のガードステージも関数呼び出しを確認します。ポリシーファイル保護と機密パス抽出は、各呼び出し位置で実行する brace group と関数本体を評価します。
standard を含むすべてのレベルで、2 つの構造境界がフェイルクローズします。自己再帰(loop() { loop; }; loop)は再帰深度の上限で拒否します。分岐する呼び出し chain は、派生コマンドの作業予算または 256 個の inline call-site projection の上限で拒否します。関数本体に付属する heredoc は安全にサポートされません。コマンドが解析不能になるため、standard では heuristic scan の対象となり、strict ではすべて拒否します。
ヒアドキュメントの分析
heredoc 本文は標準入力上のテキストです。そのテキストをデータとして扱うかプログラムとして扱うかは、読み取り側が決めます。他の解析器がセグメントを処理する前に、エンジンは狭く定義した対応条件を確認します。次の条件をすべて満たす場合だけ通過します。- コマンドに含まれる heredoc(
<<または<<-)は、標準入力(fd 0)に接続した 1 つだけです。 - delimiter は quote されています(
<<'EOF')。そのため、本文内で substitution を展開できません。 - stdin (
<、<<、<<-、<<<、<&、<>) と競合する他の入力リダイレクトはありません。 - 読み取り側はリテラルの
cat、tee、git apply、git commit、gh pr create、gh issue createのいずれかです。path prefix とenvなどのラッパーは使えません。出力 process substitution(>(...))がある場合は本文が別のコマンドへ渡るため、catとteeも拒否します。
cat > note.md <<'EOF' と git commit -F - <<'EOF' は、本文に破壊的コマンドが書かれていても許可します。
cat、tee、git commit、gh pr create、gh issue create では、機密パスを抽出する前に本文も mask します。これにより、「credentials」という単語を含む commit message を filename として扱いません。git apply の本文は patch が書き込む filename を含むため、mask しません。heredoc の外にあるコマンドは引き続き解析します。例えば cat <<'EOF' && rm -rf ~ は、rm によってブロックされます。
条件を満たさないコマンドは、strict と paranoid ではすべて拒否します。standard では、次の 3 経路のいずれかで本文を解析します。
- syntax check だけを行う shell(
bash -nなど)の標準入力へ渡す、quote 済み heredoc は実行されないデータとして許可します。 - インタープリター(
python/python2/python3、node、ruby、perl)の標準入力へ渡す、quote 済み heredoc です。コマンドライン上の他のすべての word が-で始まるリテラルで、インタープリターの program が stdin の場合に限ります。python3 - <<'PY'は条件を満たしますが、python3 tool.py <<'PY'は満たしません。後者では stdin が script の data になるためです。本文はインタープリターのルールで解析します。Paranoid interpretersではinterpreter.one-liner-paranoidとしてすべてブロックします。本文に危険な code block があればinterpreter.dangerous-commandとしてブロックし、安全な本文は許可します。 - その他すべて(quote されていない delimiter、不明な読み取り側、
bashheredoc script、script operand を持つ interpreter call)は、結合した本文の raw-text heuristic scan に渡します。一致した場合はraw-text.dangerous-commandとしてブロックし、一致しなければ許可します。
structural-limit 解析状態を報告し、standard を含むすべての安全レベルでコマンドを拒否します。
実行されないデータとして条件を通過しても、本文を file に保存する場合は追加処理があります。条件を満たした heredoc を、追記せずにリテラル path へそのまま書き込む場合(cat > setup.sh <<'EOF' または tee setup.sh <<'EOF')、エンジンはその path と本文を記憶します。同じコマンド内で後から bash setup.sh、sh setup.sh、source setup.sh を実行するか、shell startup reference(BASH_ENV、ENV、--rcfile、--init-file)で参照すると、記憶した script text を解析します。trace には reason heredoc-file の recurse step として表示します。そのため、本文が破壊的な場合、cat > x.sh <<'EOF' … EOF && bash x.sh はブロックされます。追跡範囲は意図的に限定しています。解析ごとに最大 64 file を記憶します(MAX_TRACKED_HEREDOC_FILES。超過時は派生コマンドの作業上限でフェイルクローズ)。/dev、/proc、/sys 配下の path は追跡しません。追跡対象 path への後続の書き込みまたは redirection は、保存した本文を無効にします。
Git ルールエンジン
git 解析器は subcommand と option を抽出して危険な option pattern と照合し、理由と分類(localDiscard または sharedState)を返します。この分類によってworktree の緩和を判断します。
option の照合は、git の実際の文法に従います。長形式 option には prefix matching を使うため、
--forc、--force、--force-with-lease を正しく解決します。短形式 option は bundle を展開するため、-Df は -D と -f として読み取ります。値を取る global option(-c、-C、--git-dir、--work-tree)は、subcommand を探すときに読み飛ばします。ブロック対象の git pattern の完全な一覧は、ブロックされるコマンドを参照してください。
Git SSH environment override
Git SSH environment override
Git は、network operation 中に任意の program を実行するため、
GIT_SSH_COMMAND、GIT_SSH、GIT_SSH_VARIANT を受け付けます。CC Safety Net は、これらの override を network subcommand(clone、fetch、pull、push、ls-remote、submodule)と組み合わせた場合にブロックします。network operation 中に任意のコマンドを実行できるためです。checkout の詳細
checkout の詳細
checkout は次の順序で確認します。force(--force/-f)、new-branch escape(-b/-B/--orphan はブロックしない)、--pathspec-from-file、double-dash pathspec(git checkout -- は未コミットの変更を破棄し、git checkout <ref> -- <path> は working tree を ref の version で上書きする)、曖昧な複数 positional form(2 個以上の positional がある場合は switch/restore を提案する)の順です。reset の分類
reset の分類
reset --hard/--merge は、ref が -- より前にある場合(branch pointer を移動する)は sharedState、それ以外の場合(working-tree change だけを破棄する)は localDiscard に分類します。再帰的削除対象の分類
rm の解析は recursive flag と force flag の組み合わせを検出し、対象を抽出して、現在の作業ディレクトリを基準に分類します。次の順序で確認し、最初に一致した分類を採用します。順序そのものが重要です。
この順序には、明示すべき 2 つの結果があります。
- step 4 は step 7 より前にあるため、repository を含む allow path でも Git metadata protection は緩和されません。
- step 6 は step 7 より前にあるため、allow path は動的または検証不能な対象には適用されません。
~/ から始まる directory にする必要があります。すべての安全レベルで rm、Remove-Item、find -delete に適用します。機密情報保護、deny path、root、home、保護対象の Git metadata は緩和しません。$HOME と等しい entry、または $HOME を含む上位 path は、validation 時と canonicalization 後の両方で拒否します。symlink escape は対象外です。
この分類は、recursive flag(-r/-R/--recursive)と force flag(-f/--force)の両方がある場合に実行します。path の比較には canonical(realpath)解決を使います。このため、/ を指す symlink は危険と正しく分類し、/tmp-malicious は /tmp の一時ディレクトリルールに一致しません。
日常的な使用で重要な違いがあります。
rm -rf ./subdir(cwd 内)は許可しますが、rm -rf .(cwd 自体)はブロックします。許可されるコマンドを参照してください。PowerShell Remove-Item
Remove-Item とその alias は、rm と同じ対象分類を使います。解析には、native quoting、path separator、connector、pipeline、dynamic word の由来を保持する保守的な PowerShell subset を使います。
-WhatIf、-WhatIf:$true、および-wiの略語は、そうでなければブロックされた除去を無効化します。明示的な-WhatIf:$falseは再びブロックします。- 動的フォームは strict のみです。
Remove-Item $target -Recurse -Force、Get-ChildItem … | Remove-Item -Forceパイプライン、値のない-Path、およびスプラッティング (Remove-Item @params -Recurse -Force) はすべて標準で許可され、strict ではブロックされます。例外はRemove-Item $HOME -Recurse -Forceです。これは、動的ターゲットではなくルート/ホーム ターゲットとして分類されるため、標準 でブロックされます。 - エイリアスと省略されたパラメーターは解決されるため、
ri . -r -foはブロックされます。呼び出し演算子形式 (& Remove-Item …、& { … }、. { … }) が分析され、リテラル文字列を含むInvoke-Expressionおよび$(…)部分式も分析されます。 #行コメントと<# … #>ブロック コメント (ネストされたものを含む) は無視されますが、その後の実際のコマンドはブロックされます。不正な形式または深さが制限されたブロック コメントおよび部分式はフェールクローズされます。- PowerShell ワイルドカードは、POSIX
*グロブとは異なり、ドット エントリと一致します。そのため、Remove-Item .git -Recurse -Forceとリポジトリ ルートの PowerShell ワイルドカードは両方とも Git メタデータ保護にヒットしますが、POSIX./*は.gitをカバーしません。 - シェルの選択は重要です。
posix方言は意図的に PowerShell 削除ルールを適用しませんが、autoは明示的なRemove-Itemを検出し、git.reset-hardなどのクロスシェル ルールを引き続き有効にします。
デバイスとディスクの破壊
3 つの形式は、解析不能テキストの heuristic scan にも含まれます。そのため、
echo または rg で始まる場合を除き、解析不能テキスト内の dd of=/dev/…、mkfs /dev/…、shred <arg> は raw-text.dangerous-command としてブロックされます。いずれも壊滅的操作のルールではないため、master switch とルールごとの override の優先順位に従います。
find、xargs、parallel の動的対象解析
xargs と parallel では、対象を動的入力(pipe された stdin または placeholder expansion)から得るため、cwd を基準に検証できません。parallel の SSH remote mode(-S/--sshlogin)も worktree relaxation を無効にします。
Worktree の緩和
Worktree modeが有効な場合、検証済み linked worktree 内の local-discard git command を許可します。緩和には、次の条件をすべて満たす必要があります。- 一致したルールが
localDiscardに分類されること(Git ルールエンジンの表を参照)。sharedStateルールは緩和しません。 - worktree mode が有効であること。
policy.jsonのworkflow.worktree_modeとCC_SAFETY_NET_WORKTREE=1を logical OR で結合します。 - git context の environment override(
GIT_DIR、GIT_WORK_TREE、GIT_COMMON_DIR、GIT_INDEX_FILE)がなく、コマンドラインにも--git-dir/--work-treeがないこと。
.git entry が file(directory または symlink ではない)であること、gitdir: pointer が commondir file を持つ directory に解決されること、backlink がこの worktree を指すこと、config.worktree が一致することを確認します。main worktree、bare repository、submodule は緩和しません。検証に失敗した場合は、理由に関係なくコマンドをブロックしたままにします(fail closed)。
緩和できない local discard
緩和できない local discard
検証済み linked worktree 内でも、次の操作は緩和しません。
$、*、?、[ を含む動的引数。強制的な branch reset(git checkout -B/-Bf、または git switch -C/-Cf と -f または --discard-changes の組み合わせ)。複数の -f flag を持つ git clean(使い捨て worktree の境界を越え、nested git repository を削除するために必要)。--recurse-submodules option または recursive-submodule config。git -C の path 解決
git -C の path 解決
先頭の global option を順に処理して、有効な git 作業ディレクトリを解決します。
-C <path> と inline -C<path> は directory change として適用します。--git-dir/--work-tree(別々の形式または = 形式)は明示的な git context として扱い、緩和をすべて無効にします。カスタムルール
対応する組み込み解析器がない場合は、fallback としてカスタムルールを実行します。カスタムルールは厳密に追加専用です。ブロックを追加できますが、組み込みのブロックを override したり、保護を緩和したりできません。ルールは<rulebook-name>/<rule-name> の namespace を持ち、command basename、任意の subcommand、リテラルの block_args で照合します。短形式 option は bundle を展開するため、-Ap は -A に一致します。
完全な作成ガイドと照合 semantics は、カスタムルールを参照してください。
分類を検査する
エンジンが特定のコマンドをどのように評価したかを正確に確認するには、explain を実行します。
npx cc-safety-net status は ready または degraded を出力します。該当する source の修復方法は設定の復旧を参照してください。
次に読むページ
技術ガイドは、ユーザー向けの lifecycle から設計理由までを順に説明します。このページは step 4 です。- 戻る: アーキテクチャ — この分類子が最後の部分となる順序付きガード ステージ。
- 次へ: 設計原則 — 分類がパターンベースではなくセマンティックである理由、およびレベル境界がその位置に収まる理由。