CodeQL静态分析Skill codeql

CodeQL 是一个静态代码分析框架,用于将代码作为数据库查询,支持跨过程控制和数据流分析,帮助开发者发现安全漏洞和代码缺陷。关键词:静态分析、代码安全、漏洞挖掘、数据流分析、控制流分析、CodeQL、安全测试。

漏洞挖掘 0 次安装 0 次浏览 更新于 3/24/2026

名称: codeql 类型: 工具 描述: > CodeQL 是一个静态分析框架,将代码作为数据库查询。 当您需要跨过程分析或复杂数据流跟踪时使用。

CodeQL

CodeQL 是一个强大的静态分析框架,允许开发者和安全研究人员查询代码库以寻找特定代码模式。CodeQL 标准库实现了跨过程和过程内控制流与数据流分析的支持。然而,编写自定义查询的学习曲线陡峭,CodeQL 标准库的文档仍然匮乏。

何时使用

使用 CodeQL 当:

  • 您需要在整个代码库中进行跨过程控制流和数据流查询
  • 需要对抽象语法树、控制流图和数据流图进行细粒度控制
  • 您希望防止已知漏洞和安全漏洞引入代码库
  • 您拥有源代码和第三方依赖的访问权限(并且可以构建编译语言)
  • 漏洞类别需要超出单文件模式匹配的复杂分析

考虑替代方案当:

  • 单文件模式匹配足够 → 考虑 Semgrep
  • 您没有源代码访问权限或无法构建项目
  • 分析时间关键(复杂查询可能耗时较长)
  • 您需要分析没有 GitHub Advanced Security 许可证的闭源代码库
  • 语言不被 CodeQL 支持

快速参考

任务 命令
创建数据库 (C/C++) codeql database create codeql.db --language=cpp --command='make -j8'
创建数据库 (Go) codeql database create codeql.db --language=go
创建数据库 (Java/Kotlin) codeql database create codeql.db --language=java
创建数据库 (JavaScript/TypeScript) codeql database create codeql.db --language=javascript
创建数据库 (Python) codeql database create codeql.db --language=python
分析数据库 codeql database analyze codeql.db --format=sarif-latest --output=results.sarif -- codeql/cpp-queries
列出已安装包 codeql resolve qlpacks
下载查询包 codeql pack download trailofbits/cpp-queries
运行自定义查询 codeql query run --database codeql.db -- path/to/Query.ql
测试自定义查询 codeql test run -- path/to/test/pack/

安装

安装 CodeQL

CodeQL 可以手动安装或在 macOS/Linux 上通过 Homebrew 安装。

手动安装: 导航到 CodeQL 发布页面 并下载适用于您架构的最新捆绑包。捆绑包包含 codeql 二进制文件、支持语言的查询库和预编译查询。

使用 Homebrew:

brew install --cask codeql

保持 CodeQL 更新

CodeQL 正在积极开发中。定期更新以受益于改进。

手动安装:CodeQL 发布页面 下载新更新。

Homebrew 安装:

brew upgrade codeql

验证

codeql --version

核心工作流

步骤 1: 构建 CodeQL 数据库

要构建 CodeQL 数据库,您通常需要能够构建相应代码库。确保代码库处于干净状态(例如,运行 make cleango clean 或类似命令)。

对于编译语言 (C/C++, Swift):

codeql database create codeql.db --language=cpp --command='make -j8'

如果使用 CMake 或外部构建,添加 --source-root 指定源文件树根目录:

codeql database create codeql.db --language=cpp --source-root=/path/to/source --command='cmake --build build'

对于解释语言 (Python, JavaScript):

codeql database create codeql.db --language=python

对于具有自动检测的语言 (Go, Java):

codeql database create codeql.db --language=go

对于复杂构建系统,使用 --command 参数传递构建命令。

步骤 2: 分析数据库

在数据库上运行预编译查询包:

codeql database analyze codeql.db --format=sarif-latest --output=results.sarif -- codeql/cpp-queries

输出格式包括 SARIF 和 CSV。SARIF 结果可以通过 VSCode SARIF Explorer 扩展 查看。

步骤 3: 审查结果

SARIF 文件包含发现项,带有位置、严重性和描述。导入到您的 IDE 或 CI/CD 管道中进行审查和修复。

安装第三方查询包

发布的查询包通过范围/名称/版本标识。例如:

codeql pack download trailofbits/cpp-queries trailofbits/go-queries

对于 Trail of Bits 公共 CodeQL 查询,请参阅 trailofbits/codeql-queries

如何自定义

编写自定义查询

CodeQL 查询使用声明式、面向对象的语言 QL,具有类似 Java 的语法和类似 SQL 的查询表达式。

基本查询结构:

import cpp

from FunctionCall call
where call.getTarget().getName() = "memcpy"
select call.getLocation(), call.getArgument(0)

这选择所有作为 memcpy 第一个参数传递的表达式。

创建自定义类:

import cpp

