データベースで CodeQL を実行する

完了

コードをデータベースに抽出したら、CodeQL クエリを使用して分析できるようになりました。 GitHub の専門家、セキュリティ研究者、コミュニティの共同作成者は、既定の CodeQL クエリを記述して維持します。 独自のクエリを記述することもできます。

コード スキャン分析で CodeQL クエリを使用して、ソース コードの問題を見つけ、潜在的なセキュリティの脆弱性を見つけることができます。 カスタム クエリを記述して、ソース コードで使用している各言語の問題を特定することもできます。

クエリには、次の 2 つの重要な種類があります。

  • アラート クエリでは 、コードの特定の場所での問題が強調表示されます。
  • パス クエリ は、コード内のソースとシンクの間の情報フローを記述します。

単純な CodeQL クエリ

基本的な CodeQL クエリ構造には、ファイル拡張子 .ql があり、 select 句が含まれています。 クエリ構造の例を次に示します。

/**
 *
 * Query metadata
 *
 */
import /* ... CodeQL libraries or modules ... */
/* ... Optional, define CodeQL classes and predicates ... */
from /* ... variable declarations ... /
where / ... logical formula ... /
select / ... expressions ... */

メタデータのクエリを実行する

コード スキャンで CodeQL を使用すると、クエリが検索するように設計されている潜在的な問題を強調する方法で結果が変換されます。 クエリには、結果の解釈方法を示すメタデータ プロパティが含まれています。 クエリ メタデータを使用して次の処理を行います。

  • カスタム クエリを GitHub リポジトリに追加するときに識別します。
  • クエリの目的に関する情報を指定します。

メタデータ情報には、クエリの説明、一意の ID、問題の種類 (アラートまたはパス) を含めることができます。 メタデータは、クエリ結果を解釈して表示する方法も指定します。

GitHub には、クエリ メタデータに推奨されるスタイル ガイドがあります。 CodeQL ドキュメントで確認できます。

これは、標準の Java クエリのメタデータの例です。

クエリ メタデータを示すスクリーンショット。

CodeQL では、メタデータを持たないクエリは解釈されません。 これらの結果はテーブルとして表示され、ソース コードには表示されません。

QL 構文

QL は、宣言型のオブジェクト指向クエリ言語です。 階層データ構造、特にソフトウェア成果物を表すデータベースを効率的に分析できるように最適化されています。

QL の構文は SQL に似ていますが、QL のセマンティクスは Datalog に基づいています。 Datalog は、クエリ言語としてよく使用される宣言型ロジック プログラミング言語です。 QL は主にロジック言語であるため、QL のすべての操作は論理操作です。 QL では、データ ログから再帰述語も継承されます。 QL では集計のサポートが追加され、複雑なクエリも簡潔でシンプルになります。

QL 言語は論理式で構成されます。 andornotなどの一般的な論理結合を、forallexistsなどの量指定子と共に使用します。 QL は再帰述語を継承するため、 countsumaverageなどの単純な QL 構文と集計を使用して、複雑な再帰クエリを記述することもできます。

QL 言語の詳細については、 CodeQL のドキュメントを参照してください

経路クエリ

プログラムを通じて情報が流れる方法は重要です。 無害と思われるデータは、予期しない方法で流れ、悪意を持って使用される可能性があります。

パス クエリを作成すると、コードベースを介して情報のフローを視覚化するのに役立ちます。 クエリでは、データが可能な開始点 (source) から使用可能なエンドポイント (sink) までのパスを追跡できます。 パスをモデル化するには、クエリでソース、シンク、およびそれらをリンクするデータ フローステップに関する情報を提供する必要があります。

独自のパス クエリの記述を開始する最も簡単な方法は、既存のクエリのいずれかをテンプレートとして使用することです。 サポートされている言語のこれらのクエリを取得するには、 CodeQL のドキュメントを参照してください。

パス クエリには、特定のメタデータ、クエリ述語、および select ステートメント構造が必要です。 CodeQL の組み込みパス クエリの多くは、単純な構造に従います。 構造は、分析する言語を CodeQL でモデル化する方法によって異なります。

パス クエリのテンプレートの例を次に示します。

/**
 * ...
 * @kind path-problem
 * ...
 */

