CodeQL(Mac)安装与测试(Visual Studio)简明指南

发布于:2025-09-06 ⋅ 阅读:(17) ⋅ 点赞:(0)

一、安装部分

1、下载对应版本的codeql:Releases · github/codeql-cli-binaries · GitHub

保存到本地codeql-cli文件夹内,cli即command-line interface。主要用来执行codeql的命令。

2、安装codeql仓库保存到本地codeql-repo文件夹,codeql仓库是包含多种语言的ql查询文件

3、总之,建立一个CodeQL的文件夹,文件夹里面新建3个文件,分别是:codeql-cli、codeql-repo、databases。

codeql-cli:里面放的就是codeql-osx64.zip的解压内容。

codeql-repo:里面放的是GitHub - github/codeql: CodeQL: the libraries and queries that power security researchers around the world, as well as code scanning in GitHub Advanced Security下载的压缩包解压内容。

databases:空文件夹,后面用来放codeql生成的数据库文件,现在暂时是空文件夹。

4、安装vscode(https://code.visualstudio.com/

5、配置codeql环境变量

open -e ~/.zshrc

    exportPATH="/Users/xxx/Desktop/tools/CodeQL/codeql-cli:$PATH"

source ~/.zshrc

6、在终端直接输入codeql -h,出现下面的信息,说明成功了

7、下载maven(https://maven.apache.org/download.cgi)并且配置路径


二、测试部分

1、下载测试靶场(GitHub - l4yn3/micro_service_seclab: Java漏洞靶场

2、生成数据库文件,出现Successfully,说明生成数据库文件成功.

codeql database create /Users/xxx/Desktop/tools/9-codeql/databases/test_database --language="java" --command="mvn clean install -DskipTests --file pom.xml" --source-root=/Users/xxx/Desktop/tools/9-codeql/micro_service_seclab

生成的数据库文件:

3、vscode配置ql数据库

安装codeql插件

将生成的数据库文件夹放到vscode上面的codeql插件上,如下:

至此,实现了codeql静态代码分析

三、codeql基本语法

CodeQL 使用一种专门的查询语言,名为 QL。它是一种声明式的、面向对象的逻辑编程语言。别被这些名词吓到,它的核心思想其实非常直观:将代码视为数据,通过写查询来从这些数据中寻找特定模式(比如漏洞)

一个标准的 CodeQL 查询结构非常类似于 SQL,主要由三个部分组成:fromwhereselect

1. 核心结构:from, where, select

这是所有查询的基础骨架,规定了“从哪里查、满足什么条件、输出什么结果”。

from /* 1. 声明变量和类型 */
where /* 2. 定义逻辑条件和约束 */
select /* 3. 指定输出内容 */
from 子句:声明变量和类型

from 的作用是引入我们想查询的“东西”的变量,并指定它们的“类型”。你可以把它理解为:“我要从代码的数据库里,找出所有类型为 A 的东西,并给它们起个名字叫 a”。

  • 类型(Classes): CodeQL 为不同语言预定义了大量的类型。例如:

    • Method:代表一个方法或函数。

    • MethodCall:代表一次方法或函数调用。

    • IfStmt:代表一个 if 语句。

    • Expr:代表一个表达式(Expression)。

    • Parameter:代表一个方法参数。

  • 变量(Variables): 你为指定类型的实例起的名字,方便在后面引用。

示例:

// 引入两个变量:
// - m 是一个方法 (Method)
// - call 是一次方法调用 (MethodCall)
from Method m, MethodCall call
where 子句:定义逻辑条件

where 是查询的核心,用来定义变量之间必须满足的逻辑关系。只有满足 where 中所有条件的组合,才会被 select 出来。你可以使用 and, or, not 等逻辑连接词。

  • 断言(Predicates): 这是 CodeQL 的“内置函数”,用来描述代码元素之间的关系。它们通常没有返回值,而是用来“断定”一个事实是否成立。

    • call.getCallee() = m: 这断定 call 这次方法调用所调用的目标方法正是 m

    • m.hasName("execute"): 这断定方法 m 的名字是 "execute"。

    • ifstmt.getCondition(): 获取 if 语句的条件表达式。

    • param.isType("string"): 判断参数 param 的类型是否是 string

示例:

from Method m, MethodCall call
where
  call.getCallee() = m and // 这次调用所调用的方法是 m
  m.hasName("println")    // 并且 m 的名字叫 "println"

这个 where 子句筛选出了所有调用名为 "println" 方法的调用事件。

select 子句:指定输出结果

select 用来定义最终的输出内容。你可以输出在 from 中声明的变量,也可以输出一段描述性的文字,或者两者的组合。

  • select 后面跟上变量名和你想展示的信息。

  • 你可以使用 as 关键字给输出的列起一个别名。

示例:

from Method m, MethodCall call
where
  call.getCallee() = m and
  m.hasName("println")
select call, "这是一个对 println 方法的调用"

这个查询最终会输出所有 MethodCall(即 call 变量)的实例,并在第二列附带一段固定的描述文字。


2. Putting It All Together: 一个完整的例子

让我们结合上面的知识,写一个完整的查询。

目标:在 Java 代码中,找到所有对 System.out.println 方法的调用。

import java // 导入 Java 相关的 CodeQL 库
from MethodCall call, Method printlnMethod // 声明变量:call 和 printlnMethod
where
  // 条件1: 我们要找的方法属于类 "PrintStream"
  printlnMethod.getDeclaringType().hasQualifiedName("java.io", "PrintStream") and
  // 条件2: 这个方法的名字叫 "println"
  printlnMethod.hasName("println") and
  // 条件3: call 这次调用实际调用的就是上面找到的方法
  call.getCallee() = printlnMethod
select call, "发现一次对 System.out.println 的调用" // 输出这次调用的实例和一段描述

3. 其他重要语法概念

类型和继承 (Classes and Inheritance)

CodeQL 的类型系统是面向对象的,支持继承。这非常强大,因为你可以查询一个更抽象的类型。

  • Expr (表达式) 是一个抽象类型。

  • MethodCall (方法调用), VariableAccess (变量访问), Literal (字面量) 等都继承自 Expr

所以,如果你 from Expr e,那么 e 就可以匹配代码中任何一种表达式。

断言 (Predicates)

断言是 CodeQL 的核心。你可以把它们看作是可重用的查询片段或自定义函数。断言分为两种:

  1. 无返回值的断言 (Predicate without result): 用来检查一个条件是否为真,常用于 where 子句。

    // 定义一个断言,判断一个方法是否是构造函数
    predicate isConstructor(Method m) {
      m.hasName(m.getDeclaringType().getName())
    }
    
    from Method m
    where isConstructor(m) // 在 where 中使用
    select m
    
  2. 有返回值的断言 (Predicate with result): 类似于一个有返回值的函数,可以返回计算结果。

    // 定义一个断言,返回一个方法的所有调用点
    MethodCall getACallTo(Method m) {
      result.getCallee() = m
    }
    
    from Method dangerousMethod
    where dangerousMethod.hasName("eval")
    select getACallTo(dangerousMethod) // 在 select 中使用
    

    这里的 result 是一个特殊关键字,代表该断言的返回值。

量词 (any, count, sum ...)

量词可以让你对一组值进行聚合计算或检查。

  • any(): 任意一个。

  • count(): 计数。

  • sum(): 求和。

  • avg(): 平均值。

  • min()/max(): 最小值/最大值。

示例:找到被调用超过10次的方法。

from Method m
where count(MethodCall call | call.getCallee() = m) > 10
select m, count(MethodCall call | call.getCallee() = m) as "调用次数"

这里的 | 用来分隔量词内部的变量声明和逻辑条件。

总之,我们可以根据我们自己的需求写ql脚本对代码进行审计,具体可以看看codeql官方文档学学怎么写CodeQL documentation

4、简单编写

我们现在从零开始,一步步为我自己的 Java 代码写一个简单的 QL 查询,目标是学会这个流程,理解基本结构,并明白每个文件的作用。

我们将写一个查询,用来寻找你 Java 代码中所有调用 System.out.println() 的地方。因为几乎所有 Java 项目里都有 println,而且它只涉及到最核心的“方法调用”概念,比较简单,方便我们理解;在很多生产项目中,直接使用 System.out.println 而不是专用的日志框架,通常被认为是一种不良实践。

第一步,创建一个 Java 示例项目 如果手上没有,可以快速创建一个。比如新建一个 MyTestApp 文件夹,在里面放一个 Main.java 文件

注意:我们需要一个规范的文件夹结构来存放我们的查询。CodeQL 不是单个 .ql 文件就能运行的,它需要一个“包”的概念。

第一步:创建一个MyTestApp的文件夹,在再 MyTestApp/src/main/java/com/example路径下创建一个Main.java文件。

// 文件: MyTestApp/src/main/java/com/example/Main.java
package com.example;
public class Main {
    public static void main(String[] args) {
        System.out.println("Hello, CodeQL!"); // 这是我们的目标
        String user = "admin";
        System.out.println("User is: " + user); // 这是另一个目标
    }
    public void uselessLog() {
        System.out.println("This is a useless log."); // 第三个目标
    }
}

  1. 为该项目创建 CodeQL 数据库 在 VS Code 中打开这个 MyTestApp 文件夹,然后用命令面板 (⌘+⇧+P) 运行 CodeQL: Create Database,选择 Java。

  2. 创建你的查询包 (Query Pack) 这是最关键的一步。在另外一个地方(不要在你的 Java 项目里),创建一个新的文件夹,专门用来存放你的自定义查询。比如,我们叫它 my-java-queries

在这个 my-java-queries 文件夹里,创建两个文件:

a) qlpack.yml (包配置文件) 这个文件是查询包的“身份证”,它告诉 CodeQL 这个包的名字、依赖项等信息。

# 文件: my-java-queries/qlpack.yml
name: my-custom-java-pack  # 给你的包起个名字
version: 0.0.1
library: false
dependencies:
  codeql/java-queries: ^0.8.0 # !!!至关重要:声明依赖官方Java查询库

dependencies 是最重要的部分。它表示我们的查询要构建在官方的 java-queries 库之上,这样我们才能使用像 MethodCall 这种预定义的 Java 类型。

b) FindPrintlnCalls.ql (QL 查询文件) 这是我们马上要编写查询逻辑的地方。现在可以先创建一个空文件。

  1. 将查询包添加到 VS Code 工作区

    • 在 VS Code 中,选择 File > Add Folder to Workspace...

    • 选择你刚刚创建的 my-java-queries 文件夹。

    • 现在你的 VS Code 左侧应该能同时看到 MyTestApp (你的Java代码) 和 my-java-queries (你的QL代码) 这两个文件夹了。

至此,环境准备完毕!我们知道了 qlpack.yml 是项目定义文件,.ql 是具体的查询文件。

第二步:编写 QL 查询语句(“基本结构”)

现在,打开 my-java-queries/FindPrintlnCalls.ql 文件,我们来一步步写查询。

1. 导入库和元数据

在文件最上方,我们先写一些“标准开头”。

/**
 * @name Find System.out.println calls
 * @description Finds all calls to the method System.out.println.
 * @kind problem
 * @id java/find-println-calls
 */
import java // 导入 CodeQL 的 Java 标准库
  • /** ... */ 里的内容是元数据 (Metadata)

    • @name@description 会显示在 VS Code 的界面里,方便你识别。

    • @kind problem 告诉 VS Code 这是一个寻找“问题”的查询,结果会以告警的形式展示。

    • @id 是这个查询的唯一标识符。

  • import java导入语句,它让我们能够使用所有 CodeQL 预先定义好的、用于分析 Java 的类和断言。

2. 编写 from-where-select 核心逻辑

接下来就是我们的 from-where-select 三部曲。

from
  MethodCall call // 1. from: 我们要找的东西是“方法调用”,我们给它起个名字叫 call
where
  // 2. where: 这个“方法调用”必须满足下面的条件
  call.getCallee().hasName("println") and // a) 它调用的方法,名字必须是 "println"
  call.getCallee().getDeclaringType().hasQualifiedName("java.io", "PrintStream") // b) 并且,声明这个方法的类的全限定名是 "java.io.PrintStream"
select
  call, "Avoid using System.out.println() in production code." // 3. select: 如果满足条件,就选中这个调用(call),并附带一句提示信息

逻辑分解:

  • from MethodCall call: MethodCall 是 CodeQL Java 库里预定义的一个类,代表了代码中所有的方法调用。我们声明一个变量 call 来代表 MethodCall 的每一个实例。

  • where: 这是筛选条件。

    • call.getCallee(): 这个断言 (predicate) 可以获取到 call 这个调用所指向的具体方法

    • .hasName("println"): 这是 Method 类的一个断言,判断方法名是否是 "println"。

    • .getDeclaringType(): 获取声明这个方法的

    • .hasQualifiedName("java.io", "PrintStream"): 这是 Type 类的一个断言,判断类的完整包名和类名。我们都知道 System.outjava.io.PrintStream 类型。

  • select call, "...": 这是输出。

    • 第一个参数 call 告诉 CodeQL 高亮显示代码中找到的方法调用。

    • 第二个参数是字符串,将作为结果的描述信息显示出来。


第三步:运行查询并查看结果

  1. 确保你当前选中的数据库是 MyTestApp 的数据库。

  2. 在你编写的 FindPrintlnCalls.ql 文件编辑器窗口中,单击右键

  3. 从菜单中选择 CodeQL: Run Query on Selected Database (和你截图中高亮的那个一样)。

  4. 等待几秒钟,VS Code 的 "CodeQL Results" 窗口就会弹出结果。

  5. 你应该能看到 3 个结果,点击其中任何一个,VS Code 都会自动跳转到 Main.java 文件里对应的 System.out.println(...) 那一行代码!

恭喜!你已经成功编写并运行了你的第一个自定义 CodeQL 查询!

学习参考:zangcc的【作者踩坑总结0错版】vscode配置codeql-MacBook(M1/M2芯片-arm).


网站公告

今日签到

点亮在社区的每一天
去签到