Skip to main content

创建 CodeQL 查询套件

可以为经常在 CodeQL 分析中使用的查询创建查询套件。

GitHub CodeQL 在安装后按用户授权。 根据许可证限制,只能将 CodeQL 用于某些任务。 有关详细信息,请参阅“关于 CodeQL CLI”。

如果你有 GitHub Advanced Security 许可证,则可以使用 CodeQL 进行自动分析、持续集成和持续交付。 有关详细信息,请参阅“关于 GitHub 高级安全性”。

关于创建 CodeQL 查询套件

CodeQL 查询套件根据查询的文件名、磁盘 上或 CodeQL 包 中的位置或元数据属性来选择查询。 可以为要经常在 CodeQL 分析中使用的查询创建查询套件。

使用查询套件,可将多个查询传递给 CodeQL,而无需单独指定每个查询文件的路径。 查询套件定义存储在扩展名为 .qls 的 YAML 文件中。 套件定义是一系列指令,其中每条指令都是带有(通常)单个键的 YAML 映射。 指令按它们在查询套件定义中的显示顺序执行。 执行套件定义中所有指令后,结果为一组选定的查询。

注意:要添加到查询套件的任何自定义查询都必须位于 CodeQL 包中,并包含正确的查询元数据。 有关详细信息,请参阅“将自定义查询与 CodeQL CLI 配合使用”。

查找要添加到查询套件中的查询

创建查询套件时,首先需要指定要选择的查询的位置。 可以使用以下方法定义一个或多个查询的位置:

  • query 指令 - 指示 CodeQL 查找一个或多个指定的 .ql 文件:

    - query: <path-to-query>
    

    参数必须是一个或多个文件路径,相对于包含套件定义的 CodeQL 包。

  • 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 包的兼容版本范围。 如果未指定版本,则使用最新版本的包。

注意:当路径名出现在查询套件定义中时,必须在使用正斜杠 / 作为目录分隔符的情况下提供。 这可确保查询套件定义适用于所有操作系统。

必须向套件定义添加至少一条 queryqueriesqlpack 指令,否则不会选择任何查询。 如果套件不包含进一步的指令,则会选择从文件列表、给定目录或命名 CodeQL 包中找到的所有查询。 如果有进一步的筛选指令,将仅选择与这些指令施加的约束相匹配的查询。

筛选查询套件中的查询

通过指定 queryqueriesqlpack 指令定义要添加到套件的初始查询集后,可以添加 includeexclude 指令。 这些指令基于特定属性定义选择条件:

  • 对一组查询执行 include 指令时,与条件匹配的任何查询都会保留在所选内容中,并删除不匹配的查询。
  • 对一组查询执行 exclude 指令时,与条件匹配的任何查询都将从所选内容中删除,并保留不匹配的查询。

筛选器指令的顺序非常重要。 找到指令之后出现的第一个筛选器指令确定默认情况下是包含查询还是排除查询。 如果第一个筛选器是 include,则最初找到的查询仅当与显式 include 筛选器匹配时才是套件的一部分。 如果第一个筛选器是 exclude,则最初找到的查询是套件的一部分,除非它们被显式排除。

后续指令按顺序执行,在文件后面出现的指令优先于前面的指令。 因此,include 指令可由与同一查询匹配的后续 exclude 指令重写。 同样,exclude 可由后面的 include 重写。

对于这两个指令,参数都是一个约束块,即表示约束的 YAML 映射。 每个约束都是一个映射条目,其中键通常是查询元数据属性。 值可以是:

  • 单个字符串。
  • / 括起来的正则表达式
  • 包含字符串、正则表达式或两者的列表。

若要匹配约束,元数据值必须匹配字符串或正则表达式之一。 如果有多个元数据键,每个键都必须匹配。 可匹配的标准元数据键包括:descriptionidkindnametagsprecisionproblem.severity。 有关查询元数据属性的详细信息,请参阅“CodeQL 查询的元数据”。

除元数据标记外,约束块中的键还可以是:

  • query filename - 匹配查询文件名的最后一个路径组件。
  • query path - 匹配到查询文件相对于其封闭 CodeQL 包的路径。
  • tags contain - 给定匹配字符串之一必须与 @tags 元数据属性值的以空格分隔的组件之一匹配。
  • tags contain all - 每个给定匹配字符串都必须与 @tags 元数据属性值的组件之一匹配。

筛选正在运行的查询的示例

一个常见的用例是创建一个查询套件,用于运行 CodeQL 包中的所有查询,但用户不希望运行的一些特定查询除外。 通常,我们建议对查询 id 进行筛选,这是每个查询的唯一且稳定的标识符。 以下三个查询套件定义在语义上相同,并通过查询 id 进行筛选:

此筛选器匹配默认套件 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

在此示例中,正则表达式排除相同的两个查询。 它还将排除将来添加到套件的任何查询,这些查询的标识符以 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

若要创建一个套件,以从 my-custom-queries 目录中选择具有 @kind problem 的所有查询,除了具有 @problem.severity recommendation 的查询,请使用:

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

若要创建一个套件,使其从 codeql/cpp-queries CodeQL 包中选择具有 @tag security@precision highvery-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 包的兼容版本范围。 如果未指定版本,则使用最新版本的包。

    可以使用后续 exclude 指令筛选使用 import 指令添加的查询。

  • 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

reusable-instructions.yml 添加到与当前查询套件相同的 CodeQL 包。 然后,在一个或多个查询套件中,使用 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 包来声明包含“已知”查询套件的目录。 可以通过引用查询套件的文件名在命令行上使用“已知”查询套件,而无需提供其完整路径。 这为你提供了一种指定一组查询的简单方法,而无需在 CodeQL 包和分发包内搜索。 若要声明包含“已知”查询套件的目录,请将目录添加到 CodeQL 包的根目录处的 qlpack.yml 文件中的 suites 属性。 有关详细信息,请参阅“使用 CodeQL 包自定义分析”。

将查询套件与 CodeQL 配合使用

可以在命令行上为接受 .qls 文件的任何命令指定查询套件。 例如,可以使用 query compile 编译由套件定义选择的查询,或使用 database analyze 在分析中使用查询。 有关分析 CodeQL 数据库的详细信息,请参阅“使用 CodeQL 查询分析代码”。

延伸阅读