Skip to main content

CodeQL クエリ スイートの作成

CodeQL 分析で頻繁に使用するクエリのクエリ スイートを作成できます。

この機能を使用できるユーザーについて

GitHub CodeQL は、インストール時にユーザーごとにライセンスされます。 CodeQL は、ライセンスの制限の下で特定のタスクでのみ使用できます。 詳しくは、「CodeQL CLI について」を参照してください。

GitHub Advanced Security ライセンスがある場合は、CodeQL を使用して、自動分析、継続的インテグレーション、継続的デリバリーを行うことができます。 詳しくは、「GitHub Advanced Security について」を参照してください。

CodeQL クエリ スイートの作成について

CodeQL クエリ スイートでは、ファイル名、ディスクまたは CodeQL パック上の場所、またはメタデータ プロパティに基づいてクエリを選ぶことができます。 CodeQL 分析で頻繁に使用するクエリのクエリ スイートを作成します。

クエリ スイートを使用すると、各クエリ ファイルへのパスを個別に指定せずに、複数のクエリを CodeQL に渡すことができます。 クエリ スイートの定義は、拡張子が .qls の YAML ファイルに格納されます。 スイートの定義は一連の命令であり、各命令は、(通常は) 1 つのキーを持つ YAML マッピングです。 これらの命令は、クエリ スイートの定義に指定された順序で実行されます。 スイートの定義の命令がすべて実行されると、結果は、選択されたクエリのセットになります。

注: クエリ スイートに追加するカスタム クエリは CodeQL パック内にあり、正しいクエリ メタデータを含んでいる必要があります。 詳しくは、「CodeQL CLI でのカスタム クエリの使用」をご覧ください。

クエリ スイートに追加するクエリの場所指定

クエリ スイートを作成するときは、選びたいクエリの場所を最初に指定する必要があります。 次を使用して、1 つ以上のクエリの場所を定義できます。

  • query 命令 — CodeQL に、指定された 1 つ以上の .ql ファイルを検索するように指示します。

    - query: <path-to-query>
    

    引数は、スイート定義を含む CodeQL パックを基準にした 1 つ以上のファイル パスである必要があります。

  • queries 命令 — CodeQL に、.ql ファイルのディレクトリを再帰的にスキャンするように指示します。

    - queries: <path-to-subdirectory>
    

    ディレクトリのパスは、スイート定義ファイルを含む CodeQL パックのルートを基準にする必要があります。 別の CodeQL パックに対するクエリを検索するには、from フィールドを追加します。

    - queries: <path-to-subdirectory>
      from: <ql-pack-name>
      version: ^x.y.z
    

    version フィールドは省略可能であり、この CodeQL パックの互換性のあるバージョンの範囲を指定するものです。 バージョンを指定しない場合は、パックの最新バージョンが使用されます。

  • qlpack 命令 — CodeQL に、名前付き CodeQL パックの既定のスイートでクエリを解決するように指示します。

    - qlpack: <qlpack-name>
      version: ^x.y.z
    

    クエリ パックの既定のスイートには、そのクエリ パック内で推奨されるクエリのセットが含まれています。 すべてのクエリ パックに既定のスイートがあるわけではありません。 指定されたクエリ パックで既定のスイートが定義されていない場合、qlpack 命令はパック内のすべてのクエリに解決されます。

    version フィールドは省略可能であり、この CodeQL パックの互換性のあるバージョンの範囲を指定するものです。 バージョンを指定しない場合は、パックの最新バージョンが使用されます。

注: パス名がクエリ スイート定義に表示される場合は、常にスラッシュ / をディレクトリ区切りとして指定する必要があります。 これにより、クエリ スイート定義がすべてのオペレーティング システムで確実に機能します。

スイート定義には少なくとも 1 つの queryqueries、または qlpack 命令を追加する必要があります。それ以外の場合、クエリは選ばれません。 スイートにこれ以上の命令が含まれない場合は、ファイルの一覧、指定されたディレクトリ、または名前付き CodeQL パックで見つかったすべてのクエリが選ばれます。 さらにフィルター処理命令がある場合は、それらの命令によって課される制約に一致するクエリのみが選ばれます。

クエリ スイートでのクエリのフィルター処理

queryqueries、または qlpack 命令を指定してスイートに追加するクエリの初期セットを定義した後、includeexclude の命令を追加できます。 次の命令では、特定のプロパティに基づいて選択条件が定義されます。

  • クエリのセットに対して include 命令を実行すると、条件に一致するすべてのクエリが選択に保持され、一致しないクエリは削除されます。
  • クエリのセットに対して exclude 命令を実行すると、条件に一致するすべてのクエリが選択から削除され、一致しないクエリは保持されます。

