gh CLIラッパースクリプトでAIエージェントのコマンド実行を制限

anthropics/claude-code

AIエージェントが実行できる gh CLIコマンドを限定するラッパースクリプト gh.sh を追加し、トリアージ・重複検出ワークフローの許可コマンドをこのラッパー経由に切り替えました。これにより、エージェントが意図しない操作(Issueの作成・削除・編集など)を実行するリスクを排除しています。

背景

Claude Codeには、GitHubのIssueをトリアージする /triage-issue とIssueの重複を検出する /dedupe という2つのカスタムコマンドがあります。これらのコマンドは allowed-tools フロントマターで実行可能なBashコマンドを宣言しており、従来は Bash(gh issue view:*) のように個別のサブコマンドを列挙していました。

この方式では、コマンドの種類ごとにフロントマターへの記載が必要で、管理が分散していました。加えて、gh CLI自体はIssueの作成・編集・削除など多数の操作をサポートしており、許可ルールの記述ミスがあった場合にエージェントが意図しない副作用を持つ操作を実行できてしまうリスクがありました。

このラッパースクリプトの導入で、許可コマンドの管理をスクリプト内に一元化し、エージェントへの露出面を最小化しています。

技術的な変更

scripts/gh.sh を新規追加し、gh CLIを呼び出せるサブコマンドとフラグを明示的に制限しました。許可されるのは読み取り系の操作のみです。

許可されるサブコマンド:

  • issue view
  • issue list
  • search issues
  • label list

許可されるフラグ:

  • --comments
  • --state
  • --limit
  • --label

スクリプトの中核となるバリデーションロジックは以下の通りです。まずサブコマンドをallowlistで検証し、許可外なら即座に exit 1 します。

#!/usr/bin/env bash
set -euo pipefail

ALLOWED_FLAGS=(--comments --state --limit --label)
FLAGS_WITH_VALUES=(--state --limit --label)

SUB1="${1:-}"
SUB2="${2:-}"
CMD="$SUB1 $SUB2"
case "$CMD" in
  "issue view"|"issue list"|"search issues"|"label list")
    ;;
  *)
    exit 1
    ;;
esac

shift 2

# Separate flags from positional arguments
POSITIONAL=()
FLAGS=()
skip_next=false
for arg in "$@"; do
  if [[ "$skip_next" == true ]]; then
    FLAGS+=("$arg")
    skip_next=false
  elif [[ "$arg" == -* ]]; then
    flag="${arg%%=*}"
    matched=false
    for allowed in "${ALLOWED_FLAGS[@]}"; do
      if [[ "$flag" == "$allowed" ]]; then
        matched=true
        break
      fi
    done
    if [[ "$matched" == false ]]; then
      exit 1
    fi

フラグのバリデーションでは ${arg%%=*}--state=open のような = 付き形式にも対応しており、値部分を除いたフラグ名だけをallowlistと照合しています。

コマンドのスコープは環境変数 GH_REPO または GITHUB_REPOSITORY で指定されたリポジトリに限定されます。あわせて .github/workflows/claude-issue-triage.ymlGH_REPO: ${{ github.repository }} の環境変数設定が追加され、ワークフロー実行時のリポジトリスコープが明示的に渡されるようになっています。

allowed-tools の変更前後:

変更前 (dedupe.md):

allowed-tools: Bash(gh issue view:*), Bash(gh search:*), Bash(gh issue list:*), Bash(./scripts/comment-on-duplicates.sh:*)

変更後 (dedupe.md):

allowed-tools: Bash(./scripts/gh.sh:*), Bash(./scripts/comment-on-duplicates.sh:*)

フロントマターのエントリ数が複数から1つに集約され、許可コマンドの追加・変更はスクリプト側のallowlistを編集するだけで済むようになりました。

設計判断

許可リスト方式(allowlist) を採用し、未定義のコマンドはすべて拒否する設計になっています。

スクリプトはサブコマンドとフラグの両方をallowlistで検証します。どちらかが許可リスト外であれば exit 1 で即時終了し、gh への呼び出し自体が行われません。これにより、gh issue creategh issue edit など書き込み系の操作はスクリプトレベルで遮断されます。

