CodeQL 2.26.2以降では、カスタムクエリのアラートメッセージに直接埋め込んだ [["text"|"url"]] 形式のリンクが解析されなくなりました。対象クエリは、メッセージ内の旧構文を $@ に置き換え、select 句へ「リンク先の要素」と「リンク表示文字列」をペアで追加する必要があります。
この変更で、脆弱性や問題を検出する条件まで直ちに動かなくなるとは限りません。しかし、アラートから関連コードへ移動するリンクが失われ、調査やトリアージの効率が下がります。旧構文は通常の文字列として記述されているため、クエリのコンパイルが成功しても、GitHub上ではリンクとして機能しない点に注意が必要です。
GitHubはCodeQL 2.26.2の破壊的変更として、非公開のレガシー機能だった [[ 形式のリンク解析を削除し、代わりに $@ プレースホルダーペアを使用するよう案内しています。(The GitHub Blog)
CodeQL 2.26.2でアラートリンク構文がどう変わったのか
今回廃止されたのは、select 句のアラートメッセージ文字列へ、次の形式でリンクを直接埋め込む方法です。
[["表示文字列"|"URL"]]
CodeQLのクエリファイル内では、引用符をエスケープした次のような文字列として記述されていることがあります。
"This value comes from [[\"the source\"|\"relative:///src/App.java:10:5:10:20\"]]."
CodeQL 2.26.2では、この文字列をリンクへ変換する処理が削除されました。今後は、メッセージ内に $@ を配置し、その後ろの結果列でリンク先と表示文字列を指定します。
| 項目 | 旧方式 | 現行方式 | |
|---|---|---|---|
| メッセージ内の記述 | [["text" | "url"]] | $@ | |
| リンク先の指定 | メッセージ文字列内のURL | select 句の要素列 | |
| リンク文字列の指定 | メッセージ文字列内 | select 句の文字列列 | |
| CodeQL 2.26.2での扱い | 解析されない | サポートされる | |
| 推奨度 | 非公開のレガシー構文 | 公式ドキュメント記載の方式 |
公式ドキュメントでは、アラートクエリの基本的な結果形式を select element, string とし、追加の「要素/文字列」ペアと $@ を組み合わせてリンクを生成すると説明しています。(CodeQL)
影響を受ける環境と対応の必要性
すべてのCodeQL利用者が、クエリを修正しなければならないわけではありません。主な判断基準は、独自のカスタムクエリや外部のクエリパックを使用しているかどうかです。
| 利用状況 | 影響 | 対応 |
|---|---|---|
| GitHub標準クエリだけを使用 | 旧構文の直接修正は通常不要 | 通常の回帰確認を実施 |
| 自社開発のカスタムクエリを使用 | [[ 構文があれば影響あり | $@ へ移行 |
| 社内共通のCodeQLパックを使用 | 共通パック内に旧構文がある可能性 | パック管理者が一括確認 |
| 外部ベンダーのクエリパックを使用 | 配布元の対応状況に依存 | 更新版やリリースノートを確認 |
| 独自CIでCodeQL CLIを実行 | CLI更新時に影響が顕在化 | CLIバージョンとクエリを確認 |
| GitHub Enterprise Serverを使用 | 搭載されるCodeQLバージョンに依存 | サーバー更新前に移行を完了 |
GitHub.comのcode scanningでは、新しいCodeQLバージョンが自動的に展開されます。一方、GitHub Enterprise Serverでは将来のリリースに含まれるほか、環境によってはCodeQLを手動更新できます。そのため、現在リンクが表示されていても、CLIやGHESの更新後に問題が表面化する可能性があります。(The GitHub Blog)
今回の確認対象は、主に次の場所です。
@kind problemのアラートクエリ@kind path-problemのパスクエリ.qlファイルのselect句- メッセージを生成する
.qllライブラリ - クエリを自動生成するテンプレートやスクリプト
通常のソースコードに含まれる [[、コメント内の文字列、クエリヘルプのMarkdownまで機械的に置換する必要はありません。変更対象は、アラート結果として解釈されるメッセージ文字列です。
カスタムクエリから旧[[構文を探す方法
最初に、管理しているCodeQLクエリ全体から [[ を検索します。
Git管理されたクエリリポジトリでは、次のコマンドを使用できます。
git grep -n '\[\[' -- '*.ql' '*.qll'
ripgrepを利用できる場合は、次の方法でも検索できます。
rg -n '\[\[' --glob '*.ql' --glob '*.qll' .
Windows PowerShellでは、次のように検索します。
Get-ChildItem -Recurse -File -Include *.ql,*.qll |
Select-String -Pattern '\[\['
検索結果が見つかったら、該当箇所が次のいずれに当たるかを確認します。
select句のメッセージへ直接書かれている- 文字列連結で旧リンクを組み立てている
- メッセージ生成用の述語が旧リンク文字列を返している
- 単なるコメントやテストデータに含まれている
特に見落としやすいのが、.qll 側でメッセージを生成しているケースです。
string getAlertMessage(Expr source) {
result = "Data comes from [[\"" + source.toString() + "\"|\"...\"]]."
}
クエリ本体が次のようになっている場合、.ql の select 句だけを確認しても旧構文を発見できません。
select sink, getAlertMessage(source)
組織共通パックでは、クエリ本体だけでなく、共有ライブラリやコード生成元まで検索することが重要です。
$@プレースホルダーペアの仕組み
@kind problem のアラートクエリでは、基本的に次の構造でリンクを定義します。
select
alertElement,
"Alert message with $@.",
linkedElement,
"link text"
各列の役割は次のとおりです。
| 列 | 内容 |
|---|---|
| 1列目 | アラートを表示するコード要素 |
| 2列目 | $@を含むアラートメッセージ |
| 3列目 | $@のリンク先となるコード要素 |
| 4列目 | リンクとして表示する文字列 |
たとえば、Javaクラスとスーパークラスの関係を表示するクエリは、次のように記述できます。
/**
* @kind problem
*/
import java
from Class c, Class superclass
where superclass = c.getASupertype()
select
c,
"This class extends the class $@.",
superclass,
superclass.getName()
この例では、アラート自体は c に表示されます。メッセージ内の $@ は、superclass の定義位置へ移動するリンクとして表示され、リンク文字列には superclass.getName() の結果が使用されます。
CodeQL公式ドキュメントでも、$@ の後ろに要素と文字列の2列を追加する構造が示されています。リンク先となる要素に有効なソース位置がない場合、リンクをクリックしても移動できないことがあります。(CodeQL)
[[構文から$@へ移行する具体例
1つのリンクを移行する場合
旧メッセージが次の形式だったとします。
This class extends [["BaseClass"|"relative:///src/BaseClass.java:10:1:20:1"]].
新しい方式では、URLをメッセージ文字列として組み立てません。リンク先となるCodeQL要素を結果列へ渡します。
select
c,
"This class extends the class $@.",
superclass,
superclass.getName()
移行時の重要な点は、単に次のように文字列を置換するだけでは不十分なことです。
select c, "This class extends $@."
この状態では、$@ に対応する要素列と文字列列がありません。メッセージ上に $@ がそのまま残る可能性があります。
必ず次のペアを追加します。
superclass,
superclass.getName()
複数のリンクを含める場合
1つのメッセージに複数の関連箇所を表示する場合は、左から順に $@ とペアを対応させます。
select
sink,
"Data flows from $@ to $@.",
source,
"this source",
sink,
"this sink"
対応関係は次のとおりです。
| プレースホルダー | リンク先 | 表示文字列 |
|---|---|---|
1つ目の $@ | source | this source |
2つ目の $@ | sink | this sink |
プレースホルダーよりペアが多い場合、余った列は無視されます。反対に、ペアより $@ が多い場合、対応できなかった $@ は通常の文字列として扱われます。(CodeQL)
次のように、順序を取り違えるとリンク先と表示内容が一致しません。
select
sink,
"Data flows from $@ to $@.",
sink,
"this source",
source,
"this sink"
コンパイルが成功しても、利用者を誤った場所へ誘導する可能性があるため、リンクをクリックするところまで確認する必要があります。
@kind path-problemで移行する場合
パスクエリでは、アラート位置、始点、終点、メッセージという必須列の後ろに、プレースホルダーペアを追加します。
select
sink.getNode(),
source,
sink,
"This path depends on a $@.",
source.getNode(),
"user-provided value"
この例では、最初の4列がパスクエリ本来の結果です。
| 列 | 内容 |
|---|---|
| 1列目 | アラート位置 |
| 2列目 | パスの始点 |
| 3列目 | パスの終点 |
| 4列目 | アラートメッセージ |
| 5列目 | $@ のリンク先 |
| 6列目 | $@ の表示文字列 |
データフローの PathNode をそのままリンク先にできない場合は、標準クエリでよく使われているように source.getNode() などで元のコード要素を取得します。
パスクエリのメッセージについても、通常のアラートクエリと同じ考え方で $@ を利用できます。(CodeQL)
外部ドキュメントへのリンクを埋め込んでいた場合
旧[[構文で、ソースコードではなく社内Wikiや修正手順書へリンクしていたケースでは、単純な $@ 置換ができないことがあります。
$@ のリンク先には、基本的にソース位置を持つCodeQL要素を指定します。外部URLを無理にコード要素として扱うのではなく、役割を次のように分ける方法が安全です。
- アラートメッセージでは、問題の内容と関連コードへのリンクを表示する
- 詳しい修正手順や外部資料は、クエリヘルプへ記載する
- クエリヘルプのMarkdownをSARIFへ含め、GitHubのcode scanning画面から参照できるようにする
CodeQLのカスタムクエリでは、クエリと同じ場所にMarkdown形式のドキュメントを用意できます。クエリヘルプを含むSARIFをGitHubへアップロードすると、アラート画面に説明や修正方法を表示できます。(GitHub Docs)
また、GitHubのクエリスタイルガイドでは、アラートメッセージは問題を簡潔かつ事実ベースで説明し、関連するプログラム要素には $@ でリンクすることが推奨されています。「ここをクリック」のような曖昧なリンク文字列ではなく、リンク先が分かる具体的な文言を使うことも重要です。(GitHub)
移行時に失敗しやすいポイント
| 失敗例 | 発生する問題 | 対処 |
|---|---|---|
[[...]]を$@へ文字置換しただけ | 対応するリンク列がなく、$@が残る | 要素と表示文字列の2列を追加 |
| 3列目に文字列を指定した | 正しい結果パターンにならない | 3列目は位置を持つ要素にする |
$@とペアの数が一致しない | 一部のリンクが表示されない | 左から順に個数を照合 |
| ペアの順序が逆 | 表示文字列とリンク先が食い違う | メッセージの出現順で並べる |
| 位置を持たない要素を指定 | クリックしても移動しない | AST要素やgetNode()を使用 |
.qlだけを検索した | .qllのメッセージ生成処理を見落とす | ライブラリとテンプレートも検索 |
| コンパイルだけで完了と判断 | UI上のリンク切れを発見できない | SARIF生成後に画面で確認 |
外部URLを無理に$@へ移行 | 適切なリンク先要素を用意できない | 詳細URLをクエリヘルプへ移す |
すべての[[を一括置換 | コメントやテストデータまで変更する | select結果に使われる文字列だけ修正 |
特に危険なのは、リンク先の要素を追加したことでクエリの行数が増えるケースです。
たとえば、リンク先候補が複数存在する条件になっていると、同じアラート位置に対して複数の結果が生成される可能性があります。移行前後でアラート件数を比較し、不要な重複が増えていないことも確認してください。
CodeQL 2.26.2対応を検証する手順
CodeQL CLIのバージョンを確認する
ローカル環境や独自CIでは、最初に実行中のCodeQL CLIを確認します。
codeql version --format=terse
codeql version は、CodeQLツールチェーンのバージョンを表示する公式コマンドです。(GitHub Docs)
複数のCIランナーや開発端末がある場合は、端末ごとに確認してください。VS Code拡張機能が独自に管理するCLIと、ターミナルから実行するCLIが異なることもあります。
クエリをコンパイルチェックする
修正したクエリを、CodeQL 2.26.2以降のCLIで検証します。
codeql query compile --check-only -- path/to/query-pack
単一ファイルだけを確認する場合は、クエリファイルを直接指定します。
codeql query compile --check-only -- path/to/CustomQuery.ql
--check-only を使用すると、クエリプランを保存せず、QLコードの妥当性を短時間で確認できます。(GitHub Docs)
ただし、コンパイル成功だけではリンクの有効性を確認できません。リンク先の要素にソース位置がなくても、型や結果形式が成立する場合があるためです。
カスタムクエリのテストを実行する
テストパックを用意している場合は、次のコマンドを実行します。
codeql test run path/to/test-pack
個別のテストディレクトリも指定できます。
codeql test run java/tests/CustomQuery
CodeQLのテストフレームワークは、期待する結果と実際の結果を比較します。CLI更新による破壊的変更を本番のcode scanningへ反映する前に検出する用途にも適しています。(GitHub Docs)
SARIFを生成して最終確認する
実際のCodeQLデータベースに対してクエリを実行し、SARIFを生成します。
codeql database analyze \
path/to/database \
path/to/CustomQuery.ql \
--format=sarif-latest \
--output=codeql-results.sarif
クエリパック全体を確認する場合は、クエリファイルの代わりにパックやクエリスイートを指定します。
SARIF生成後は、テスト用ブランチや検証用リポジトリで次の項目を確認します。
- アラートメッセージに
$@が残っていない - リンク文字列が意図した内容になっている
- クリックすると正しいソース位置へ移動する
- アラートの主位置が移行前と一致している
- アラート件数が不自然に増減していない
- パスクエリの始点と終点が正しく表示される
- 同じ場所に重複アラートが生成されていない
CodeQL CLIでは、database analyze によってクエリを実行し、GitHubへアップロードできるSARIF結果を生成できます。(GitHub Docs)
組織でカスタムクエリを管理している場合の進め方
複数のリポジトリで共通のCodeQLパックを使用している場合、各リポジトリで個別修正するのではなく、配布元のパックを修正します。
安全な展開順序は次のとおりです。
- 共通クエリパック全体から
[[を検索する - リンク先として使用していたURLが、どのコード要素を指していたか確認する
$@と要素/文字列ペアへ書き換える- CodeQL 2.26.2以降でコンパイルとテストを実行する
- 代表的な言語とリポジトリでSARIFを生成する
- GitHub画面でリンク動作を確認する
- 修正版のクエリパックを新しいバージョンとして公開する
- 利用側の依存バージョンやロックファイルを更新する
- CIで旧
[[構文を検出するチェックを追加する
再発防止として、次のようなCIチェックをクエリパックへ追加しておくと効果的です。
if git grep -n '\[\[' -- '*.ql' '*.qll'; then
echo "Legacy CodeQL alert link syntax was found."
exit 1
fi
ただし、テストデータとして意図的に旧構文を保存する場合は、対象ディレクトリを除外するか、許可リストを用意してください。
まとめ:まずクエリソースの[[検索から始める
CodeQL 2.26.2では、アラートメッセージ内の非公開リンク構文 [["text"|"url"]] が解析されなくなりました。旧構文を利用しているカスタムクエリは、メッセージ中を $@ に変更するだけでなく、select 句へリンク先のコード要素とリンク表示文字列をペアで追加する必要があります。
最初に .ql と .qll を [[ で検索してください。該当箇所が見つかったら、URL文字列を手作業で生成する実装をやめ、実際のAST要素、クラス、メソッド、式、データフローノードなどをリンク先として指定します。
修正後は、コンパイルチェックだけで完了とせず、クエリテスト、SARIF生成、GitHub画面でのクリック確認まで実施します。外部の修正手順や社内ドキュメントへ誘導していたリンクは、アラートメッセージへ無理に残さず、クエリヘルプへ移すと保守しやすくなります。

コメント