「常に続行」を選んだはずなのに、エージェントがまた「このコマンドを実行してよいですか」と聞いてきました。三度目の確認が出たところで、私は手を止めて、画面を閉じるかどうか迷っておりました。
設定が効いていないのではないか、と疑う気持ちはよく分かります。公式のフォーラムにも、似た症状の相談が出ています。ただ、私が先にお伝えしたいのは、原因探しより前に済ませておくと楽になる作業があるということです。
それは、緩める範囲を、設定の名前ではなくコマンドの種類で決めておくことです。ここでは、その決め方と、承認ログを仕分ける小さなスクリプトを書き残します。
原因が分からない日は、聞かれる理由が二通りあります
承認を求められる理由を、私は次の二つに分けて考えるようになりました。
一つめは、設定そのものが効いていない場合です。バージョンの不具合や、設定を保存した場所と読まれる場所が違う、といった可能性があります。これは製品側の事情なので、自分では直せません。
二つめは、設定は効いているのに、そのコマンドが「続行してよい範囲」に入っていない場合です。こちらは、範囲の決め方を見直すだけで落ち着くことがあります。
どちらなのかをその場で見分けるのは、意外と難しいのです。だからこそ、見分けられない日のために、自分の側の線引きを先に持っておくことが役に立ちます。線引きがあれば、聞かれたときに「これは許してよい」「これは毎回聞かれてよい」と、落ち着いて答えられます。
なお、設定画面の項目名や選択肢の数は、版によって変わることがあります。ここでは画面の文言には踏み込まず、お手元の画面で確かめていただく前提で進めます。
緩める範囲は、三つの種類で決めます
コマンドを、影響の大きさで三つに分けました。
| 種類 | 例 | 私の扱い |
|---|---|---|
| 読むだけ | ls、cat、grep、git status | 聞かなくてよい |
| 手元を書き換える | mkdir、sed -i、npm run build | 毎回聞かれてよい(慣れたら範囲を限って緩める) |
| 消す・送る・権限を変える | rm、curl、git push、sudo | 必ず聞く |
ここで大切なのは、「常に続行」という一つの選択肢に、三つの種類をまとめて預けないことです。読むだけのコマンドまで毎回聞かれると疲れますし、疲れた人は承認ボタンを機械的に押すようになります。それがいちばん危うい状態です。
反対に、消す・送るコマンドまで一緒に緩めてしまうと、取り返しのつかない操作が確認なしに走ります。
承認ログを仕分けるスクリプト
では、自分が普段どんなコマンドを許しているのかを、実際に数えてみます。承認の画面に出たコマンドを一行ずつ控えておき、次のスクリプトに渡します。
何を解決するかというと、「聞かなくてよい割合」を数字で見て、緩める範囲が妥当かどうかを判断する材料にすることです。標準ライブラリだけで動きます。
#!/usr/bin/env python3
"""classify_cmds.py — 承認ログのコマンドを「聞かなくてよい/聞いてほしい」に仕分ける。
使い方: python3 classify_cmds.py commands.txt (1 行 1 コマンド)
先頭の語とオプションだけを見る粗い分類なので、判断の叩き台として使います。"""
import sys, shlex, collections
READ_ONLY = {"ls", "cat", "head", "tail", "grep", "rg", "find", "pwd", "wc", "diff", "tree", "which"}
GIT_READ = {"status", "log", "diff", "show", "branch", "blame"}
LOCAL_WRITE = {"mkdir", "touch", "cp", "mv", "sed", "tee"}
RUNNERS = {"npm", "pnpm", "yarn", "pytest", "python3", "node", "make"}
ALWAYS_ASK = {"rm", "curl", "wget", "ssh", "scp", "sudo", "chmod", "chown", "kill", "dd"}
GIT_RISKY = {"push", "reset", "clean", "checkout", "rebase", "commit"}
def classify(line):
try:
t = shlex.split(line)
except ValueError:
return "ask", "引用符が閉じていないため解釈不能"
if not t:
return "skip", ""
# 連結やコマンド置換があると、先頭の語だけでは安全と言えない
if any(x in line for x in ("|sh", "| sh", "| bash", "$(", "`", "&&", ";")):
return "ask", "連結・コマンド置換を含む"
c = t[0]
if c in ALWAYS_ASK:
return "ask", f"{c} は削除・通信・権限変更のいずれか"
if c == "git":
sub = t[1] if len(t) > 1 else ""
if sub in GIT_READ: return "auto", "git の参照系"
if sub in GIT_RISKY: return "ask", f"git {sub} は履歴や外部に影響"
return "ask", "git のその他の操作"
if c in READ_ONLY: return "auto", "読むだけ"
if c == "sed" and "-i" in t: return "ask", "sed -i は上書き"
if c in LOCAL_WRITE: return "ask", "手元のファイルを書き換える"
if c in RUNNERS: return "ask", "中で何が走るかは中身次第"
return "ask", "未知のコマンド"
if __name__ == "__main__":
rows = [l.rstrip("\n") for l in open(sys.argv[1], encoding="utf-8") if l.strip()]
tally = collections.Counter()
for l in rows:
k, why = classify(l)
tally[k] += 1
print(f"{k:5} | {l[:48]:48} | {why}")
total = sum(tally.values())
print(f"\n合計 {total} 件: auto={tally['auto']} ask={tally['ask']}")
print(f"聞かなくてよい割合: {tally['auto']/total:.0%}")なぜこう書いたかというと、未知のコマンドを「聞く」側に倒したかったからです。分類できないものを自動で通す設計にすると、見落としがそのまま事故になります。
手元の 15 行で動かしてみた結果
動作の確認用に、私が普段よく見かけるコマンドを 15 行並べたサンプルを用意して流しました。実際の承認ログではなく、この記事のために書いた一覧です。
結果は、15 件のうち 7 件が「聞かなくてよい」、8 件が「聞いてほしい」で、割合は 47% でした。
見ていて気づいたのは、npm run build や npm test が「聞いてほしい」側に入ることです。私の感覚では毎回聞かれると面倒な部類ですが、これらは package.json の中身次第でどんな処理でも走らせられます。中身を自分で読んで安全だと確認したリポジトリに限って、緩める価値があるのだと思います。
もう一つの発見は、パイプの扱いでした。cat logs/app.log | head -50 は読むだけとして通りましたが、| sh や && が付くと一気に「聞く」側へ倒れます。このスクリプトは先頭の語しか見ていないため、パイプの右側に別の危険なコマンドが来る形までは見抜けません。粗い道具だと割り切って、最終判断は人が行う前提で使ってください。
承認の画面で、立ち止まって自分に聞く三つのこと
線引きを持っていても、承認の画面を前にすると急いでしまいます。そこで私は、ボタンを押す前に、次の三つだけを自分に聞くようにしています。
一つめは、「これは読むだけか、書き換えるか」です。画面に出ているコマンドの先頭の語と、-i や -rf のようなオプションを見れば、たいてい判断がつきます。
二つめは、「元に戻せるか」です。手元のファイルの書き換えは、バージョン管理に入っていれば戻せます。削除や外部への送信は、戻せないことがあります。
三つめは、「連結されていないか」です。&& や ; でつながれていると、最初の一語は無害でも、後ろに別の操作が控えていることがあります。先ほどのスクリプトが連結を「聞く」側に倒したのも、この理由からです。
この三つに答えるのに、十秒もかかりません。それでも、答えずに押してしまうより、はるかに後悔が少なくなりました。
緩め方の順番は、狭いほうから
私は、次の順番で試しています。
まず、読むだけのコマンドを許可リストに入れます。これで、確認の回数がいちばん減ります。
次に、使っているリポジトリのビルドとテストだけを、そのフォルダに限って許します。最後の「消す・送る」は、体調が万全でない日でも崩さないように、緩めません。
緩めるのは設定の名前ではなく、コマンドの種類で。 この線引きを持っているだけで、「効かない」と感じた日にも、慌てて全部を許可する側へ倒れずに済みます。
次にやること
まずは、直近の承認画面に出たコマンドを 10 行ほど控えて、上のスクリプトに流してみてください。「聞かなくてよい」の割合が出たら、それが読むだけのコマンドの多さの目安になります。
設定が効かない原因のほうは、お使いの版の更新履歴と、公式のフォーラムの該当スレッドを確かめていただければと思います。私も、版が変わるたびに同じ確認をやり直しております。