关于 CodeQL 分析工作流程和编译语言
代码扫描通过对一个或多个 CodeQL 数据库运行查询来工作。每个数据库都包含代码库中单一语言代码的表示。对于编译语言 C/C++、C#、Go、Java、Kotlin 和 Swift,填充此数据库的过程通常涉及构建代码和提取数据。
启用代码扫描时,默认设置和高级设置都将使用最简单的方法生成用于分析的 CodeQL 数据库。对于 C# 和 Java,CodeQL 数据库是直接从代码库生成的,无需构建(`none` 构建模式)。对于其他编译语言,CodeQL 将使用 `autobuild` 构建模式构建代码库。或者,您可以使用 `manual` 构建模式来指定显式构建命令,以便仅分析这些自定义命令构建的文件。
CodeQL 构建模式
CodeQL action 支持三种不同的编译语言构建模式
- `none` - 直接从代码库创建 CodeQL 数据库,无需构建代码库(所有解释型语言都支持,C# 和 Java 也支持)。
- `autobuild` - CodeQL 检测最可能的构建方法,并使用它尝试构建代码库并创建用于分析的数据库(所有编译语言都支持)。
- `manual` - 您在工作流程中定义要用于代码库的构建步骤(所有编译语言都支持)。
构建模式比较
| 构建模式特性 | 无 | 自动构建 | 手动 |
|---|---|---|---|
| 默认设置和组织级启用使用 | 是(C# 和 Java) | 是,在不支持 `none` 的情况下 | 否 |
| 无需用户配置即可成功分析 | 是 | 变量 | 否 |
| 分析的完整性 | 未分析生成的代码 | 变量 | 用户控制 |
| 分析的准确性 | 良好 | 良好 | 最佳 |
建议
首次设置代码扫描或跨多个代码库设置代码扫描时,最好使用默认设置。默认设置使用最简单的方法生成 CodeQL 数据库并分析您的代码,以便您可以尽快开始修复警报。解决初始警报后,您可能需要切换到具有手动构建过程的高风险代码库的高级设置。
在多语言代码库中使用多种构建模式
对于包含多种编译语言的代码库,您可以对不同的语言使用不同的构建模式。例如,如果您的代码库包含 C/C++、C# 和 Java,您可能需要为一种语言(此处为 C/C++)提供手动构建步骤。此工作流程为每种语言指定不同的构建模式。
strategy:
matrix:
include:
# Analyzes C and C++ code using the commands in `Build C and C++ code`
- language: c-cpp
build-mode: manual
# Analyzes C# code by automatically detecting a build
- language: csharp
build-mode: autobuild
# Analyzes Java code directly from the codebase without a build
- language: java-kotlin
build-mode: none # analyzes Java only
steps:
- name: Checkout repository
uses: actions/checkout@v4
# Initializes CodeQL tools and creates a codebase for analysis.
- name: Initialize CodeQL
uses: github/codeql-action/init@v3
with:
languages: ${{ matrix.language }}
- if: ${{ matrix.build-mode == 'manual' }}
name: Build C and C++ code
run: |
echo 'If you are using a "manual" build mode for one or more of the' \
'languages you are analyzing, replace this with the commands to build' \
'your code, for example:'
echo ' make bootstrap'
echo ' make release'
exit 1
有关 CodeQL 最新版本支持的语言、库和框架的信息,请参阅 CodeQL 文档中的“支持的语言和框架”。有关运行 CodeQL 最新版本的系统要求的信息,请参阅 CodeQL 文档中的“系统要求”。
关于 CodeQL 的“无”构建模式
对于 C# 和 Java,除非代码库还包含 Kotlin 代码,否则启用代码扫描的默认设置时,CodeQL 将在无需构建的情况下创建数据库。如果代码库除了 Java 代码外还包含 Kotlin 代码,则由于 Kotlin 分析需要构建,因此将使用自动构建过程启用默认设置。
如果出现以下情况,则在没有构建的情况下创建 CodeQL 数据库可能会产生不如使用 `autobuild` 或手动构建步骤准确的结果
- 无法查询构建脚本以获取依赖项信息,并且依赖项猜测不准确。
- 存储库通常在构建过程中生成代码。
要使用autobuild或手动构建步骤,您可以使用高级设置。
注意
对于Java分析,如果将build-mode设置为none并且在存储库中找到Kotlin代码,则不会分析Kotlin代码,并且会生成警告。请参阅“构建Java和Kotlin”。
关于CodeQL的自动构建
CodeQL操作在以下情况下使用autobuild分析编译语言。
- 启用默认设置,并且该语言不支持
none构建(C#和Java支持)。 - 启用高级设置,并且工作流指定
build-mode: autobuild。 - 启用高级设置,并且工作流具有使用
autobuild操作(github/codeql-action/autobuild@v3)的该语言的自动构建步骤。
使用build-mode选项的示例
# Initializes the CodeQL tools for scanning.
name: Analyze
strategy:
matrix:
include:
# Analyze C and C++ code
- language: c-cpp
build-mode: autobuild
# Analyze Go code
- language: go
build-mode: autobuild
steps:
- uses: github/codeql-action/init@v3
with:
languages: ${{ matrix.language }}
build-mode: ${{ matrix.build-mode }}
使用自动构建步骤的示例
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v3
with:
languages: ${{ matrix.language }}
- name: Autobuild
uses: github/codeql-action/autobuild@v3
关于手动指定构建步骤
只有在启用高级设置后才能指定手动构建步骤,请参阅“为代码扫描配置高级设置”。
如果autobuild失败,或者您想分析与autobuild过程构建的源文件不同的源文件集,则需要执行以下操作
- 如果您的工作流为该语言指定了构建模式,请将构建模式更改为
manual。 - 如果您的工作流包含
autobuild步骤,请删除或注释掉工作流中的autobuild步骤。
然后取消注释run步骤并手动指定要使用的构建过程。对于C/C++、C#、Go、Java、Kotlin和Swift,CodeQL将分析您的指定构建步骤构建的任何源代码。有关如何编辑工作流文件的更多信息,请参阅“自定义代码扫描的高级设置”。
更新您的工作流以将build-mode定义为manual。
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
- uses: github/codeql-action/init@v3
with:
languages: ${{ matrix.language }}
build-mode: manual
- uses: github/codeql-action/analyze@v3
with:
category: "/language:${{ matrix.language }}"
或者,更新您的工作流以注释掉“自动构建”步骤。
# Autobuild attempts to build any compiled languages.
# - name: Autobuild
# uses: github/codeql-action/autobuild@v3
指定构建命令
启用手动构建后,取消注释工作流中的run步骤并添加适合您存储库的构建命令。run步骤使用操作系统的shell运行命令行程序。您可以修改这些命令并添加更多命令来自定义构建过程。
- run: |
make bootstrap
make release
有关run关键字的更多信息,请参阅“GitHub Actions的工作流语法”。
如果您为编译语言添加了手动构建步骤,并且代码扫描仍然无法在您的存储库上运行,请通过GitHub支持门户与我们联系。
编译语言的自动构建步骤
GitHub托管的运行器始终使用autobuild所需的软件运行。如果您使用GitHub Actions的自托管运行器,则可能需要安装其他软件才能使用autobuild过程。此外,如果您的存储库需要特定版本的构建工具,则可能需要手动安装它。
注意
如果您的工作流使用language矩阵,autobuild会尝试构建矩阵中列出的每种编译语言。如果没有矩阵,autobuild会尝试构建存储库中源文件最多的受支持编译语言。除Go之外,除非您提供显式构建命令,否则您存储库中其他编译语言的分析将会失败。
构建C/C++
CodeQL支持C/C++代码的autobuild或manual构建模式。
C/C++的自动构建摘要
| 支持的系统类型 | 系统名称 |
|---|---|
| 操作系统 | Windows、macOS和Linux |
| 构建系统 | Windows:MSbuild和构建脚本 Linux和macOS:Autoconf、Make、CMake、qmake、Meson、Waf、SCons、Linux Kbuild和构建脚本 |
autobuild步骤的行为会根据运行提取的操作系统而有所不同。
Windows自动检测
在Windows上,autobuild步骤尝试使用以下方法自动检测适用于C/C++的合适构建方法
- 在最接近根目录的解决方案(
.sln)或项目(.vcxproj)文件上调用MSBuild.exe。如果autobuild在与顶级目录相同的(最短)深度检测到多个解决方案或项目文件,它将尝试构建所有这些文件。 - 调用看起来像构建脚本的脚本——build.bat、build.cmd和build.exe(按此顺序)。
Linux和macOS自动检测
在Linux和macOS上,autobuild步骤会查看存储库中存在的文件以确定使用的构建系统
- 在根目录中查找构建系统。
- 如果没有找到,请搜索子目录中是否存在具有C/C++构建系统的唯一目录。
- 运行适当的命令来配置系统。
C/C++的运行器要求
在Ubuntu Linux运行器上,autobuild可能会尝试自动安装检测到的配置和构建步骤所需的依赖项。默认情况下,此行为在GitHub托管的运行器上启用,在自托管的运行器上禁用。您可以通过将环境中的CODEQL_EXTRACTOR_CPP_AUTOINSTALL_DEPENDENCIES设置为true或false来显式启用或禁用此功能。有关定义环境变量的更多信息,请参阅“在变量中存储信息”。
对于自托管运行器,除非启用了依赖项的自动安装,否则您可能需要安装gcc编译器,并且特定项目可能还需要访问clang或msvc可执行文件。您还需要安装您的项目依赖的构建系统(例如msbuild、make、cmake、bazel)和实用程序(例如python、perl、lex和yacc)。如果您启用了依赖项的自动安装,则必须确保运行器正在使用Ubuntu并且它可以在无需密码的情况下运行sudo apt-get。
Windows运行器需要powershell.exe位于PATH中。
构建C#
CodeQL支持C#代码的none、autobuild或manual构建模式。
当您为包含C#代码的存储库启用默认设置时,构建模式会自动设置为none。
C#的无构建
CodeQL会还原依赖项并生成一些额外的源文件,以提供更准确的结果,然后从所有源文件和依赖项创建数据库。
依赖项使用多种启发式方法和策略进行还原。以下文件是主要信息来源:*.csproj、*.sln、nuget.config、packages.config、global.json和project.assets.json。
以下生成的源文件是可选的,但会显著提高CodeQL数据库的正确性
global生成的using指令用于处理MSbuild的隐式using功能。- ASP.NET Core视图文件(
.cshtml文件)被转换为.cs文件。
依赖程序集名称、生成的源文件和存储库中的源文件的信息将被编译并用于创建CodeQL数据库。
C#无构建分析的准确性
创建CodeQL数据库而无需构建完整代码依赖于能够还原依赖项并能够将存储库中的源文件编译在一起。当还原依赖项或编译源代码时出现问题时,这会影响CodeQL数据库和代码扫描分析结果的准确性。
您可以通过以下步骤确保更准确的分析
- 提供对公共互联网的访问,或确保可以访问私有Nuget feed。
- 检查存储库是否需要多个版本的相同Nuget依赖项。CodeQL只能使用一个版本,通常选择较新的版本(如果有多个版本)。这种方法可能不适用于所有存储库。
- 检查是否引用了多个版本的.NET,例如
net48、net5.0和netstandard1.6。CodeQL只能使用一个版本,这可能会影响准确性。 - 避免类名冲突,否则这可能会导致缺少方法调用目标,从而影响数据流分析。
C#的自动构建摘要
| 支持的系统类型 | 系统名称 |
|---|---|
| 操作系统 | Windows、macOS和Linux |
| 构建系统 | .NET和MSbuild,以及构建脚本 |
CodeQL注入的C#编译器标志
注意
以下编译器标志仅适用于您使用构建模式manual的情况。
CodeQL跟踪器通过拦截构建过程并将信息转发到相关的CodeQL语言提取器来启用所有编译语言的提取。跟踪器将某些标志注入C#编译器调用,以确保每个组件都被构建并包含在CodeQL数据库中,这可能会导致您的C#代码在CodeQL分析期间的构建方式与您的预期不同。
/p:MvcBuildViews=true
当此选项设置为true时,ASP.NET 模型-视图-控制器 (MVC) 项目中的视图会在构建过程中进行预编译,这有助于捕获错误并提高性能。跟踪器注入此标志以确保 CodeQL 查找并突出显示可能涉及通过从这些视图生成的代码进行数据流的安全性问题。更多信息,请参见 Microsoft Learn 中的“向 MVC 应用程序添加视图”。
/p:UseSharedCompilation=false
将此选项设置为false 将禁用共享编译功能的使用,这可能会导致构建时间变慢。当未指定/p:UseSharedCompilation=false时,msbuild会启动一个编译器服务器进程,所有编译都将由该单个进程完成。但是,CodeQL 跟踪器依赖于检查新创建进程的参数。
/p:EmitCompilerGeneratedFiles=true
将此选项设置为true将在构建过程中发出编译器生成的的文件。此选项会导致编译器生成用于支持改进的正则表达式支持、序列化和 Web 应用程序视图生成的附加源文件。这些生成的工件通常不会被编译器写入磁盘,但是将选项设置为true会强制将文件写入磁盘,因此提取器可以处理这些文件。
对于某些旧版项目和使用.sqlproj文件的项目,您可能会发现注入的/p:EmitCompilerGeneratedFiles=true属性会导致msbuild出现意外问题。有关此问题的故障排除信息,请参见“C# 编译器意外失败”。
Windows自动检测
autobuild 进程尝试使用以下方法自动检测适用于 C# 的合适的构建方法
- 在最接近根目录的解决方案 (
.sln) 或项目 (.csproj) 文件上调用dotnet build。 - 在最接近根目录的解决方案或项目文件上调用
MSBuild.exe。如果autobuild检测到多个解决方案或项目文件与顶级目录的距离相同(最短),它将尝试构建所有这些文件。 - 调用看起来像构建脚本的脚本—
build.bat、build.cmd和build.exe(按此顺序)。
Windows 上 C# 的运行器要求
对于在自托管运行器上进行的 .NET Core 应用程序开发,需要 .NET SDK(用于dotnet)。
对于 .NET Framework 应用程序开发,您需要 Microsoft Build Tools(用于msbuild)和 Nuget CLI(用于nuget)。
Windows运行器需要powershell.exe位于PATH中。
如果您计划使用build-mode: none创建 CodeQL 数据库,您还需要提供对公共互联网的访问权限,或者必须确保可以访问私有 Nuget 源。
Linux和macOS自动检测
- 在最接近根目录的解决方案 (
.sln) 或项目 (.csproj) 文件上调用dotnet build。 - 在最接近根目录的解决方案或项目文件上调用
MSbuild。如果autobuild检测到多个解决方案或项目文件与顶级目录的距离相同(最短),它将尝试构建所有这些文件。 - 调用看起来像构建脚本的脚本—
build和build.sh(按此顺序)。
Linux 和 macOS 上 C# 的运行器要求
对于在自托管运行器上进行的 .NET Core 应用程序开发,需要 .NET SDK(用于dotnet)。
对于 .NET Framework 应用程序开发,您需要 Mono Runtime(用于运行mono、msbuild或nuget)。
如果您计划使用build-mode: none创建 CodeQL 数据库,您还需要提供对公共互联网的访问权限,或者必须确保可以访问私有 Nuget 源。
构建 Go
CodeQL 支持 Go 代码的autobuild或manual构建模式。
Go 的自动构建摘要
| 支持的系统类型 | 系统名称 |
|---|---|
| 操作系统 | Windows、macOS和Linux |
| 构建系统 | Go 模块、dep和 Glide,以及包括 Makefile 和 Ninja 脚本在内的构建脚本 |
Go 的自动检测
autobuild进程尝试自动检测在提取所有.go文件之前安装 Go 存储库所需的依赖项的合适方法
- 调用
make、ninja、./build或./build.sh(按此顺序),直到其中一个命令成功并且后续的go list ./...也成功,表明已安装所需的依赖项。 - 如果这些命令均未成功,则查找
go.mod、Gopkg.toml或glide.yaml,并分别运行go get(除非使用版本控制)、dep ensure -v或glide install来尝试安装依赖项。 - 最后,如果找不到这些依赖项管理器的配置文件,请重新排列适合添加到
GOPATH的存储库目录结构,并使用go get安装依赖项。提取完成后,目录结构将恢复正常。 - 提取存储库中的所有 Go 代码,类似于运行
go build ./...。
注意
如果您使用默认设置,它将查找go.mod文件以自动安装兼容版本的 Go 语言。
Go 的提取器选项
默认情况下,不会分析测试代码(以_test.go结尾的文件中的代码)。使用 CodeQL CLI 时,您可以使用选项--extractor-option extract_tests=true覆盖此选项,或者通过将环境变量CODEQL_EXTRACTOR_GO_OPTION_EXTRACT_TESTS设置为true来覆盖此选项。
此外,默认情况下,vendor目录不包含在 CodeQL Go 分析中。您可以通过在使用 CodeQL CLI 时传递--extractor-option extract_vendor_dirs=true选项来覆盖此选项,或者通过将环境变量CODEQL_EXTRACTOR_GO_OPTION_EXTRACT_VENDOR_DIRS设置为true来覆盖此选项。
构建 Java 和 Kotlin
CodeQL 支持以下构建模式。
- Java:
none、autobuild或manual - Kotlin:
autobuild或manual
当您首次为存储库启用默认设置时,如果仅检测到 Java 代码,则构建模式将设置为none。如果检测到 Kotlin 或 Java 和 Kotlin 代码的组合,则构建模式将设置为autobuild。
如果您稍后将 Kotlin 代码添加到使用none构建模式的存储库中,CodeQL 分析将报告一条警告消息,说明不支持 Kotlin。您需要禁用默认设置并重新启用它。重新启用默认设置时,构建模式将更改为autobuild,以便可以分析这两种语言。或者,您可以更改为高级设置。更多信息,请参见“警告:在您的项目中检测到 X 个 Kotlin 文件,如果没有构建,则无法处理这些文件”。
Java 的无构建
在从所有存在的 Java 文件创建数据库之前,CodeQL 将尝试运行 Gradle 或 Maven 以提取准确的依赖项信息(但不调用构建)。每个根 Maven 或 Gradle 项目文件(祖先目录中没有任何构建脚本的构建脚本)都会查询依赖项信息,如果发生冲突,则更喜欢更新的依赖项版本。有关运行 Maven 或 Gradle 的运行器要求的信息,请参见“Java 的运行器要求”。
Java 无构建分析的准确性
如果没有构建就创建 CodeQL Java 数据库,如果出现以下情况,则产生的结果可能不如使用autobuild或手动构建步骤准确
- 无法查询 Gradle 或 Maven 构建脚本以获取依赖项信息,并且依赖项猜测(基于 Java 包名称)不准确。
- 存储库通常会在构建过程中生成代码。如果您使用不同的模式创建 CodeQL 数据库,则会对此进行分析。
您可以通过以下步骤确保更准确的分析
- 提供对公共互联网的访问权限,或确保可以访问私有构件存储库。
- 检查存储库是否需要多个版本的相同依赖项。CodeQL 只能使用一个版本,通常会选择较新的版本(如果有多个版本)。这种方法可能不适用于所有存储库。
- 检查不同的源 Java 文件是否需要多个版本的 JDK API。当看到多个版本时,CodeQL 将使用任何构建脚本所需的最高版本。这意味着某些需要较低版本 JDK 的文件将被部分分析。例如,如果某些文件需要 JDK 8,但在一个或多个构建脚本中找到了 JDK 17 要求,则 CodeQL 将使用 JDK 17。任何需要 JDK 8 并且无法使用 JDK 17 构建的文件都将被部分分析。
- 避免类名冲突(例如,多个文件定义
org.myproject.Test),否则这可能会导致缺少方法调用目标,从而影响数据流分析。
Java 的自动构建摘要
| 支持的系统类型 | 系统名称 |
|---|---|
| 操作系统 | Windows、macOS 和 Linux(无限制) |
| 构建系统 | Gradle、Maven 和 Ant |
Java 的自动检测
autobuild进程尝试通过应用以下策略来确定 Java 代码库的构建系统
- 在根目录中搜索构建文件。检查 Gradle,然后检查 Maven,然后检查 Ant 构建文件。
- 运行找到的第一个构建文件。如果同时存在 Gradle 和 Maven 文件,则使用 Gradle 文件。
- 否则,搜索根目录的直接子目录中的构建文件。如果只有一个子目录包含构建文件,则运行在该子目录中标识的第一个文件(使用与 1 相同的优先级)。如果多个子目录包含构建文件,则报告错误。
Java 的运行器要求
如果您使用的是自托管运行器,则应存在所需的 Java 版本。
-
如果运行器将用于分析需要单个 Java 版本的存储库,则需要安装相应的 JDK 版本,并且需要将其放在 PATH 变量中(以便可以找到
java和javac)。 -
如果运行器将用于分析需要多个 Java 版本的存储库,则需要安装相应的 JDK 版本,并且可以通过
toolchains.xml文件指定。这是一个配置文件,通常由 Apache Maven 使用,它允许您指定工具的位置、工具的版本以及使用工具所需的任何其他配置。更多信息,请参见 Apache Maven 文档中的“使用工具链的指南”。
以下可执行文件可能在一系列 Java 项目中需要,并且应该出现在 PATH 变量中,但在所有情况下都不必。
mvn(Apache Maven)gradle(Gradle)ant(Apache Ant)
您还需要安装构建系统(例如make、cmake、bazel)和项目依赖的实用程序(例如python、perl、lex和yacc)。
Windows运行器需要powershell.exe位于PATH中。
构建 Swift
CodeQL 支持 Swift 代码的autobuild或manual构建模式。
Swift 的自动构建摘要
| 支持的系统类型 | 系统名称 |
|---|---|
| 操作系统 | macOS |
| 构建系统 | Xcode |
autobuild进程尝试从 Xcode 项目或工作区构建最大的目标。
默认情况下,Swift 代码的代码扫描使用 macOS 运行器。由于 GitHub 托管的 macOS 运行器比 Linux 和 Windows 运行器更昂贵,因此我们建议您只构建要分析的代码。有关 GitHub 托管运行器定价的更多信息,请参阅“GitHub Actions 的计费说明”。
对于属于 Actions Runner Controller (ARC) 的运行器,不支持 Swift 代码的代码扫描,因为 ARC 运行器仅使用 Linux,而 Swift 需要 macOS 运行器。但是,您可以同时使用 ARC 运行器和自托管的 macOS 运行器。有关更多信息,请参阅“关于 Actions Runner Controller”。
在 CodeQL 分析工作流中自定义 Swift 编译
xcodebuild 和 swift build 都支持 Swift 构建。我们建议在构建过程中只针对一个架构。例如,对于 xcodebuild 使用 ARCH=arm64,或者对于 swift build 使用 --arch arm64。
您可以将 archive 和 test 选项传递给 xcodebuild。但是,推荐使用标准的 xcodebuild 命令,因为它应该最快,并且应该是 CodeQL 成功扫描所需的一切。
对于 Swift 分析,您必须始终在生成 CodeQL 数据库之前显式安装通过 CocoaPods 或 Carthage 管理的依赖项。