class MemcpyCall extends FunctionCall {
  MemcpyCall() {
    this.getTarget().getName() = "memcpy"
  }

  Expr getDestination() {
    result = this.getArgument(0)
  }

  Expr getSource() {
    result = this.getArgument(1)
  }

  Expr getSize() {
    result = this.getArgument(2)
  }
}

from MemcpyCall call
select call.getLocation(), call.getDestination()

关键语法参考

语法/运算符 描述 示例
from Type x where P(x) select f(x) 查询: 为所有 P(x) 为真的 x 选择 f(x) from FunctionCall call where call.getTarget().getName() = "memcpy" select call
exists(...) 存在量词 exists(FunctionCall call | call.getTarget() = fun)
forall(...) 全称量词 forall(Expr e | e = arg.getAChild() | e.isConstant())
+ 传递闭包 (1+ 次) start.getASuccessor+()
* 自反传递闭包 (0+ 次) start.getASuccessor*()
result 方法/函数输出的特殊变量 result = this.getArgument(0)

示例: 查找未处理的错误

import cpp

/**
 * @name 未处理的错误返回值
 * @id custom/unhandled-error
 * @description 返回错误代码但未检查的函数调用
 * @kind problem
 * @problem.severity warning
 * @precision medium
 */

predicate isErrorReturningFunction(Function f) {
  f.getName().matches("%error%") or
  f.getName().matches("%Error%")
}

from FunctionCall call
where
  isErrorReturningFunction(call.getTarget()) and
  not exists(Expr parent |
    parent = call.getParent*() and
    (parent instanceof IfStmt or parent instanceof SwitchStmt)
  )
select call, "错误返回值未检查"

添加查询元数据

查询元数据在初始注释中定义:

/**
 * @name 问题简短名称
 * @id 范围/查询名称
 * @description 问题详细描述
 * @kind problem
 * @tags security external/cwe/cwe-123
 * @problem.severity error
 * @precision high
 */

必填字段:

  • name: 标识问题的短字符串
  • id: 唯一标识符 (小写字母、数字、/-)
  • description: 详细描述 (几句话)
  • kind: problempath-problem
  • problem.severity: errorwarningrecommendation
  • precision: lowmediumhighvery-high

输出格式要求:

  • problem 查询: 输出必须是 (Location, string)
  • path-problem 查询: 输出必须是 (DataFlow::Node, DataFlow::PathNode, DataFlow::PathNode, string)

测试自定义查询

创建测试包并包含 qlpack.yml:

name: scope/name-test
version: 0.0.1
dependencies:
  codeql-query-pack-to-test: "*"
extractor: cpp

创建测试目录 (例如 MemcpyCall/) 包含:

  • test.c: 源代码文件,包含要检测的代码模式
  • MemcpyCall.qlref: 文本文件,包含查询路径
  • MemcpyCall.expected: 预期输出

运行测试:

codeql test run -- path/to/test/pack/

如果 MemcpyCall.expected 缺失或不正确,会创建 MemcpyCall.actual 文件。审查它,如果正确,重命名为 MemcpyCall.expected

高级用法

创建新查询包

初始化查询包:

codeql pack init <scope>/<name>

这会创建 qlpack.yml 文件:

---
library: false
warnOnImplicitThis: false
name: <scope>/<name>
version: 0.0.1

添加标准库依赖:

codeql pack add codeql/cpp-all

创建工作空间文件 (codeql-workspace.yml) 以便 CLI 正常工作。

安装依赖:

codeql pack install

配置 CLI 以找到您的查询,创建 ~/.config/codeql/config:

--search-path /full/path/to/your/codeql/root/directory

推荐目录结构

.
├── codeql-workspace.yml
├── cpp
│   ├── lib
│   │   ├── qlpack.yml
│   │   └── scope
│   │       └── security
│   │           └── someLibrary.qll
│   ├── src
│   │   ├── qlpack.yml
│   │   ├── suites
│   │   │   ├── scope-cpp-code-scanning.qls
│   │   │   └── scope-cpp-security.qls
│   │   └── security
│   │       └── AppSecAnalysis
│   │           ├── AppSecAnalysis.c
│   │           ├── AppSecAnalysis.qhelp
│   │           └── AppSecAnalysis.ql
│   └── test
│       ├── qlpack.yml
│       └── query-tests
│           └── security
│               └── AppSecAnalysis
│                   ├── AppSecAnalysis.c
│                   ├── AppSecAnalysis.expected
│                   └── AppSecAnalysis.qlref

递归和传递闭包

递归谓词:

predicate isReachableFrom(BasicBlock start, BasicBlock end) {
  start = end or isReachableFrom(start.getASuccessor(), end)
}

使用传递闭包 (等效):

predicate isReachableFrom(BasicBlock start, BasicBlock end) {
  end = start.getASuccessor*()
}

使用 * 表示零次或多次应用,+ 表示一次或多次。

排除单个文件

