GitHub CodeQL 语义分析

FreeGuideOnline 16阅读 2026-07-13

什么是 GitHub CodeQL?

CodeQL 是 GitHub 推出的一款语义代码分析引擎。与传统的静态分析工具不同,它不只进行模式匹配或语法检查,而是将你的代码当作数据来查询。它通过构建一个完整的代码数据库,把源码解析为关系型表示,然后你可以像查询数据库一样编写查询来发现各类漏洞、错误与模式。

  • 语义分析:理解代码的“含义”,能够识别数据流、控制流、类型层级等。
  • 变体分析:找到某个已知漏洞的所有相似形态,而不仅仅是字面匹配。
  • 集成于 CI/CD:通过 GitHub Actions 自动扫描每个拉取请求,防止问题合入主分支。

环境准备与基础概念

安装 CodeQL CLI 与 VS Code 扩展

  1. 下载 CodeQL CLI 二进制包。
  2. 解压并配置环境变量,使 codeql 命令全局可用。
  3. 在 VS Code 中搜索并安装 CodeQL 扩展。
  4. 将下载的 CLI 目录路径填入扩展设置 “CodeQL CLI Executable Path”。

之后你便可以直接在编辑器里编写、执行查询并查看结果。

核心概念:数据库与查询

  • CodeQL 数据库:包含代码的抽象语法树(AST)、数据流关系、控制流图、类型信息等。可通过 codeql database create <db-name> 创建。
  • QL 包:可复用的查询或库的集合,通常结构为 codeql-<语言>-queries
  • 查询文件:以 .ql 为后缀,包含一个或多个 from...where...select 子句。

QL 语言快速入门

逻辑查询与类声明

QL 是一种声明性、面向对象的查询语言,核心结构为:

from Variable v, VariableUse use
where v = use.getTarget() and
      v.getName() = "password"
select use, "变量 'password' 的不可信引用"

你还可以定义来描述特定类型的代码元素:

class SensitiveVariable extends Variable {
  SensitiveVariable() {
    this.getName().regexpMatch("secret|password|token")
  }
}

这样在后续查询中就可以直接使用 SensitiveVariable

数据流分析

CodeQL 的真正威力在于数据流。它提供了多个标准库模块来实现污点追踪。

import cpp
import semmle.code.cpp.dataflow.DataFlow

from DataFlow::Node source, DataFlow::Node sink
where source = /* 危险输入源 */ and
      sink = /* 潜在不安全汇点 */ and
      DataFlow::flow(source, sink)
select sink, "未经验证的输入流入危险函数"

运用 DataFlow::flow 可以帮助你发现跨函数、跨文件的污点传播路径。

创建你的第一个代码扫描分析

1. 生成 CodeQL 数据库

以 Java 项目为例:

codeql database create my-db --language=java --command="mvn clean compile"

--command 应包含构建项目所需的完整指令。

2. 编写查询:不安全的反序列化

新建 UnsafeDeserialization.ql

/**
 * @name 不安全的反序列化
 * @description 识别出从不可信数据源直接进行 Java 反序列化的位置
 * @kind path-problem
 * @id java/unsafe-deserialization
 */

import java
import semmle.code.java.dataflow.DataFlow
import semmle.code.java.dataflow.ExternalFlow

class UntrustedSource extends SourceNode {
  UntrustedSource() { this.asExpr() instanceof RemoteInput }
}

class DeserializationSink extends SinkNode {
  DeserializationSink() {
    exists(MethodAccess ma |
      ma.getMethod().hasName("readObject") or
      ma.getMethod().hasName("readUnshared")
    |
      this.asExpr() = ma.getQualifier()
    )
  }
}

from UntrustedSource src, DeserializationSink sink, DataFlow::PathNode sourceNode, DataFlow::PathNode sinkNode
where DataFlow::flowPath(src, sink) and
      flowPath(sourceNode, sinkNode)
select sinkNode, sourceNode, sinkNode, "不可信数据流入 $@.", sinkNode, "反序列化调用"
  • path-problem 类型的查询可以生成可追踪的数据流路径。
  • 利用 RemoteInput 识别出网络或用户输入来源。
  • DataFlow::flowPath 捕获完整路径供查看。

3. 运行查询并解读结果

codeql query run UnsafeDeserialization.ql --database=my-db --output=results.bqrs
codeql bqrs decode --format=csv results.bqrs --output=results.csv

VS Code 扩展可以直接将结果以高亮路径形式展示在编辑器中,每条告警均可点击查看数据从源到汇的完整步骤。

将分析集成到 GitHub 仓库

使用默认的 CodeQL 工作流

在仓库的 .github/workflows/codeql.yml 中启用:

name: "CodeQL"
on:
  push:
    branches: [ main ]
  pull_request:
    branches: [ main ]
jobs:
  analyze:
    name: Analyze
    runs-on: ubuntu-latest
    permissions:
      security-events: write
    steps:
      - uses: actions/checkout@v3
      - uses: github/codeql-action/init@v2
        with:
          languages: java
      - run: |
                    mvn clean compile
      - uses: github/codeql-action/analyze@v2

每次推送代码,GitHub 都会执行分析并将安全告警推送到仓库的“Security”选项卡下。

自定义查询包

可以将自己写的 .ql 文件放入 .github/codeql/custom-queries 目录,并在工作流中指定:

- uses: github/codeql-action/init@v2
  with:
    queries: .github/codeql/custom-queries

这样你的专用规则就会与内置查询一起运行。

高级语义分析技巧

函数建模与数据流扩展

当默认的数据流规则无法识别某些自定义框架时,你可以自行添加建模:

  • 源定义class MySource extends DataFlow::Node { ... }
  • 阶梯谓词MySource::flowsTo(DataFlow::Node sink)
  • 利用 QL 的 override 机制扩展现有 TaintTracking 配置。

例如,用于识别通过自定义 @Secured 注解标记的函数返回值不应被视为可信输入。

跨模块分析

针对多模块、多仓库的项目,CodeQL 支持将多个数据库视为一个联合数据库进行分析,只需在创建数据库时指定额外模块目录:

codeql database create combined-db --language=java --command="..." --source-root=.

自定义类继承检测

检查抽象类或接口的不正确实现:

from Interface i, Class c
where c.getAnAncestor() = i and
      not c.declaresMethod(i.getAMethod())
select c, "未实现接口 " + i.getName() + " 中的方法"

排查常见问题

  • 数据库创建失败:检查 --command 使用的构建指令是否能在当前环境无错执行。
  • 查询无结果:确认查询中的谓词是否匹配实际代码结构。可以用 select any() 快速查看元素数量。
  • 性能过慢:避免使用无限制的递归,为复杂类添加 exists 约束。
  • 扩展无法加载:确保 qlpack.yml 正确声明依赖,并在 VS Code 中通过 CodeQL: Install Pack Dependencies 安装。

总结与学习路径

CodeQL 的语义分析能力为代码安全审查与缺陷发现带来了质变。从基础的查询语法,到复杂的数据流变体分析,再到 CI/CD 全自动集成,它提供了一套覆盖研发全流程的现代化代码健康保障体系。

下一步你可以:

开始用语义的力量,重新审视你的代码。