import <language>
// For some languages (Java/C++/Python/Swift), you need to explicitly import the data-flow library, such as
// import semmle.code.java.dataflow.DataFlow or import codeql.swift.dataflow.DataFlow
...

module Flow = DataFlow::Global<MyConfiguration>;
import Flow::PathGraph

from Flow::PathNode source, Flow::PathNode sink
where Flow::flowPath(source, sink)
select sink.getNode(), source, sink, "<message>"

そのテンプレートでは、次の手順を実行します。

  • MyConfiguration は、 sourcesinkの間のデータ フロー方法を定義する述語を含むモジュールです。
  • Flow は、 MyConfigurationに基づくデータ フロー計算の結果です。
  • Flow::Pathgraph は、クエリにパスの説明を含めるためにインポートする必要がある結果のデータ フロー グラフ モジュールです。
  • source sinkは、構成で定義されているグラフ内のノードであり、Flow::PathNodeはその種類です。
  • DataFlow::Global<..> はデータ フローの呼び出しです。 代わりに TaintTracking::Global<..> を使用して、追加のテイント ステップの既定のセットを含めることができます。

パス クエリを記述する方法

パスの説明を生成するには、クエリでパス グラフを計算する必要があります。 そのためには、 edgesというクエリ述語を定義します。 クエリ述語は、 query 注釈を持つ非メンバー述語です。 クエリ注釈は、述語が評価するすべてのタプルを返します。

edges述語は、計算するグラフのエッジ関係を定義します。 クエリによって生成される各結果に関連するパスを計算するために使用されます。 また、標準データ フロー ライブラリの 1 つで、パス グラフ モジュールから定義済みの edges 述語をインポートすることもできます。

データ フロー ライブラリには、パス グラフ モジュールに加えて、データ フロー分析でよく使用される他のクラス、述語、およびモジュールが含まれています。 CodeQL データ フロー ライブラリは、データ フロー グラフをモデル化するか、データ フロー分析を実装することによって機能します。 通常のデータ フロー ライブラリは、各ステップでデータ値が保持される情報フローを分析するために使用されます。

edgesが定義されているデータ フロー ライブラリ (DataFlow.qll) から pathgraph モジュールをインポートするステートメントの例を次に示します。

import DataFlow::PathGraph

CodeQL に含まれる多数の追加ライブラリをインポートできます。 また、さまざまな一般的なフレームワークや環境でデータ フロー分析を実装するように特別に設計されたライブラリをインポートすることもできます。

PathNodeクラスは、データ フロー分析を実装するように特別に設計された例です。 Node呼び出しコンテキスト (シンクを除く)、アクセス パス、および構成で拡張されます。 PathNodeソースから到達可能な値のみが生成されます。

インポート パスの例を次に示します。

import semmle.code.cpp.ir.dataflow.internal.DataFlowImpl

必要に応じて、すべての言語のパス グラフのノードを指定する nodes クエリ述語を定義できます。 nodesを定義すると、選択したノードは端点を持つエッジのみを定義します。 nodesを定義しない場合は、edgesの使用可能なすべてのエンドポイントを選択する必要があります。

データベース分析

クエリを使用して CodeQL データベースを分析すると、ソース コードのコンテキストで意味のある結果が得られます。 結果は、アラートまたはパスとして SARIF または別の解釈形式でスタイル設定されます。

選択したクエリを実行して結果を解釈することでデータベースを分析する CodeQL データベース コマンドの例を次に示します。

codeql database analyze --format=<format> ---output=<output> [--threads=<num>] [--ram=<MB>] <options>... -- <database> <query|dir|suite>...

このコマンドは、 codeql database run-queries コマンドと codeql database interpret-results 配管コマンドの効果を結合します。

または、ソース コード アラートとして解釈される要件を満たしていないクエリを実行することもできます。 これを行うには、 codeql-database run-queries または codeql query runを使用します。 次に、 codeql bqrs decode を使用して、生の結果を読み取り可能な表記に変換します。

CodeQL CLI マニュアルでは、使用可能な CodeQL CLI コマンドの完全な一覧を取得できます。

SARIF ファイルをカテゴリと共に使用する