フィルター命令の順序は重要です。 命令の場所指定後に表示される最初のフィルター命令によって、既定でクエリを含めるか除外するかが決まります。 最初のフィルターが include の場合、最初に場所指定されたクエリは、明示的な include フィルターと一致する場合にのみスイートの一部になります。 最初のフィルターが exclude の場合、最初に場所指定されたクエリは、明示的に除外されている場合にのみスイートの一部になります。

後続の命令は順番に実行され、ファイル内で後にある命令の方が前にある命令よりも優先されます。 そのため、include 命令は、同じクエリに一致する後の exclude 命令によってオーバーライドできます。 同様に、exclude は後の includeによってオーバーライドできます。

どちらの命令でも、引数は制約ブロックです。つまり、制約を表す YAML マップです。 各制約はマップ エントリであり、この場合のキーは通常、クエリ メタデータ プロパティです。 次の値が考えられます。

  • 1 つの文字列。
  • / で囲まれた正規表現
  • 文字列、正規表現、またはその両方を含むリスト。

制約に一致させるには、メタデータ値が文字列または正規表現のいずれかに一致する必要があります。 複数のメタデータ キーがある場合は、各キーを一致させる必要があります。 照合に使用できる標準メタデータ キーは descriptionidkindnametagsprecision、および problem.severity です。 クエリ メタデータ プロパティについて詳しくは、「CodeQL クエリのメタデータ」をご覧ください。

メタデータ タグに加えて、制約ブロック内のキーは次の場合もあります。

  • query filename — クエリ ファイル名の最後のパス コンポーネントと照合します。
  • query path — 外側の CodeQL パックを基準にした、クエリ ファイルのパスと照合します。
  • tags contain — 指定された照合文字列の 1 つが、@tags メタデータ プロパティの値のスペース区切りコンポーネントのいずれかに一致する必要があります。
  • tags contain all — 指定されたそれぞれの照合文字列が、@tags メタデータ プロパティのコンポーネントのいずれかに一致する必要があります。

実行するクエリのフィルター処理の例

一般的なユース ケースは、ユーザーが実行したくない特定のクエリを除き、CodeQL パックですべてのクエリを実行するクエリ スイートを作成することです。 一般的には、クエリごとに一意で安定した識別子であるクエリ id でフィルター処理することをお勧めします。 次の 3 つのクエリ スイート定義は、意味的に同一であり、クエリ id でフィルター処理されます。

このフィルターは、除外された識別子を持つ 2 つのクエリを除き、codeql/cpp-queries の既定のスイート内のすべてのクエリと一致します。

- qlpack: codeql/cpp-queries
- exclude:
    id:
      - cpp/cleartext-transmission
      - cpp/cleartext-storage-file

この例では、クエリごとに個別の exclude 命令が使用されます。

- qlpack: codeql/cpp-queries
- exclude:
    id: cpp/cleartext-transmission
- exclude:
    id: cpp/cleartext-storage-file

この例では、正規表現によって同じ 2 つのクエリが除外されます。 また、cpp/cleartext- で始まる識別子を持つスイートに追加される今後のクエリも除外されます。

- qlpack: codeql/cpp-queries
- exclude:
    id:
      - /^cpp\/cleartext-.*/

codeql/cpp-queries CodeQL パックの既定のスイート内のすべてのクエリを選び、セキュリティ クエリのみを含むように絞り込むスイートを定義するには、次を使用します。

- qlpack: codeql/cpp-queries
- include:
    tags contain: security

my-custom-queries ディレクトリから @kind problem@precision high のすべてのクエリが選ばれるスイートを定義するには、次を使用します。

- queries: my-custom-queries
- include:
    kind: problem
    precision: very-high

次のクエリ スイート定義は、上記の定義とは異なる動作をします。 この定義は、@kind problem "または" @precision very-high であるクエリが選ばれます。__

- queries: my-custom-queries
- include:
    kind: problem
- include:
    precision: very-high

@problem.severity recommendation を含むものを除き、my-custom-queries ディレクトリから @kind problem のすべてのクエリが選ばれるスイートを作成するには、次を使用します。

- queries: my-custom-queries
- include:
    kind: problem
- exclude:
    problem.severity: recommendation

codeql/cpp-queries CodeQL パックから @tag security@precision high または very-high のすべてのクエリが選ばれるスイートを作成するには、次を使用します。

