はじめに
IBM Bob には、Git のブランチ間の差分を AI がレビューしてくれるレビューワークフロー(Code Reviews)があります。指摘は Bob Findings パネルに一覧表示され、そこから直接 Bob に修正させることもできます。
公式ドキュメントはこちらです。
この記事では、実際に手を動かして
- ローカルの Python プログラムを Git リポジトリ化してリモート(GitHub)に push する
- Bob のレビューパネルを開く
- わざと SQL インジェクションを埋め込んだ変更を加えてレビューさせる
- 指摘された内容を Bob 自身に修正させる
という一連の流れを確認します。
処理はすべて IDE 内でローカルに動きます。また、レビューは auto-approval で実行されるため、分析の各ステップで確認を求められることはありません。開始したらそのまま最後まで走ります。
テスト環境
| 項目 | バージョン |
|---|---|
| IBM Bob | 2.0.3 |
| OS | Windows 11 |
UI のラベルやショートカットキーは Bob のバージョンによって変わる可能性があります。本記事は筆者が実際に試した時点での挙動と、上記ドキュメントの記載を突き合わせた内容です。
検証に使ったサンプルプログラム
社員テーブルを作って 2 件だけデータを入れる、ごく単純な Python スクリプトを用意します。この時点では create table と insert をするだけで、SQL インジェクションの余地はありません。
import sqlite3
def main() -> None:
conn = sqlite3.connect("sample.db")
cursor = conn.cursor()
cursor.execute(
"create table if not exists shain (id integer primary key, name text)"
)
cursor.execute("delete from shain")
cursor.execute("insert into shain (id, name) values (?, ?)", (1, "田中"))
cursor.execute("insert into shain (id, name) values (?, ?)", (2, "佐藤"))
conn.commit()
conn.close()
if __name__ == "__main__":
main()
これを「レビュー前の正常な状態」としてリモートに push しておき、あとから危険なコードを追加してレビューさせるという段取りにします。
手順 1: リモートリポジトリを作成する
まず GitHub 側で空のリポジトリを作成します。今回は次の名前で作成しました。
260817reviewtest
なお、レビュー自体はローカルブランチ同士の比較でも動きます。リモートを用意するのは、ドキュメントのベストプラクティスにある「リモートブランチと比較して、最新のリモート状態に対して自分の変更を検証する」という使い方を再現するためです。実務ではこの形が基本になるはずなので、今回はリモートありで進めます。
ただし、git init しただけでリモートを登録していないリポジトリでは、筆者の環境ではレビュー対象のブランチが取得できず、そのままではレビューを開始できませんでした。リモートを作らずに試したい場合は回避策が必要なので、別記事にまとめました。
ブランチの比較は GitHub でも GitLab でも動作します(後述の Issue 検証だけが GitHub 限定です)。
手順 2: ローカルリポジトリを初期化してリモートに push する
リモートリポジトリと同じ名前のローカルフォルダ(260817reviewtest)を作り、そこに sample_shain_select.py を置いた状態から始めます。このディレクトリで以下を順に実行します(PowerShell で実行しました)。
git init
git add .
git commit -m "first commit"
git branch -M main
git remote add origin https://github.example.com/your-account/260817reviewtest.git
git push -u origin main
git remote set-head origin -a
各コマンドの意味は以下のとおりです。
| コマンド | 何をしているか |
|---|---|
git init |
ローカル Git リポジトリを初期化 |
git add . |
サンプルプログラムをステージング |
git commit -m "first commit" |
最初のコミットを作成 |
git branch -M main |
ブランチ名を main に変更 |
git remote add origin ... |
リモートリポジトリを origin として登録 |
git push -u origin main |
リモートへ push し、上流ブランチを設定 |
git remote set-head origin -a |
リモートの既定ブランチ(origin/HEAD)を自動判定して設定 |
実際の実行結果
実行時のログは次のようになりました。
PS ...\260817reviewtest> git init
Initialized empty Git repository in .../260817reviewtest/.git/
PS ...\260817reviewtest> git add .
PS ...\260817reviewtest> git commit -m "first commit"
[master (root-commit) fa53188] first commit
1 file changed, 30 insertions(+)
create mode 100644 sample_shain_select.py
PS ...\260817reviewtest> git branch -M main
PS ...\260817reviewtest> git remote add origin https://github.example.com/your-account/260817reviewtest.git
PS ...\260817reviewtest> git push -u origin main
Enumerating objects: 3, done.
Counting objects: 100% (3/3), done.
Writing objects: 100% (3/3), 600 bytes | 300.00 KiB/s, done.
Total 3 (delta 0), reused 0 (delta 0), pack-reused 0
To https://github.example.com/your-account/260817reviewtest.git
* [new branch] main -> main
branch 'main' set up to track 'origin/main'.
PS ...\260817reviewtest> git remote set-head origin -a
origin/HEAD set to main
ログから読み取れるポイントを 3 つ挙げます。
1. 最初のコミットは master に作られる
PS ...\260817reviewtest> git commit -m "first commit"
[master (root-commit) fa53188] first commit
git init の既定ブランチ名が master になっている環境なので、コミット直後は master です。だからこそ次の git branch -M main でリネームが必要になります。すでに main で初期化される設定(init.defaultBranch=main)の環境なら、この行は実質何もしません。
2. 上流ブランチが設定される
PS ...\260817reviewtest> git push -u origin main
* [new branch] main -> main
branch 'main' set up to track 'origin/main'.
-u を付けているので、ローカル main が origin/main を追跡する設定まで入ります。これによってレビュー時のブランチ選択で origin/main が扱いやすくなります。
3. origin/HEAD が設定される
PS ...\260817reviewtest> git remote set-head origin -a
origin/HEAD set to main
git remote set-head origin -a の出力です。次の節で詳しく触れます。
git remote set-head origin -a は忘れずに
最後の 1 行について補足します。
ドキュメント上、レビューの前提条件は「比較したいローカル/リモートブランチにアクセスできること」だけで、リモートの設定が必須とは書かれていません。ただ、このコマンドで origin/HEAD を実際のデフォルトブランチ(ここでは main)に向けておくと、後述するブランチ選択に origin/HEAD が候補として現れます。 実行していなければ origin/HEAD というリモート参照自体が存在しないため、選択肢に出てきません。
なお git push だけではこの参照は作られません(git clone した場合は自動で作られます)。Bob は既定ブランチをこの参照から判定しているとみられ、参照が無いとブランチ一覧そのものが取得できないことがあるため、git init から始めた場合は実行しておくのが無難です。
実行すると、設定結果がそのまま出力されます。
$ git remote set-head origin -a
origin/HEAD set to main
あとから確認したい場合は git remote show origin を使います。
$ git remote show origin
* remote origin
Fetch URL: https://github.example.com/your-account/260817reviewtest.git
Push URL: https://github.example.com/your-account/260817reviewtest.git
HEAD branch: main
Remote branch:
main tracked
Local branch configured for 'git pull':
main merges with remote main
Local ref configured for 'git push':
main pushes to main (up to date)
HEAD branch: main と表示されていれば OK です。あわせて main tracked と main pushes to main (up to date) も確認できるので、リモートとの紐づけが完了しているかどうかをこの 1 コマンドで把握できます。
手順 3: レビューパネルを開く
サイドバーから Bob のレビューパネルを開きます。開き方は 3 通りあります。
- StartWorkflowアイコンをクリックして、レビューパネルを開く
- チャットで
/レビューコマンドを実行する - サイドバーの ソース管理からReview パネルのアイコンをクリックする
パネルが開くと、比較対象のリポジトリとブランチが表示されます。
ここで開く「レビューパネル」はサイドバー側にあり、レビューを開始するための画面です。レビュー結果が出る BOB FINDINGS は下部のパネル領域に表示される別のパネルなので、混同しないよう注意してください。
パネルに何も出てこないときは
筆者の環境では、パネルを開いた直後はリポジトリ情報が反映されておらず、ウィンドウを再読み込みしたら表示されたということがありました。
手順は次のとおりです。
-
Ctrl + Shift + Pでコマンドパレットを開く - 開発者: ウィンドウの再読み込み(Developer: Reload Window) を実行する
再読み込みが終わると、パネルにレビュー対象のリポジトリとブランチが表示されます。
Bob 固有の機能ではなく VS Code 標準のウィンドウ再読み込みなので、拡張機能の状態を作り直したいとき全般に使えます。ドキュメントにもレビュー用のショートカットは記載されていないため、表示がおかしいときの定番の対処として覚えておくとよさそうです。
それでもリポジトリやブランチが出ない場合は、
-
git remote -vでリモートが登録されているか -
git remote show originでHEAD branchが設定されているか - リモートへの push が完了しているか
-
git symbolic-ref refs/remotes/origin/HEAD --shortが値を返すか
を順に確認するのが早いです。
特に最後の項目が要注意です。ここが空(コマンドが失敗する)だと、ブランチ一覧の取得までたどり着けないことがあります。git init から始めたリポジトリでは、手順 2 の git remote set-head origin -a を実行するまでこの参照は作られません。リモートを登録していない場合は上記の別記事の回避策を参照してください。
ブランチ選択ドロップダウンの中身
「レビュー対象」のドロップダウンから比較対象を選びます。ブランチ検索のボックスも付いているので、数が多くなっても絞り込めます。
ドキュメントによると、ここには次の 4 種類が並びます(後者 3 つにはバッジが付きます)。
| 項目 | 内容 |
|---|---|
| 現在のブランチ | アクティブなブランチ |
| デフォルトブランチ | リポジトリのデフォルトブランチ(バッジで表示) |
| リモートブランチ | リモートリポジトリのブランチ(バッジで表示) |
| ローカルブランチ | その他のローカルブランチ(バッジで表示) |
今回のリポジトリでは、実際には次の 3 つが表示されました。
| 表示 | バッジ | 対応する分類 |
|---|---|---|
main |
現在 | 現在のブランチ |
origin/main |
origin | リモートブランチ |
origin/HEAD |
origin | デフォルトブランチ |
ローカルブランチが出てこないのは、ローカルに main しかなく、それが現在のブランチだからです。git checkout -b feature などでブランチを増やせば、4 つ目のグループとして現れます。
そして origin/HEAD が候補に出ているのは、手順 2 の最後に git remote set-head origin -a を実行したからです。このコマンドを省くとこの項目は現れません。なお origin/HEAD は origin/main へのシンボリック参照なので、今回の構成ではどちらを選んでも同じコミットとの比較になります。ブランチ運用が複雑になってきたときに「既定ブランチ」を名前で指定できるのが利点です。
任意の組み合わせで比較できるのがポイントです。たとえば「リモートの main とローカルの main」といった比較も可能なので、「push 前の手元の状態が、リモートの最新とどう違うか」をそのまま検証できます。今回のデモでも origin/main を選び、手元の未コミット変更と比較させています。
コミットされていない変更を含める(未コミットの変更を含める)
レビュー開始前に コミットされていない変更を含める のチェックボックスがあります。これを有効にすると、まだコミットしていないローカルの編集もレビュー対象に入ります。
そして重要なのが、レビュー対象が現在のブランチの場合は自動で有効になるという点です。この記事のあとの手順では「ファイルを書き換えて、コミットせずにそのままレビュー」という流れをとりますが、それが成立するのはこの挙動のおかげです。コミットしてから push して…という手間なしに、書きかけのコードをレビューにかけられます。
(オプション)GitHub Issue を紐づける
GitHub の Issue を選択しておくと、変更が Issue の要件を満たしているかどうかまで Bob が確認してくれます。実装漏れのチェックに使える機能です。
- 利用には GitHub アカウントと Issue の URL が必要です
- この機能は GitHub 限定です(GitLab ではブランチ比較のみ)
PR を出す前に「要件を全部カバーできているか」を見てもらう、という使い方がドキュメントでも推奨されています。なお、今回の検証では使っていません。
(オプション)レビュー対象から除外する
Bob設定 → Bob所見 タブ → レビュー除外 で、glob パターンによる除外を設定できます。
.vscode/**
*.test.ts
除外したファイルはパネルのファイル一覧にも出てこなくなり、分析自体もスキップされます。生成コード・テスト・設定ファイルなど、レビューしても意味が薄いものを外しておくと、指摘のノイズが減ります。
手順 4: わざと SQL インジェクションを仕込む
ここからが本題です。レビューが本当に効くのかを確かめるため、意図的に脆弱なコードを追加します。
main() の中に、f-string で SQL を組み立てる処理を追加します。
import sqlite3
def main() -> None:
conn = sqlite3.connect("sample.db")
cursor = conn.cursor()
cursor.execute(
"create table if not exists shain (id integer primary key, name text)"
)
cursor.execute("delete from shain")
cursor.execute("insert into shain (id, name) values (?, ?)", (1, "田中"))
cursor.execute("insert into shain (id, name) values (?, ?)", (2, "佐藤"))
target_id = 1
sql_preview = f"select name from shain where id = {target_id}"
cursor.execute(sql_preview)
row = cursor.fetchone()
print(row[0] if row else "該当なし")
# target_id = 1
# cursor.execute("select name from shain where id = ?", (target_id,))
# row = cursor.fetchone()
# print(row[0] if row else "該当なし")
conn.commit()
conn.close()
if __name__ == "__main__":
main()
今回は target_id がリテラルなので実害はありませんが、sql_preview = f"select name from shain where id = {target_id}"は**「SQL 文を文字列連結で組み立てている」という典型的な SQL インジェクションのパターン**そのものです。外部入力が入るようになった瞬間に脆弱性になります。
このコードが指摘されるかどうかが、レビューワークフローの実力を見るポイントになります。
なお、この変更はコミットしていません。前述のコミットされていない変更を含めるが効くので、そのままレビューにかけられます。
手順 5: レビューを実行する
レビュー対象に origin/main を選び、レビューを開始 を押します。これで「リモートの最新の状態」と「手元の未コミット変更」が比較されます。
しばらく待つと結果が返ってきて、期待どおり SQL インジェクションを検出してくれました。
レビュー結果は基本的には英語で出力されます。 その場合はチャット欄に
日本語で
と入力すれば、そのまま日本語で回答してくれます。
指摘は BOB FINDINGS パネルに一覧表示されます。このパネルは「問題」「出力」「デバッグ コンソール」「ターミナル」と同じ下部のパネル領域にタブとして並びます(サイドバーではありません)。タブにはバッジが付くので、指摘があることに気づきやすくなっています。
今回検出された指摘は次のように表示されました。
ERROR
SQL Injection via f-string query construction
場所 .../sample_shain_select.py:16
:16 は仕込んだ f-string の行に対応しています。
指摘をクリックすると、
- 該当ファイルへジャンプする
- 前後のコードと合わせてインライン注釈で表示される
- 詳細(なぜ危険なのか、どう直すべきか)が読める
という形で確認できます。今回は「文字列連結で SQL を構築している」ことと「パラメータ化クエリを使うべき」という内容でした。
結果を日本語にする
指摘事項のものを日本語で読み直したい場合は、Ask モードに切り替えて「日本語で」と依頼するのが確実です。レビュー直後のチャットには指摘の内容が残っているので、それだけで日本語に置き換えてくれます。
指摘を絞り込む・グループ化する
BOB FINDINGS パネルには、指摘を整理するための機能が揃っています(ドキュメントには記載がないため、以下は UI 上で確認した内容です)。
検索
「検出結果を検索」のボックスにキーワードを入れて絞り込めます。
重要度でフィルタ
「すべての重要度」のドロップダウンから、表示する重要度を切り替えられます。指摘が多いときに重大なものだけ残す、といった使い方ができます。
グループ化
グループ化の軸は 3 種類から選べます。
| グループ化 | 用途 |
|---|---|
| ファイル別にグループ化 | ファイル単位でまとめて直したいとき |
| タイプ別にグループ化 | 同種の問題(SQL インジェクションなど)を横断で潰したいとき |
| 重要度別にグループ化 | 重大なものから優先的に対応したいとき |
今回は重要度別にグループ化して確認したところ、仕込んだ SQL インジェクションが最上位の ERROR グループに入っていました。指摘が多数出た場合でも、この表示なら「まず何から直すべきか」が一目で判断できます。
同じ脆弱性が複数ファイルに散っているようなケースでは「タイプ別」、レビュー対象のファイルを 1 つずつ片付けたいときは「ファイル別」が便利です。
手順 6: 指摘を Bob に修正させる
各指摘には 2 つのアクションが用意されています。
| アクション | 内容 |
|---|---|
| Bobで修正 | その指摘を解決するタスクを起動し、自動で修正させる |
| 閉じる | 不要な指摘を一覧から消す |
今回は Bobで修正 で修正を依頼しました。すると、文字列連結だった箇所をパラメータ化クエリ(プレースホルダ)に書き換えてくれました。
target_id = 1
cursor.execute("select name from shain where id = ?", (target_id,))
row = cursor.fetchone()
print(row[0] if row else "該当なし")
f-string で組み立てていた SQL が、? プレースホルダと引数タプルに置き換わっています。これで値は SQL 文とは別経路で渡されるため、SQL インジェクションは成立しなくなります。
修正後はそのままテストの実行まで進めることもできますが、今回は検証が目的なのでスキップしました。
なお不要な検知だと判断した指摘は 閉じる で消せるので、一覧を「本当に対応すべきものだけ」に整理していく運用ができます。
まとめ
レビューワークフローを一通り試して、以下が確認できました。
-
レビューはブランチ間の差分に対して行われる
現在 / デフォルト / リモート / ローカル から自由に組み合わせて比較できます(該当するものだけが候補に出ます)。リモートブランチと比較すれば、最新のリモート状態に対する検証になります。git remote set-head origin -aをやっておくとorigin/HEADを既定ブランチとして選べます。 -
未コミットの変更もレビューできる
コミットされていない変更を含める が効き、現在のブランチが対象なら自動で有効になります。書きかけのコードをそのまま見てもらえます。 -
ブランチが表示されないときはウィンドウを再読み込みする
/reviewまたはサイドバーのアイコンから開き、出てこなければCtrl + Shift + P→「開発者: ウィンドウの再読み込み」。それでも駄目なら Git のリモート設定と、git symbolic-ref refs/remotes/origin/HEAD --shortが値を返すかを確認。 -
指摘は検索・フィルタ・グループ化で整理できる
BOB FINDINGS パネルで、重要度によるフィルタと、ファイル別/タイプ別/重要度別のグループ化が使えます。 -
検出から修正までシームレスにつながる
Bobで修正でパラメータ化クエリへの書き換えまで一気に完了します。不要な指摘は閉じるで整理。 -
Issue 連携と除外設定を組み合わせると実用度が上がる
GitHub Issue を紐づければ要件のカバー漏れまで確認でき、Review Exclusions で生成コードやテストを外せばノイズが減ります。
ドキュメントにもあるとおり、自動レビューは人間のレビューの前段(first pass)として使うものです。PR を出す前に一度通しておけば、レビュアーが指摘するはずだった内容を先に潰せるので、レビューの往復を減らせます。まずは小さなリポジトリで一度通してみると挙動が掴みやすいと思います。

















