Skip to main content

为 CodeQL 分析准备代码

可以生成 CodeQL 数据库,其中包含分析代码所需的数据。

谁可以使用此功能?

具有存储库读取访问权限的用户

CodeQL 可用于以下存储库类型:

关于为分析准备代码

在使用 CodeQL 分析代码之前,需要创建一个 CodeQL 数据库,其中包含对代码运行查询所需的所有数据。 可以使用 CodeQL CLI 自行创建 CodeQL 数据库。

CodeQL 分析依赖于从代码中提取关系数据,并使用它来生成 CodeQL 数据库。 CodeQL 数据库包含有关代码库的所有重要信息,可通过执行 CodeQL 查询对其进行分析。

在生成 CodeQL 数据库之前,需要:

  1. 安装并设置 CodeQL CLI。 有关详细信息,请参阅“设置 CodeQL CLI”。
  2. 查看要分析的代码:
    • 对于分支,请查看要分析的分支的头。
    • 对于拉取请求,请签出拉取请求的头部提交,或签出 GitHub 生成的拉取请求的合并提交。
  3. 设置代码库的环境,确保所有依赖项都可用。
  4. 若要编译语言获得最佳结果,请查找适用于代码库的生成命令(如果有)。 通常可在 CI 系统的配置文件中找到。

代码库准备就绪后,可以运行 codeql database create 以创建数据库。 有关详细信息,请参阅“为非编译语言创建数据库”和“为编译语言创建数据库”。

正在运行 codeql database create

CodeQL 数据库是通过从项目的签出根目录运行以下命令创建的:

codeql database create <database> --language=<language-identifier>

必须指定以下内容:

  • <database>:要创建的新数据库的路径。 执行命令时将创建此目录,无法指定现有目录。

  • --language:要为其创建数据库的语言的标识符。 与 --db-cluster 一起使用时,该选项接受逗号分隔的列表,也可以多次指定。 CodeQL 支持为以下语言创建数据库:

    语言Identifier可选替代标识符(如果有)
    C/C++c-cppccpp
    C#csharp
    Gogo
    Java/Kotlinjava-kotlinjavakotlin
    JavaScript/TypeScriptjavascript-typescriptjavascripttypescript
    Pythonpython
    Rubyruby
    Swiftswift

    注意:**** 如果指定替代标识符之一,则等效于使用标准语言标识符。 例如,指定 javascript 而不是 javascript-typescript 不排除对 TypeScript 代码的分析。 可以使用 --paths-ignore 选项在高级设置工作流中执行此操作。 有关详细信息,请参阅“自定义代码扫描的高级设置”。

如果你的代码库中有调用构建过程的构建命令或脚本,我们建议你也指定该命令或脚本:

   codeql database create <database> --command <build> \
         --language=<language-identifier>

用于创建数据库的选项

如果需要编译代码,以及如果要为多种语言创建 CodeQL 数据库,可以根据源文件的位置指定其他选项。

选项必选使用情况
<database>指定要为 CodeQL 数据库创建的目录的名称和位置。 如果尝试覆盖现有目录,则该命令将失败。 如果还指定 --db-cluster,则这是父目录,并且会为分析的每种语言创建一个子目录。
--language指定要为其创建数据库的语言的标识符,以下之一:c-cppcsharpgojava-kotlinjavascript-typescriptpythonrubyswift。 与 --db-cluster 一起使用时,该选项接受逗号分隔的列表,也可以多次指定。
--command推荐。 用于指定为代码库调用生成过程的生成命令或脚本。 命令从当前文件夹运行,或者从定义的位置 --source-root 运行。 Python 和 JavaScript/TypeScript 分析不需要。
--build-mode推荐。 当未提供 --command 指定是否在没有生成 (none) 的情况下创建 CodeQL 数据库,或尝试检测生成命令 (autobuild) 时,供 C# 和 Java 使用。 默认情况下,使用自动生成检测。 有关生成模式的比较,请参阅“CodeQL 生成模式”。
--db-cluster在多语言代码库中使用,为 --language 指定的每种语言生成一个数据库。
--no-run-unnecessary-builds推荐。 用于抑制 CodeQL CLI 不需要监视生成的语言的生成命令(例如,Python 和 JavaScript/TypeScript)。
--source-root如果在存储库的检出根目录之外运行 CLI,请使用此选项。 默认情况下,database create 命令假定当前目录是源文件的根目录,使用此选项可指定其他位置。
--codescanning-config高级。 如果具有指定如何创建 CodeQL 数据库以及后续步骤中要运行的查询的配置文件,请进行使用。 有关详细信息,请参阅“自定义代码扫描的高级设置”和“database create”。