- queries: .
  from: codeql/cpp-queries
- include:
    tags contain: security
    precision:
    - high
    - very-high

注: codeql resolve queries /path/to/suite.qls コマンドを使用すると、クエリ スイート定義で選ばれているクエリを確認できます。 詳しくは、「クエリを解決する」を参照してください。

既存のクエリ スイート定義の再利用

既存のクエリ スイート定義は、次を指定することで再利用できます。

  • import 命令 — 以前に定義した .qls ファイルによって選ばれたクエリを現在のスイートに追加します。

    - import: <path-to-query-suite>
    

    インポートされたスイートのパスは、現在のスイート定義を含む CodeQL パックを基準にする必要があります。 インポートされたクエリ スイートが別の QL パックにある場合は、次を使用できます。

    - import: <path-to-query-suite>
      from: <ql-pack>
      version: ^x.y.z
    

    version フィールドは省略可能であり、この CodeQL パックの互換性のあるバージョンの範囲を指定するものです。 バージョンを指定しない場合は、パックの最新バージョンが使用されます。

    import 命令を使用して追加されたクエリは、後続の exclude 命令を使用してフィルター処理できます。

  • apply 命令 — 以前に定義した .qls ファイルのすべての命令を現在のスイートに追加します。 適用された .qls ファイル内の命令が、apply の代わりに表示されたかのようにして実行されます。 適用されたスイートの includeexclude の命令は、以前の命令によって追加されたクエリにも作用します。

    - apply: <path-to-query-suite>
    

    apply 命令を使用して、.yml ファイルに保存された一連の再利用可能な条件を複数のクエリ定義に適用することもできます。 詳しくは、下記のをご覧ください。

再利用性の例

複数のクエリ スイート定義で同じ条件を使用するには、命令を含む別の .yml ファイルを作成します。 たとえば、reusable-instructions.yml という名前のファイルに次を保存します。

- include:
    kind:
    - problem
    - path-problem
    tags contain: security
    precision:
    - high
    - very-high

現在のクエリ スイートと同じ CodeQL パックに reusable-instructions.yml を追加します。 次に、1 つ以上のクエリ スイートで、apply 命令を使用して、再利用可能な命令を現在のスイートに適用します。 次に例を示します。

- queries: queries/cpp/custom
- apply: reusable-instructions.yml

これにより、queries/cpp/custom のクエリがフィルター処理され、再利用可能な条件に一致するもののみが含まれるようになります。

別の CodeQL パックのクエリで reusable-instructions.yml を使用してスイート定義を作成することもできます。 .qls ファイルがクエリと同じ CodeQL パックにある場合は、apply 命令の直後に from フィールドを追加できます。

# load queries from the default suite of my-org/my-other-custom-queries
- qlpack: my-org/my-other-custom-queries

# apply the reusable instructions from the my-org/my-custom-instructions CodeQL pack
- apply: reusable-instructions.yml
  from: my-org/my-custom-instructions
  version: ^1.2.3 # optional

import 命令の一般的なユース ケースは、別のクエリ スイートのクエリにさらにフィルターを適用することです。 たとえば、このスイートでは、cpp-security-and-quality スイートをさらにフィルター処理し、lowmedium の有効桁数クエリが除外されます。

- import: codeql-suites/cpp-security-and-quality.qls
  from: codeql/cpp-queries
- exclude:
    precision:
      - low
      - medium

別のスイートからインポートされたクエリを include する場合、構文は少し異なります。

- import: codeql-suites/cpp-security-and-quality.qls
  from: codeql/cpp-queries
- exclude: {}
- include:
    precision:
      - very-high
      - high

空の exclude 命令に注目してください。 これは、後続の include 命令がインポートされたスイートからのクエリをフィルター処理できるようにするために必要です。

クエリ スイートの名前付け

クエリ スイートの名前を指定するには、description 命令を指定します。

- description: <name-of-query-suite>

クエリ スイートの保存

.qls 拡張子が付いたファイルにクエリ スイートを保存し、CodeQL パックに追加します。 詳しくは、「CodeQL パックを使った分析のカスタマイズ」を参照してください。

CodeQL でのクエリ スイートの使用

.qls ファイルを受け入れるコマンドに対して、コマンド ラインでクエリ スイートを指定できます。 たとえば、query compile を使用してスイート定義によって選ばれたクエリをコンパイルしたり、database analyze を使用して分析でクエリを使用したりできます。 CodeQL データベースを分析する方法について詳しくは、「CodeQL クエリによるコード分析」をご覧ください。

参考資料