GitHub CodeQLの2026年4月更新ポイント:models-as-dataでサニタイザとバリデータに対応

GitHub CodeQLの2026年4月更新で最も重要なのは、models-as-dataでサニタイザとバリデータを定義できるようになったことです。これにより、社内フレームワークや独自ライブラリの「この関数を通れば安全」「この検証に通った値だけ使える」といった知識を、カスタムQLを書かずにYAMLでCodeQL分析へ反映しやすくなりました。GitHubは2026年4月21日のChangelogで、C/C++、C#、Go、Java/Kotlin、JavaScript/TypeScript、Python、Ruby、Rust向けに、この機能をCodeQL 2.25.2以降で利用できると発表しています。(The GitHub Blog)

CodeQLのアラートに対して「実際には自社のサニタイズ関数を通しているのに警告される」「バリデーション済みの分岐なのにtaint flowが止まらない」と感じていた開発者、DevOpsエンジニア、platform teamにとって、今回の更新は実務上かなり大きい変更です。単なる検出ルール追加ではなく、組織固有の安全性判断をCodeQLのデータフロー解析に載せやすくなったと考えると分かりやすいでしょう。

目次

GitHub CodeQLの最新動向: models-as-dataで何が変わったか

今回の「CodeQL adds sanitizers and validators support in models-as-data」は、CodeQLのtaint trackingをカスタマイズする方法を広げる更新です。

これまでCodeQLで独自のサニタイザやバリデータを正確に扱うには、カスタムCodeQLコードを書く必要がありました。2026年4月の更新後は、YAMLのdata extensionファイルにモデルを追加することで、barrierModelとbarrierGuardModelを宣言できます。GitHubの説明では、サニタイザはbarrier、バリデータはbarrier guardとして扱われます。(The GitHub Blog)

項目これまで2026年4月更新後
独自サニタイザの扱いカスタムQLが必要になりやすいbarrierModelでYAML定義できる
独自バリデータの扱い分岐条件を表現するにはQL実装が必要になりやすいbarrierGuardModelで条件付きの安全判定を表現できる
導入しやすさCodeQLに詳しい担当者が必要セキュリティチームと開発チームがモデルを共同管理しやすい
platform teamでの展開リポジトリごとの個別対応になりがちmodel packとして組織横断で共有しやすい
主な効果検出精度の調整に手間がかかるfalse positive削減と社内ライブラリ対応を進めやすい

重要なのは、これは「CodeQLが自動的にすべての独自関数を安全と判断する」機能ではない点です。安全とみなす関数・メソッド・条件を、開発組織側が明示的にモデル化します。つまり、便利になる一方で、誤ったモデルを入れると本来出るべきアラートまで消える可能性があります。

models-as-dataとは何か

models-as-dataは、CodeQLの分析に必要なライブラリモデルを、CodeQLコードではなくデータとして表現する考え方です。実体としては、YAML形式のdata extensionsを使い、ソース、シンク、フローサマリ、今回追加されたbarrierやbarrier guardなどを定義します。

GitHub Docsでは、model packsは標準ライブラリで認識されていないカスタムライブラリやフレームワークをCodeQL分析に認識させるために使えると説明されています。model packはdata extensionsを含み、指定された分析に追加モデルを自動的に反映できます。(GitHub Docs)

たとえば、社内に次のような共通関数があるケースを考えます。

const safeHtml = escapeForHtml(userInput);
document.body.innerHTML = safeHtml;

実際にはescapeForHtmlがHTMLエスケープを適切に行っているとしても、CodeQLがその関数を知らなければ、ユーザー入力がinnerHTMLへ流れているように見える場合があります。models-as-dataを使うと、「escapeForHtmlの戻り値は特定のXSS系クエリではtaint flowを止めてよい」といったモデルを追加できます。

sanitizerとvalidatorの違いを理解する

今回の更新を正しく使うには、sanitizerとvalidatorを分けて考える必要があります。

sanitizerは値そのものを安全な形に変換する

sanitizerは、危険な入力を安全に使える形へ変換する関数です。CodeQLではbarrierとして表現され、barrierModelで定義します。

典型例は次のようなものです。

脆弱性の種類sanitizerの例期待する扱い
XSSHTMLエスケープ関数エスケープ後の戻り値はHTML出力で安全とみなす
URL redirect許可済みURLへ正規化する関数正規化後の値だけを安全とみなす
Command injection引数を安全な形式へエンコードする関数対象コマンドの文脈でのみ安全とみなす