可以指定提取程序选项来自定义创建 CodeQL 数据库的提取程序的行为。 有关详细信息,请参阅“提取程序选项”。

有关创建数据库时可以使用的所有选项的完整详细信息,请参阅“database create”。

单语言示例

此示例在 /checkouts/example-repo 为签出的存储库创建单个 CodeQL 数据库。 它使用 JavaScript 提取器在存储库中创建 JavaScript 和 TypeScript 代码的分层表示形式。 生成的数据库存储在 /codeql-dbs/example-repo 中。

$ codeql database create /codeql-dbs/example-repo --language=javascript-typescript \
    --source-root /checkouts/example-repo

> Initializing database at /codeql-dbs/example-repo.
> Running command [/codeql-home/codeql/javascript/tools/autobuild.cmd]
    in /checkouts/example-repo.
> [build-stdout] Single-threaded extraction.
> [build-stdout] Extracting
...
> Finalizing database at /codeql-dbs/example-repo.
> Successfully created database at /codeql-dbs/example-repo.

多语言示例

此示例在 /checkouts/example-repo-multi 为签出的存储库创建两个 CodeQL 数据库。 它使用:

  • --db-cluster 用于请求分析多种语言。
  • --language 用于指定要为其创建数据库的语言。
  • --command 用于告知工具代码库的生成命令,此处为 make
  • --no-run-unnecessary-builds 用于告知工具跳过不需要的语言(如 Python)的生成命令。

生成的数据库存储在 /codeql-dbs/example-repo-multipythoncpp 子目录中。

$ codeql database create /codeql-dbs/example-repo-multi \
    --db-cluster --language python,c-cpp \
    --command make --no-run-unnecessary-builds \
    --source-root /checkouts/example-repo-multi
Initializing databases at /codeql-dbs/example-repo-multi.
Running build command: [make]
[build-stdout] Calling python3 /codeql-bundle/codeql/python/tools/get_venv_lib.py
[build-stdout] Calling python3 -S /codeql-bundle/codeql/python/tools/python_tracer.py -v -z all -c /codeql-dbs/example-repo-multi/python/working/trap_cache -p ERROR: 'pip' not installed.
[build-stdout] /usr/local/lib/python3.6/dist-packages -R /checkouts/example-repo-multi
[build-stdout] [INFO] Python version 3.6.9
[build-stdout] [INFO] Python extractor version 5.16
[build-stdout] [INFO] [2] Extracted file /checkouts/example-repo-multi/hello.py in 5ms
[build-stdout] [INFO] Processed 1 modules in 0.15s
[build-stdout] <output from calling 'make' to build the C/C++ code>
Finalizing databases at /codeql-dbs/example-repo-multi.
Successfully created databases at /codeql-dbs/example-repo-multi.
$

进度和结果

如果指定的选项存在任何问题,将报告错误。 对于解释语言以及为 C# 和 Java 指定 --build-mode none 时,提取过程显示在控制台中。 对于每个源文件,控制台显示提取是成功还是失败。 如果生成已编译的语言,控制台将显示生成系统的输出。

成功创建数据库后,会在命令中指定的路径处找到一个新目录。 如果使用 --db-cluster 选项创建多个数据库,则会为每种语言创建一个子目录。 每个 CodeQL 数据库目录包含许多子目录,包括关系数据(分析所需)以及用于显示分析结果的源存档(创建数据库时制作的源文件的副本)。

为非编译语言创建数据库

CodeQL CLI 包括为非编译语言(特别是 JavaScript(和 TypeScript)、Python 和 Ruby)创建数据库的提取程序。 如果在执行 database create 时将 JavaScript、Python 或 Ruby 指定为 --language 选项,则会自动调用这些提取程序。 为这些语言创建数据库时,必须确保所有其他依赖项都可用。