CodeQL 检测构建过程。如果目标文件已存在且是最新的,相应的源文件不会添加到数据库。这可以减少数据库大小,但意味着 CodeQL 对排除文件只有部分了解,无法推理通过它们的数据流。

推荐: 包含第三方库,并根据位置过滤问题,而不是在数据库创建时排除文件。

编辑器支持

VSCode: CodeQL 扩展 提供 LSP 支持、语法高亮、查询运行和 AST 可视化。

Neovim: codeql.nvim 提供类似功能。

Helix/其他编辑器: 使用 CodeQL LSP 服务器和 Tree-sitter 语法 for CodeQL

VSCode 快速查询: 使用 “CodeQL: Quick Query” 命令在数据库中运行单个查询。

调试查询: 添加数据库源到工作空间,然后使用 “CodeQL: View AST” 显示单个节点的 AST。

配置

CodeQL 标准库

CodeQL 标准库是语言特定的。请参考 API 文档:

支持的语言

CodeQL 支持 C/C++、C#、Go、Java、Kotlin、JavaScript、TypeScript、Python、Ruby 和 Swift。查看 支持的语言和框架 了解详情。

CI/CD 集成

GitHub Actions

从仓库设置中的 “Code security and analysis” 启用代码扫描。选择默认或高级设置。

高级设置工作流:

name: "CodeQL"

on:
  push:
    branches: [ "main" ]
  pull_request:
    branches: [ "main" ]
  schedule:
    - cron: '34 10 * * 6'

jobs:
  analyze:
    name: Analyze
    runs-on: ${{ (matrix.language == 'swift' && 'macos-latest') || 'ubuntu-latest' }}
    timeout-minutes: ${{ (matrix.language == 'swift' && 120) || 360 }}

    permissions:
      actions: read
      contents: read
      security-events: write

    strategy:
      fail-fast: false
      matrix:
        language: [ 'cpp' ]

    steps:
    - name: Checkout repository
      uses: actions/checkout@v4

    - name: Initialize CodeQL
      uses: github/codeql-action/init@v3
      with:
        languages: ${{ matrix.language }}

    - name: Autobuild
      uses: github/codeql-action/autobuild@v3

    - name: Perform CodeQL Analysis
      uses: github/codeql-action/analyze@v3
      with:
        category: "/language:${{matrix.language}}"

对于编译语言,用自定义构建命令替换 autobuild:

- run: |
    make -j8

在 CI 中使用自定义查询

在 “Initialize CodeQL” 步骤中指定查询包和查询:

- uses: github/codeql-action/init@v3
  with:
    queries: security-extended,security-and-quality
    packs: trailofbits/cpp-queries

对于仓库本地查询:

- uses: github/codeql-action/init@v3
  with:
    queries: ./codeql/UnhandledError.ql
    packs: trailofbits/cpp-queries

注意仓库相对路径的前缀 .。所有查询必须是查询包的一部分,并包含 qlpack.yml 文件。

在 CI 中测试自定义查询

name: Test CodeQL queries

on: [push, pull_request]

jobs:
  codeql-test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - id: init
        uses: github/codeql-action/init@v3
      - uses: actions/cache@v4
        with:
          path: ~/.codeql
          key: ${{ runner.os }}-${{ runner.arch }}-${{ steps.init.outputs.codeql-version }}
      - name: Run tests
        run: |
          ${{ steps.init.outputs.codeql-path }} test run ./path/to/query/tests/

此工作流缓存查询提取和编译以加快后续运行。

常见错误

错误 为什么错误 正确方法
创建数据库前未构建项目 CodeQL 不会有完整信息 运行 make clean 或等效命令,然后用 CodeQL 构建
从数据库中排除第三方库 阻止通过库代码的跨过程分析 包含库,根据位置过滤结果
在查询包中使用相对导入 导致解析问题 使用标准库的绝对导入
未添加查询元数据 SARIF 输出缺乏严重性、描述 始终添加必填字段的元数据注释
忘记工作空间文件 CLI 找不到查询包 在根目录创建 codeql-workspace.yml

限制

  • 许可: 闭源代码库需要 GitHub Enterprise 或 Advanced Security 许可证
  • 构建要求: 编译语言必须可构建;无构建 = 不完整数据库
  • 性能: 复杂跨过程查询在大型代码库上可能耗时较长
  • 语言支持: 仅限于 CodeQL 支持的语言和框架
  • 学习曲线: 编写自定义查询的学习曲线陡峭;文档匮乏
  • 单语言数据库: 每个数据库针对一种语言;多语言项目需要多个数据库

相关技能

技能 何时一起使用
semgrep 首先使用 Semgrep 进行快速模式分析,然后使用 CodeQL 进行更深层的跨过程分析
sarif-parsing 用于在自定义 CI/CD 管道中处理 CodeQL SARIF 输出

资源

Trail of Bits 关于 CodeQL 的博客文章

学习资源

编写自定义 CodeQL 查询

视频资源

使用 CodeQL 进行漏洞发现

CI/CD 中的 CodeQL