CodeQL では、静的分析結果を共有するための SARIF がサポートされています。 SARIF は、さまざまな静的分析ツールの出力を表すために設計されています。

CodeQL 分析に SARIF 出力を使用する場合は、カテゴリを指定する必要があります。 カテゴリは、同じコミット リポジトリと、異なる言語またはコードの異なる部分で実行される複数の分析を区別できます。 ただし、同じカテゴリの SARIF ファイルが互いに上書きされます。

カテゴリ値が分析の実行間で一貫している場合、CodeQL を使用して各 SARIF 出力ファイルをスキャンして、同じコード ベース内のさまざまな言語を分析できます。 スキャンされる言語をカテゴリの識別子として使用することをお勧めします。

1 つの例を次に示します。 カテゴリ値は、SARIF v1 の <run>.automationId プロパティ、SARIF v2 の <run>.automationLogicalId プロパティ、および SARIF v2.1.0 の <run>.automationDetails.id プロパティとして (末尾にスラッシュが追加されている場合)、表示されます。

SARIF の結果を GitHub に投稿する

データベースの準備ができたら、対話形式でクエリを実行できます。 または、一連のクエリを実行して一連の結果を SARIF 形式で生成し、GitHub.com のターゲット リポジトリに結果をアップロードできます。

codeql github upload-results --sarif=<file> [--github-auth-stdin] [--github-url=<url>] [--repository=<repository-name>] [--ref=<ref>] [--commit=<commit>] [--checkout-path=<path>] <options>...

GitHub に結果をアップロードするには、各継続的インテグレーション (CI) サーバーに、CodeQL CLI で使用する GitHub アプリまたは個人用アクセス トークンがあることを確認します。 security_events書き込みアクセス許可を持つアクセス トークンまたは GitHub アプリを使用する必要があります。

CI サーバーがこのスコープを持つトークンを既に使用して GitHub からリポジトリをチェックアウトしている場合は、CodeQL CLI で同じトークンを使用することが許可される可能性があります。 それ以外の場合は、 security_events 書き込みアクセス許可を持つ新しいトークンを作成し、このトークンを CI システムのシークレット ストアに追加します。 セキュリティのベスト プラクティスは、 --github-auth-stdin フラグを設定し、標準入力を使用してコマンドにトークンを渡すことです。

SARIF の結果をアップロードする

Microsoft 以外の静的分析ツールの結果を GitHub リポジトリに表示するコード スキャンでは、結果を、SARIF 2.1.0 JSON スキーマの特定のサブセットをサポートする SARIF ファイルに格納する必要があります。 コード スキャン API または CodeQL CLI を使用して、結果をアップロードできます。

新しいコード スキャンの結果をアップロードするたびに、CodeQL によって結果が処理され、リポジトリにアラートが追加されます。 同じ問題に対するアラートの重複を防ぐために、コード スキャンでは SARIF partialFingerprints プロパティを使用してさまざまな実行の結果を照合し、選択したブランチの最新の実行に 1 回だけ表示されるようにします。 これにより、ファイルが編集されたときに、アラートを適切なコードの行に照合させることができます。

結果のルール ID は、分析全体で同じである必要があります。 指紋データは、CodeQL 分析ワークフローまたは CodeQL ランナーを使用して作成された SARIF ファイルに自動的に含まれます。

SARIF 仕様では、名前付きフィンガープリント型から指紋へのディクショナリである JSON プロパティ名 partialFingerprintsが使用されます。 このプロパティには、少なくとも primaryLocationLineHash の値が含まれています。これにより、プライマリの場所のコンテキストに基づいて指紋が提供されます。

upload-sarif アクションを使用して SARIF ファイルをアップロードし、このデータが見つからない場合、GitHub はソース ファイルから partialFingerprints フィールドを設定しようとします。 さらに、 /code-scanning/sarifs API エンドポイントを使用して指紋データなしで SARIF ファイルをアップロードすると、コード スキャン アラートが処理されて表示されるときに、ユーザーに重複するアラートが表示されることがあります。

静的分析ツールを使用しているときに重複するアラートが表示されないようにするには、SARIF ファイルをアップロードする前に指紋データを計算し、 partialFingerprints プロパティを設定します。 便利な開始点は、 upload-sarif アクションと同じスクリプトを使用することです。