また、set -euo pipefail を宣言することで、スクリプト内でのコマンド失敗・未定義変数参照・パイプ失敗を確実にエラーとして扱い、意図しない挙動でコマンドが続行されることを防いでいます。エージェントへのプロンプトにもラッパーの使い方を具体例付きで記載することで、エージェントが正しいインターフェースを通じてのみ操作するよう誘導しています。

まとめ

gh.sh ラッパーの導入は、AIエージェントに与えるツールの「最小権限原則」を実装レベルで担保する変更です。allowed-tools フロントマターでの個別コマンド列挙から、スクリプト内allowlistへの集約により、許可範囲の変更が単一箇所の編集で完結する保守性の高い構造が実現されています。

記事メタデータ

Generated by:
Claude Sonnet 4.6 for DiffDaily
LLM Trace:
1657a626

この記事はAIによって自動生成されています。内容の正確性については、必ずソースコードやPRを確認してください。

品質レビュー結果

Review Status:
承認済み
Review Count:
1回
Reviewed by:
Gemini 2.5 Pro for DiffDaily

Review Criteria:

記事構成 ✓ PASS

Title, Context, Technical Detailの存在と明確さ

記事全体の構成が「総論→各論→結論」の3部構成に準拠しており、リード文、背景、技術的変更、設計判断、まとめの各要素が明確に配置されています。

カスタムMarkdown構文 ✓ PASS

シンタックスハイライト・GitHubリンク記法の正確性

ファイル名付きシンタックスハイライト(`bash:scripts/gh.sh`)およびGitHubのPRリンク記法(`[PR #28533](URL)`)が正しく使用されています。

対象読者への適合性 ✓ PASS

エンジニア向けの適切な技術レベルと表現

CLI、シェルスクリプト、GitHub Actionsに関する知識を前提としており、専門知識を持つエンジニアという対象読者に適した技術レベルと表現で書かれています。

パラグラフ・ライティング ✓ PASS

トピックセンテンス・1段落1トピック・段落長

各セクションが総論→各論の構成で、各段落がトピックセンテンスで始まっています。1段落1トピックの原則と適切な段落長が守られており、可読性が高いです。

Diff内容との照合 ✓ PASS

コードブロックとDiff内容の一致

記事内のコード引用(`gh.sh` の一部、`dedupe.md` の変更前後)は、提供されたDiff情報と完全に一致しています。ファイルパスも正確です。

技術用語の正確性 ✓ PASS

技術用語の正確な使用

「ラッパースクリプト」「allowlist」「サブコマンド」「最小権限原則」などの技術用語が、文脈に沿って正確に使用されています。

説明の技術的正確性 ✓ PASS

技術的主張の正確性と論理性

ラッパースクリプトが意図しないコマンド実行を防ぐ仕組みや、`allowed-tools`の集約による保守性向上といった技術的な説明が、Diff内容に基づいており正確です。

事実の突合 ✓ PASS

PR情報による主張の裏付け(ハルシネーション検出)

記事内のすべての主張(許可コマンドリスト、影響を受けるファイル、設計思想など)は、PRのタイトル、説明、Diff内容によって裏付けられており、ハルシネーションは検出されませんでした。

数値・固有名詞の確認 ✓ PASS

PR番号・コミットID・バージョン等の正確性

PR番号(#28533)が正確に記載されています。

タイトル・説明との一致 ✓ PASS

記事タイトル・説明とPR内容の一致

記事のタイトル「gh CLIラッパースクリプトでAIエージェントのコマンド実行を制限」は、PRの主題を的確に要約しています。

外部知識の正確性 ✓ PASS

PRに記載のない外部知識(LTS、サポート状況など)の不使用

PR情報に含まれない外部知識(バージョンのサポート状況、リリース日程など)の記述はなく、すべての情報が提供された情報源に基づいています。

時間表現の正確性 ✓ PASS

時間表現がPR情報と一致しているか

完了した変更を説明する「〜しました」「〜されています」といった時制表現が使われており、PRの状況と一致しています。