注意:为 JavaScript、TypeScript、Python 和 Ruby 运行 database create 时,不应指定 --command 选项。 否则,此操作将替代正常的提取程序调用,将创建一个空数据库。 如果为多种语言创建数据库,并且其中一种语言为已编译语言,请使用 --no-run-unnecessary-builds 选项跳过不需要编译的语言的命令。

JavaScript 和 TypeScript

创建 JavaScript 数据库不需要其他依赖项,但如果项目包含 TypeScript 文件,则必须安装 Node.js 14 或更高版本并在 PATH 上作为 node 提供。 在命令行中,可以指定 --language=javascript-typescript 提取 JavaScript 和 TypeScript 文件:

codeql database create --language=javascript-typescript --source-root <folder-to-extract> <output-folder>/javascript-database

此处,我们指定了一个 --source-root 路径,该路径是执行数据库创建的位置,但不一定是代码库的签出根。

默认情况下,不会提取 node_modulesbower_components 目录中的文件。

Python

创建适用于 Python 的数据库时,必须确保:

  • 已安装 Python 3,可用于 CodeQL 提取程序。
  • 已安装代码使用的 Python 版本。

在命令行中,必须指定 --language=python。 例如:

codeql database create --language=python <output-folder>/python-database

这会从代码的签出根执行 database create 子命令,在 <output-folder>/python-database 生成新的 Python 数据库。

Ruby

为 Ruby 创建数据库不需要额外的依赖项。 在命令行中,必须指定 --language=ruby。 例如:

codeql database create --language=ruby --source-root <folder-to-extract> <output-folder>/ruby-database

此处,我们指定了一个 --source-root 路径,该路径是执行数据库创建的位置,但不一定是代码库的签出根。

为已编译语言创建数据库

对于t最多已编译语言,CodeQL 需要调用所需的生成系统来生成数据库,因此生成方法必须可用于 CLI。 此方法会创建包含生成的代码的数据库。 CodeQL 有两种方法来生成代码库:

此外,对于 C# 和 Java,可以选择生成数据库而不生成代码。 如果要为多个存储库启用 code scanning,此选项尤其有用。 有关详细信息,请参阅“CodeQL 生成模式”。

自动检测生成系统

CodeQL CLI 包括 C/C++、C#、Go、Java、Kotlin 和 Swift 代码的自动生成程序。 CodeQL 自动生成程序允许为编译语言生成项目,而无需指定任何生成命令。 调用自动生成程序时,CodeQL 检查源以获取生成系统的证据,并尝试运行提取数据库所需的最佳命令集。 有关详细信息,请参阅“对编译语言进行 CodeQL 代码扫描”。

如果不包含 --command 选项 或设置 --build-mode none,则为已编译语言执行 codeql database create 时自动调用自动生成程序。 例如,对于 C/C++ 代码库,只需运行:

codeql database create --language=cpp <output-folder>/cpp-database

如果代码库使用标准生成系统,则依赖于自动生成程序通常是创建数据库的最简单方法。 对于需要非标准生成步骤的源,可能需要在命令行中显式定义每个步骤。

注意:

  • 如果要生成 Go 数据库,请安装 Go 工具链(版本 1.11 或更高版本),如果有依赖项,则相应的依赖项管理器(如 dep)。
  • Go 自动生成程序尝试自动检测在存储库中用 Go 编写的代码,并且仅在尝试提取依赖项时运行生成脚本。 若要强制 CodeQL 将提取限制为由生成脚本编译的文件,请设置环境变量 CODEQL_EXTRACTOR_GO_BUILD_TRACING=on 或使用 --command 选项指定生成命令。

指定生成命令

以下示例旨在让你了解可为编译语言指定的一些生成命令。

