差分を読むだけのGitツールに、ファイルを上書きする余地はある?
AIエージェント用のツールをgit diffに絞っても、引数の監査は外せない。Pythonのshell=Falseを使っていても、ファイル名のつもりの入力で出力先を変えられる。
ファイル名を1引数で渡したら
手元のPython 3.14.8、Git 2.50.1(Apple Git-155)で確認した。一時ディレクトリにリポジトリを作り、ステージ後にファイルを変更する。コミットは不要。
from pathlib import Path
import subprocess
from tempfile import TemporaryDirectory
with TemporaryDirectory() as tmp:
root = Path(tmp)
def git(*args):
return subprocess.run(
['git', *args], cwd=root, shell=False,
check=True, capture_output=True, text=True)
git('init', '-q', '--template=')
(root / 'sample.txt').write_text('before\n')
git('add', '--', 'sample.txt')
(root / 'sample.txt').write_text('after\n')
target = root / 'keep.txt'
target.write_text('KEEP\n')
name = '--output=keep.txt'
bad = git('diff', name)
print('unsafe:', len(bad.stdout), target.read_text().startswith('diff --git'))
target.write_text('KEEP\n')
safe = git('--literal-pathspecs', 'diff', '--', name)
print('safe:', len(safe.stdout), target.read_text().strip())
good = git('--literal-pathspecs', 'diff', '--', 'sample.txt')
print('normal:', '-before' in good.stdout, '+after' in good.stdout)
print('wildcard:', bool(git('diff', '--', '*.txt').stdout))
print('literal:', bool(git('--literal-pathspecs', 'diff', '--', '*.txt').stdout))
出力は次のとおり。
unsafe: 0 True
safe: 0 KEEP
normal: True True
wildcard: True
literal: False
最初の0は標準出力の文字数。自分は空の出力だけ見て終わりそうになった。keep.txtを読むと、KEEPが差分に置き換わっていた。終了コードも0。check=Trueでは止まらないんだよね。
シェルを通さなくてもGitは引数を読む
引数は分割されていない。Git自身が--output=keep.txtをオプションとして処理した。書き込み権限のある出力先を指定できれば、読み取り用のつもりのツールから上書きできる。
shell=Falseでシェルの解釈を避けても、呼び出し先のオプション解析は残る。
修正後はdiffの後ろに--を置いた。以降はパスとして扱われる。該当ファイルはないので差分は空で、KEEPは残った。normalでは通常のファイルの変更行も取得できた。
次に、ファイル名の範囲を絞る
--の後ろでも、Gitのpathspecでは*.txtがパターンとして働く。wildcard: Trueは、これでsample.txtの差分を取得した結果だ。
ファイル名を完全一致で受け付けるなら、グローバルオプションの--literal-pathspecsも付ける。literal: Falseは、文字どおり*.txtというファイルが存在しないため。パターンで選ぶ機能なら、仕様に合わせて使い分ける。
同梱の公式マニュアルでも仕様を確認した。Web版はgit diffとGitの共通オプション。手元の版のマニュアルも地味に便利。
ツール定義へ戻す
エージェントへ公開する引数はpathに絞り、実行側でオプションと区切りを固定する。今回の対処は、オプション混入とパターン展開まで。リポジトリ設定や外部diffの実行可否、読ませるファイルの範囲は別途管理する。ツールを増やすときも、正常な名前とハイフンで始まる入力を並べて確認するつもりだ。
手元のGitラッパーでは、ファイル名の直前に--を入れていますか?