1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Git diffを監査する、引数経由のファイル上書き

1
Posted at

差分を読むだけの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ラッパーでは、ファイル名の直前に--を入れていますか?

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?