注意:--command 选项接受单个参数,如果需要使用多个命令,请多次指定 --command。 如果需要传递子命令和选项,则需要引用整个参数才能正确解释。

  • 使用 make 生成的 C/C++ 项目:

    codeql database create cpp-database --language=c-cpp --command=make
    
  • 使用 dotnet build 生成的 C# 项目:

    最好添加 /t:rebuild 以确保生成所有代码,或者先执行 dotnet clean(未生成的代码不会包含在 CodeQL 数据库中):

    codeql database create csharp-database --language=csharp --command='dotnet build /t:rebuild'
    
  • 使用 CODEQL_EXTRACTOR_GO_BUILD_TRACING=on 环境变量生成的 Go 项目:

    CODEQL_EXTRACTOR_GO_BUILD_TRACING=on codeql database create go-database --language=go
    
  • 使用自定义生成脚本生成的 Go 项目:

    codeql database create go-database --language=go --command='./scripts/build.sh'
    
  • 使用 Gradle 生成的 Java 项目:

    # Use `--no-daemon` because a build delegated to an existing daemon cannot be detected by CodeQL.
    # To ensure isolated builds without caching, add `--no-build-cache` on persistent machines.
    codeql database create java-database --language=java-kotlin --command='gradle --no-daemon clean test'
    
  • 使用 Maven 生成的 Java 项目:

    codeql database create java-database --language=java-kotlin --command='mvn clean install'
    
  • 使用 Ant 生成的 Java 项目:

    codeql database create java-database --language=java-kotlin --command='ant -f build.xml'
    
  • 从 Xcode 项目或工作区生成的 Swift 项目。 默认情况下,将生成最大的 Swift 目标:

    最好确保项目处于干净状态,并且没有可用的生成项目。

    xcodebuild clean -all
    codeql database create -l swift swift-database
    
  • 使用 swift build 生成的 Swift 项目:

    codeql database create -l swift -c "swift build" swift-database
    
  • 使用 xcodebuild 生成的 Swift 项目:

    codeql database create -l swift -c "xcodebuild build -target your-target" swift-database
    

    可以将 archivetest 选项传递给 xcodebuild。 但是,建议使用标准 xcodebuild 命令,因为它应该是最快的命令,并且应该是 CodeQL 成功扫描所需的全部命令。

  • 使用自定义生成脚本生成的 Swift 项目:

    codeql database create -l swift -c "./scripts/build.sh" swift-database
    
  • 使用 Bazel 生成的项目:

    # Navigate to the Bazel workspace.
    
    # Before building, remove cached objects
    # and stop all running Bazel server processes.
    bazel clean --expunge
    
    # Build using the following Bazel flags, to help CodeQL detect the build:
    # `--spawn_strategy=local`: build locally, instead of using a distributed build
    # `--nouse_action_cache`: turn off build caching, which might prevent recompilation of source code
    # `--noremote_accept_cached`, `--noremote_upload_local_results`: avoid using a remote cache
    # `--disk_cache=`: avoid using a disk cache. Note that a disk cache is no longer considered a remote cache as of Bazel 6.
    codeql database create new-database --language=<language> \
    --command='bazel build --spawn_strategy=local --nouse_action_cache --noremote_accept_cached --noremote_upload_local_results --disk_cache= //path/to/package:target'
    
    # After building, stop all running Bazel server processes.
    # This ensures future build commands start in a clean Bazel server process
    # without CodeQL attached.
    bazel shutdown
    
  • 使用自定义生成脚本生成的项目:

    codeql database create new-database --language=<language> --command='./scripts/build.sh'
    

此命令运行包含生成项目所需的所有命令的自定义脚本。

使用间接生成跟踪

如果编译语言的 CodeQL CLI 自动生成程序不适用于 CI 工作流,并且无法使用 codeql database trace-command 包装生成命令的调用,可以使用间接生成跟踪来创建 CodeQL 数据库。 若要使用间接生成跟踪,CI 系统必须能够为每个生成操作设置自定义环境变量。

若要使用间接生成跟踪创建 CodeQL 数据库,请从项目的签出根目录运行以下命令:

codeql database init ... --begin-tracing <database>

必须指定以下内容:

  • <database>:要创建的新数据库的路径。 执行命令时将创建此目录,无法指定现有目录。
  • --begin-tracing:创建可用于设置环境的脚本,将在该环境中跟踪生成命令。

可以像平常一样为 codeql database init 命令指定其他选项。

注意:如果生成在 Windows 上运行,则必须设置 --trace-process-level <number>--trace-process-name <parent process name>,以便该选项指向父 CI 进程,该进程将观察所分析代码的所有生成步骤。

codeql database init 命令将输出消息:

Created skeleton <database>. This in-progress database is ready to be populated by an extractor. In order to initialise tracing, some environment variables need to be set in the shell your build will run in. A number of scripts to do this have been created in <database>/temp/tracingEnvironment. Please run one of these scripts before invoking your build command.

