# 创建 CodeQL 查询套件

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

你可以为要在 CodeQL 分析中频繁使用的查询创建查询套件。 有关详细信息，请参阅“[CodeQL 查询套件](/zh/code-security/concepts/code-scanning/codeql/codeql-query-suites)”。

> \[!NOTE]
> 要添加到查询套件的任何自定义查询都必须位于 [CodeQL 包](/zh/code-security/codeql-cli/getting-started-with-the-codeql-cli/customizing-analysis-with-codeql-packs)中，并包含正确的查询元数据。 有关详细信息，请参阅“[编写 CodeQL CLI 的自定义查询](/zh/code-security/how-tos/scan-code-for-vulnerabilities/scan-from-the-command-line/writing-and-sharing-custom-queries-for-the-codeql-cli)”。

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

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

* ```
          `query` 指令：告知 CodeQL 查找一个或多个指定的 `.ql` 文件：
  ```

  ```yaml
  - query: <path-to-query>
  ```

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

* ```
          `queries` 指令：告知 CodeQL 以递归方式扫描目录中的 `.ql` 文件：
  ```

  ```yaml
  - queries: <path-to-subdirectory>
  ```

  目录的路径必须相对于包含套件定义文件的 CodeQL 包的根目录。 要查找相对于其他 CodeQL 包的查询，请添加 `from` 字段：

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

  ```
            `version` 字段是可选的，指定此 CodeQL 包的兼容版本范围。
  ```

如果未指定版本，则使用最新版本的包。

* ```
          `qlpack` 指令：告知 CodeQL 解析指定 CodeQL 包的默认套件中的查询：
  ```

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

  查询包的默认套件在该查询包中包含一组建议的查询。 并非所有查询包都有默认套件。 如果给定的查询包未定义默认套件，则 qlpack 指令将解析为包中的所有查询。

  ```
            `version` 字段是可选的，指定此 CodeQL 包的兼容版本范围。
  ```

如果未指定版本，则使用最新版本的包。

> \[!NOTE]
> 当查询套件定义中出现路径名时，必须始终使用正斜杠 (`/`) 作为目录分隔符。 这可确保查询套件定义适用于所有操作系统。

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

## 筛选查询套件中的查询

通过指定 `query`、`queries` 或 `qlpack` 指令定义要添加到套件的初始查询集后，可以添加 `include` 和 `exclude` 指令。 这些指令基于特定属性定义选择条件：

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

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

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

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

* 单个字符串。
* 用 `/` 括起来的[正则表达式](https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/util/regex/Pattern.html)。
* 包含字符串、正则表达式或两者的列表。

要匹配约束，元数据值必须匹配字符串或正则表达式之一。 如果有多个元数据键，每个键都必须匹配。
可匹配的标准元数据键包括：`description`、`id`、`kind`、`name`、`tags`、`precision` 和 `problem.severity`。
有关查询元数据属性的详细信息，请参阅 [CodeQL 查询的元数据](https://codeql.github.com/docs/writing-codeql-queries/metadata-for-codeql-queries/#metadata-for-codeql-queries)。

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

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

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

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

此筛选器匹配默认套件 `codeql/cpp-queries` 中的所有查询，但具有排除标识符的两个查询除外：

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

在此示例中，每个查询都使用单独的 `exclude` 指令：

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

在此示例中，正则表达式排除相同的两个查询。 它还将排除将来添加到套件的任何查询，这些查询的标识符以 `cpp/cleartext-` 开头：

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

要定义一个套件，使其选择 `codeql/cpp-queries` CodeQL 包的默认套件中的所有查询，然后将其细化为仅包含安全查询，请使用：

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

要定义一个套件，用于从 `@kind problem` 目录中选择具有 `@precision high` 和 `my-custom-queries` 的所有查询，请使用：

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

请注意，以下查询套件定义的行为方式与上述定义不同。 此定义选择 `@kind problem`*或*`@precision very-high` 的查询：

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

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

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

要创建一个套件，使其从 `@tag security` CodeQL 包中选择具有 `@precision high` 和 `very-high` 或 `codeql/cpp-queries` 的所有查询，请使用：

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

> \[!NOTE]
> 可使用 `codeql resolve queries /path/to/suite.qls` 命令查看查询套件定义选择了哪些查询。 有关详细信息，请参阅“[解析查询](/zh/code-security/codeql-cli/codeql-cli-manual/resolve-queries)”。

## 重用现有查询套件定义

可以通过指定以下内容重复使用现有的查询套件定义：

* ```
          `import` 指令：将先前定义的 `.qls` 文件选择的查询添加到当前套件：
  ```

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

  导入套件的路径必须相对于包含当前套件定义的 CodeQL 包。 如果导入的查询套件位于其他 QL 包中，则可以使用：

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

  ```
            `version` 字段是可选的，指定此 CodeQL 包的兼容版本范围。
  ```

如果未指定版本，则使用最新版本的包。

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

* ```
          `apply` 指令：将先前定义的 `.qls` 文件中的所有指令添加到当前套件。 执行所应用的 `.qls` 文件中的指令，就像它们显示为代替 `apply` 一样。
  ```

应用的套件中的任何 `include` 和 `exclude` 指令也会作用于任何先前指令添加的查询：

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

```
            `apply` 指令还可用于将保存在 `.yml` 文件中的一组可重用条件应用于多个查询定义。 有关详细信息，请参阅下面的[示例](#reusability-examples)。
```

### 可重用性示例

要在多个查询套件定义中使用相同的条件，请创建包含指令的单独的 `.yml` 文件。 例如，将以下内容保存在名为 `reusable-instructions.yml` 的文件中：

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

将 `reusable-instructions.yml` 添加到与当前查询套件相同的 CodeQL 包。 然后，在一个或多个查询套件中，使用 `apply` 指令将可重用指令应用于当前套件。 例如：

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

这将筛选 `queries/cpp/custom` 中的查询，以仅包含与可重用条件匹配的查询。

还可以在不同的 CodeQL 包中的查询上使用 `reusable-instructions.yml` 创建套件定义。 如果 `.qls` 文件与查询位于同一 CodeQL 包中，则可以在 `from` 指令后面立即添加 `apply` 字段：

```yaml
# 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` 套件并排除 `low` 和 `medium` 精确查询：
```

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

如果要从另一个套件导入 `include` 查询，语法略有不同：

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

请注意空的 `exclude` 指令。 这是确保后续 `include` 指令能够筛选导入的套件中的查询所必需的。

## 命名查询套件

可以通过指定 `description` 指令为查询套件提供一个名称：

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

## 保存查询套件

将查询套件保存在扩展名为 `.qls` 的文件中，并将其添加到 CodeQL 包中。 有关详细信息，请参阅“[使用 CodeQL 包自定义分析](/zh/code-security/codeql-cli/getting-started-with-the-codeql-cli/customizing-analysis-with-codeql-packs#custom-codeql-packs)”。

## 将查询套件与 CodeQL 配合使用

可以在命令行上为接受 `.qls` 文件的任何命令指定查询套件。 例如，可以使用 `query compile` 编译由套件定义选择的查询，或使用 `database analyze` 在分析中使用查询。 有关分析 CodeQL 数据库的更多信息，请参阅 [使用 CodeQL 查询分析代码](/zh/code-security/codeql-cli/getting-started-with-the-codeql-cli/analyzing-your-code-with-codeql-queries)。

## 延伸阅读

* ```
          [CodeQL 查询](https://codeql.github.com/docs/writing-codeql-queries/codeql-queries/#codeql-queries)
  ```