ただし、サニタイズは文脈依存です。HTML本文に安全な値が、JavaScript文字列、HTML属性、URLパラメータでも安全とは限りません。barrierModelを広く定義しすぎると、実際の脆弱性を見落とすリスクがあります。

validatorは条件を満たした場合だけ安全と判断する

validatorは、入力値を変換するのではなく「この値は条件を満たしているか」を判定する関数です。CodeQLではbarrier guardとして表現され、barrierGuardModelで定義します。

たとえば次のようなコードです。

if (isValid(userInput)) {
  db.query(userInput);
}

この場合、isValid(userInput)がtrueを返す分岐内だけ、userInputを安全とみなしたい場面があります。barrierGuardModelでは、どの引数が検証対象か、どの戻り値なら受け入れ条件か、どのquery kindに適用するかを指定します。GitHubのJavaScript向けガイドでも、barrierGuardModel(type, path, acceptingValue, kind)として定義する例が示されています。(codeql.github.com)

モデル使う場面例注意点
barrierModel関数の戻り値などが安全化されるescape(input)の戻り値文脈ごとの安全性を確認する
barrierGuardModel条件分岐で安全性が保証されるif (isValid(input))の中trueとfalseを取り違えない
sourceModel新しいtaint sourceを追加する独自HTTP request wrapper今回の主役ではないが併用されやすい
sinkModel新しい危険な利用先を追加する独自SQL実行関数barrierだけ入れてsinkを忘れない
summaryModel関数を通るデータフローを表現するwrapper関数の引数から戻り値sanitizerとは逆にflowをつなぐ

実務で効くユースケース

社内共通ライブラリのfalse positiveを減らす

最も分かりやすい活用シーンは、社内共通のサニタイズ関数です。

たとえば、Webアプリケーション基盤チームがescapeHtml()、sanitizeUrl()、validateRedirectUrl()のような関数を提供している場合、各リポジトリで同じCodeQLアラートが繰り返し発生することがあります。これを個別にdismissするのではなく、model packとして定義すれば、分析側に「この関数はこの文脈では安全性を担保する」と伝えられます。

ただし、まずは本当にfalse positiveかを確認してください。単に「アラートが多いから消したい」という目的でbarrierを追加すると、CodeQLの価値を下げてしまいます。

独自フレームワークのセキュリティ仕様をCodeQLに教える

大規模組織では、Express、Django、Springなどの標準フレームワークだけでなく、社内独自のAPIゲートウェイ、認可ミドルウェア、入力検証レイヤーを使うことがあります。

CodeQLがそれらを標準で把握していない場合、taint sourceやsinkの追加だけでなく、今回追加されたsanitizerとvalidatorのモデル化が重要になります。

たとえば、次のような設計です。

社内仕様CodeQLで表現したい内容
HtmlSafeString.from(input)はHTMLエスケープ済み文字列を返すbarrierModelで戻り値をbarrierにする
UrlPolicy.isAllowed(url)がtrueならリダイレクト可能barrierGuardModelで許可分岐を表現する
db.safeQuery(sqlTemplate, params)はプレースホルダ付きで実行する必要に応じてsinkやsummaryと組み合わせて確認する
RequestContext.currentUserInput()が外部入力を返すsourceModelと組み合わせる

platform teamが組織横断で展開する

platform teamやAppSecチームにとっては、model packを標準化できる点が大きなメリットです。GitHub Docsでは、default setupのリポジトリ単位では.github/codeql/extensions配下にmodel packを置く方法、組織全体では公開済みmodel packを指定して展開する方法が説明されています。(GitHub Docs)

個別リポジトリで場当たり的にアラートを閉じるより、モデルをバージョン管理し、レビューし、共通展開するほうが再現性があります。特にグローバル開発組織では、地域やチームごとに判断がぶれにくくなります。

具体例: barrierModelでサニタイザを定義する

JavaScript/TypeScriptの例では、encodeURIComponentの戻り値をHTML injection向けのbarrierとして扱う定義が示されています。形式は次のようなYAMLです。(codeql.github.com)

extensions:
  - addsTo:
      pack: codeql/javascript-all
      extensible: barrierModel
    data:
      - ["global", "Member[encodeURIComponent].ReturnValue", "html-injection"]