Based on your operating system, we recommend you run: ...

codeql database init 命令使用包含环境变量和值的文件创建 <database>/temp/tracingEnvironment,这些环境变量和值将启用 CodeQL 跟踪一系列生成步骤。 这些文件名为 start-tracing.{json,sh,bat,ps1}。 将其中一个文件与 CI 系统的机制结合使用,以便为将来的步骤设置环境变量。 方法:

  • 读取 JSON 文件,对其进行处理,然后以 CI 系统所需的格式输出环境变量。 例如,Azure DevOps 需要 echo "##vso[task.setvariable variable=NAME]VALUE"
  • 或者,如果 CI 系统保留该环境,请获取适当的 start-tracing 脚本以在 CI 系统的 shell 环境中设置 CodeQL 变量。

生成代码;(可选)使用存储 start-tracing 脚本的目录中的 end-tracing.{json,sh,bat,ps1} 脚本取消设置环境变量;然后运行命令 codeql database finalize <database>

使用间接生成跟踪创建 CodeQL 数据库后,可以像使用任何其他 CodeQL 数据库一样使用该数据库。 例如,如果使用的是代码扫描,请分析数据库并将结果上传到 GitHub。

使用间接生成跟踪创建 CodeQL 数据库的示例

注意:如果使用 Azure DevOps 管道,则创建 CodeQL 数据库最简单的方法是使用 GitHub Advanced Security for Azure DevOps。 有关文档,请参阅 Microsoft Learn 中的配置 GitHub Advanced Security for Azure DevOps

以下示例演示如何在 Azure DevOps 管道中使用间接生成跟踪来创建 CodeQL 数据库:

steps:
    # Download the CodeQL CLI and query packs...
    # Check out the repository ...

    # Run any pre-build tasks, for example, restore NuGet dependencies...

    # Initialize the CodeQL database.
    # In this example, the CodeQL CLI has been downloaded and placed on the PATH.
    - task: CmdLine@1
       displayName: Initialize CodeQL database
      inputs:
          # Assumes the source code is checked out to the current working directory.
          # Creates a database at `<current working directory>/db`.
          # Running on Windows, so specifies a trace process level.
          script: "codeql database init --language csharp --trace-process-name Agent.Worker.exe --source-root . --begin-tracing db"

    # Read the generated environment variables and values,
    # and set them so they are available for subsequent commands
    # in the build pipeline. This is done in PowerShell in this example.
    - task: PowerShell@1
       displayName: Set CodeQL environment variables
       inputs:
          targetType: inline
          script: >
             $json = Get-Content $(System.DefaultWorkingDirectory)/db/temp/tracingEnvironment/start-tracing.json | ConvertFrom-Json
             $json.PSObject.Properties | ForEach-Object {
                 $template = "##vso[task.setvariable variable="
                 $template += $_.Name
                 $template += "]"
                 $template += $_.Value
                 echo "$template"
             }

    # Execute the pre-defined build step. Note the `msbuildArgs` variable.
    - task: VSBuild@1
        inputs:
          solution: '**/*.sln'
          msbuildArgs: /p:OutDir=$(Build.ArtifactStagingDirectory)
          platform: Any CPU
          configuration: Release
          # Execute a clean build, in order to remove any existing build artifacts prior to the build.
          clean: True
       displayName: Visual Studio Build

    # Read and set the generated environment variables to end build tracing. This is done in PowerShell in this example.
    - task: PowerShell@1
       displayName: Clear CodeQL environment variables
       inputs:
          targetType: inline
          script: >
             $json = Get-Content $(System.DefaultWorkingDirectory)/db/temp/tracingEnvironment/end-tracing.json | ConvertFrom-Json
             $json.PSObject.Properties | ForEach-Object {
                 $template = "##vso[task.setvariable variable="
                 $template += $_.Name
                 $template += "]"
                 $template += $_.Value
                 echo "$template"
             }

    - task: CmdLine@2
       displayName: Finalize CodeQL database
       inputs:
          script: 'codeql database finalize db'

    # Other tasks go here, for example:
    # `codeql database analyze`
    # then `codeql github upload-results` ...

后续步骤