この定義では、3つの要素を指定しています。

要素意味
globalグローバルオブジェクトから対象を探す
Member[encodeURIComponent].ReturnValueencodeURIComponentの戻り値を対象にする
html-injection適用するbarrierの種類を指定する

実務では、encodeURIComponentをそのまま真似するのではなく、自社のサニタイズ関数がどの脆弱性文脈で安全なのかを確認してから定義します。

たとえば、社内関数escapeForHtmlがHTML本文への出力に限って安全なら、考え方としては次のようになります。

extensions:
  - addsTo:
      pack: codeql/javascript-all
      extensible: barrierModel
    data:
      - ["@my-org/security-utils", "Member[escapeForHtml].ReturnValue", "html-injection"]

このようなモデルを入れる前に、escapeForHtmlがHTML属性、URL、JavaScriptコンテキストでも安全なのかを確認してください。安全でない文脈までbarrierにすると、XSSの見落としにつながります。

具体例: barrierGuardModelでバリデータを定義する

validatorは、条件分岐とセットで意味を持ちます。JavaScriptの例では、isValid(userInput)がtrueを返す場合に、SQL injection向けのtaint flowを止めるモデルが示されています。(codeql.github.com)

extensions:
  - addsTo:
      pack: codeql/javascript-all
      extensible: barrierGuardModel
    data:
      - ["my-package", "Member[isValid].Argument[0]", "true", "sql-injection"]

この定義で特に重要なのは、3列目の"true"です。これは「この戻り値の場合に安全とみなす」という受け入れ条件を表します。

Pythonの例では、Djangoのurl_has_allowed_host_and_schemeがtrueを返す場合に、URL redirection向けのbarrier guardとして扱うモデルが示されています。(codeql.github.com)

extensions:
  - addsTo:
      pack: codeql/python-all
      extensible: barrierGuardModel
    data:
      - [
          "django",
          "Member[utils].Member[http].Member[url_has_allowed_host_and_scheme].Argument[0,url:]",
          "true",
          "url-redirection",
        ]

実務でよくある失敗は、「名前がvalidateだから安全」と判断してしまうことです。たとえば、validateLength(input)が文字数だけを見ている場合、SQL injectionやXSSの安全性は保証しません。そのような関数をbarrierGuardModelに登録してはいけません。

導入前に確認すべき判断基準

models-as-dataでsanitizerやvalidatorを追加する前に、次の観点で棚卸ししてください。

判断ポイント確認することNG例
安全性の根拠セキュリティレビュー済みの関数か実装を見ずに関数名だけで判断する
適用文脈XSS、SQL injection、URL redirectなど、どの種類に効くかすべての脆弱性に効くと広く定義する
戻り値と引数安全なのは戻り値か、引数か、条件分岐内の値かArgument[0]とReturnValueを取り違える
条件値validatorでtrueとfalseのどちらが安全側か否定条件を見落とす
影響範囲どのリポジトリ、言語、query kindに反映されるか組織全体に未検証モデルを配布する
検証方法導入前後でアラート差分を確認できるかいきなり本番のcode scanningに反映する

特に、platform teamが全社展開する場合は、最初から組織全体へ適用するのではなく、代表的な数リポジトリで差分を確認するのが安全です。false positiveが減るだけでなく、本来残すべきアラートが消えていないかを確認してください。

導入手順: 開発チームとplatform team向け

実務では、次の順序で進めると失敗しにくくなります。

手順作業担当の目安
1CodeQLアラートから、同じ独自関数に起因するfalse positiveを抽出する開発チーム、AppSec
2対象関数が本当にサニタイザまたはバリデータかレビューするAppSec、ライブラリオーナー
3barrierModelまたはbarrierGuardModelで最小限のモデルを書く開発チーム、platform team
4小さな検証用リポジトリまたは代表リポジトリで差分を見るDevOps、platform team
5model packとしてバージョン管理するplatform team
6リポジトリ単位、または組織単位で展開するplatform team
7CodeQLやquery packの更新時にモデルを再評価するAppSec、platform team

CodeQL CLIでpublished model packを使う場合は、codeql database analyzeに--model-packsを指定できます。GitHub Docsでは、標準query packと組み合わせてmodel packの依存情報を分析に利用する例が示されています。(GitHub Docs)

codeql database analyze /codeql-dbs/my-company \
  --format=sarif-latest \
  --model-packs my-org/my-model-pack \
  --output=/temp/results.sarif \
  codeql/javascript-queries

default setupを使っている場合は、個別リポジトリでは.github/codeql/extensions配下にmodel packを置く方法があります。組織全体で使う場合は、GitHub Container registryに公開し、各リポジトリからアクセスできる状態にしてから設定する必要があります。(GitHub Docs)

導入時に失敗しやすいポイント

barrierを広く定義しすぎる

最も危険なのは、便利だからといってbarrierを広く定義することです。

たとえば、sanitize(input)という名前の関数があったとしても、それがHTML用なのか、SQL用なのか、URL用なのかは実装を見なければ分かりません。HTMLエスケープ関数をSQL injectionのbarrierにしてしまうと、明らかに誤ったモデルになります。

validatorの条件を逆にする

barrierGuardModelでは、受け入れ値を正しく指定する必要があります。isUnsafe(input)のように、危険な場合にtrueを返す関数を、trueで安全と定義してしまうと分析結果が壊れます。

関数名だけで判断せず、次のような条件分岐をコードで確認してください。

if (!isUnsafe(input)) {
  use(input);
}

このようなケースでは、単純に"true"を指定すればよいとは限りません。

query kindを確認しない

html-injection、sql-injection、url-redirectionなどのkindは、どのクエリにモデルを適用するかを決める重要な値です。対象言語のカスタマイズガイドでサポートされるkindを確認し、実際に減らしたいアラートと対応しているかを見てください。

model packのバージョン範囲を放置する

model packはextensionTargetsで対象query packとバージョン範囲を指定します。CodeQLやquery packを更新したときに、モデルが想定どおり注入されているか確認しないと、「モデルを書いたのに効かない」という状態になり得ます。GitHub Docsでも、model packは指定したextensionTargetsの条件に合うquery packへdata extensionsを注入すると説明されています。(GitHub Docs)

今回の更新をどう評価すべきか

今回のGitHub CodeQL更新は、単なる機能追加というより、CodeQLを組織の実コードに合わせて育てるための変更です。

特に効果が出やすいのは、次のような組織です。

組織・プロジェクトの状態期待できる効果
社内共通のセキュリティライブラリを使っている独自サニタイザやバリデータをCodeQLへ反映しやすい
CodeQLアラートの一部が繰り返しfalse positiveになるレビュー済みモデルによりトリアージ負荷を下げられる
複数リポジトリで同じフレームワークを使っているmodel packで横展開しやすい
AppSecチームとplatform teamがあるセキュリティ知識をYAMLモデルとして管理しやすい
グローバルに開発拠点がある判断基準をコード化し、地域差を減らしやすい

一方で、サニタイズやバリデーションの仕様が曖昧なプロジェクトでは、先にライブラリ仕様を整理するべきです。models-as-dataは、曖昧な安全性を自動で判断してくれる機能ではありません。正しいセキュリティ仕様を、CodeQLが理解できる形に変換する仕組みです。

まず取るべきアクション

GitHub CodeQLをすでに使っているチームは、最初に過去のcode scanningアラートを見直し、「同じ独自サニタイズ関数を通っているのに繰り返し検出される」パターンを探してください。次に、その関数がどの脆弱性文脈で本当に安全なのかをAppSecやライブラリオーナーと確認し、小さなmodel packでbarrierModelまたはbarrierGuardModelを試すのが現実的です。

platform teamは、いきなり全社展開するのではなく、代表リポジトリで導入前後のSARIFやGitHub code scanning alertsを比較し、消えるべきアラートだけが消えているかを確認しましょう。問題なければ、model packをバージョン管理し、default setupやCLIの--model-packsで展開範囲を広げます。

2026年4月のCodeQL更新は、独自ライブラリが多い開発組織ほど価値があります。アラートを単に減らすためではなく、組織のセキュリティ設計をCodeQL分析へ正しく伝えるための仕組みとして活用することが重要です。

この記事を書いた人

実務の現場で詰まりがちなポイントを地図にするITブログ「IT trip」を運営。Windows/Office(Teams・Excel)からSQL、サーバ運用、ガジェットまで、再現性のある手順と“なぜそうなるか”を丁寧に解説します。読んだらすぐ試せること、そして迷った人の次の一歩が見えることを大切にしています。

コメント

コメントする

目次