CodeQL 文档

Java 和 Kotlin 代码的基本查询

了解如何使用带有 CodeQL 扩展的 Visual Studio Code 编写和运行简单的 CodeQL 查询。

有关安装 VS Code 版 CodeQL 扩展的信息,请参阅“安装 VS Code 版 CodeQL。”

关于查询

我们将运行的查询搜索用于空字符串的低效测试。例如,以下 Java 代码

public class TestJava {
    void myJavaFun(String s) {
        boolean b = s.equals("");
    }
}

或以下 Kotlin 代码

void myKotlinFun(s: String) {
    var b = s.equals("")
}

在这两种情况下,将 s.equals("") 替换为 s.isEmpty() 将更有效。

查找用于实验的 CodeQL 数据库

在开始为 Java/Kotlin 代码编写查询之前,您需要一个 CodeQL 数据库来运行这些查询。最简单的方法是直接从 GitHub.com 下载使用 Java/Kotlin 的存储库的数据库。

  1. 在 Visual Studio Code 中,单击左侧边栏中的 **QL** 图标 CodeQL 扩展的图标。 以显示 CodeQL 扩展。
  2. 单击 CodeQL 扩展顶部的 **从 GitHub 获取** 或 GitHub 徽标 CodeQL 扩展选项的图标,用于从 GitHub 下载 CodeQL 数据库。 以打开一个输入字段。
  3. 将存储库的 URL 复制到该字段,然后按键盘 **Enter** 键。例如,https://github.com/apache/activemq.
  4. 可选:如果存储库提供多个 CodeQL 数据库,请选择 java 以下载从 Java/Kotlin 代码创建的数据库。

有关数据库下载进度的信息将显示在 Visual Studio Code 的右下角。下载完成后,该数据库将在 CodeQL 扩展的 **数据库** 部分中显示带有复选标记的图标(见下图)。

运行快速查询

VS Code 版 CodeQL 扩展向命令面板添加了几个 **CodeQL:** 命令,包括 **快速查询**,您可以使用它在没有任何设置的情况下运行查询。

  1. 从 Visual Studio Code 中的命令面板中,选择 **CodeQL: 快速查询**。

  2. 片刻之后,将打开一个新选项卡 *quick-query.ql*,您可以为当前选定的 CodeQL 数据库(此处为 java 数据库)编写查询。如果您收到提示,要求将工作区重新加载为多文件夹工作区以允许快速查询,请接受或使用入门工作流创建新工作区。

    image-quick-query

  1. 在快速查询选项卡中,删除 select "",并将以下查询粘贴到导入语句 import java 下面。

    from MethodAccess ma
    where
        ma.getMethod().hasName("equals") and
        ma.getArgument(0).(StringLiteral).getValue() = ""
    select ma, "This comparison to empty string is inefficient, use isEmpty() instead."
    

    请注意,CodeQL 将 Java 和 Kotlin 视为同一语言的一部分,因此即使此查询以 import java 开头,它也适用于 Java 和 Kotlin 代码。

  1. 将查询保存到其默认位置(工作区的临时“快速查询”目录,位于 GitHub.vscode-codeql/quick-queries 下)。

  2. 在查询选项卡中右键单击,然后选择 **CodeQL: 在所选数据库上运行查询**。(或者,从命令面板运行该命令。)

    查询将需要几秒钟才能返回结果。查询完成后,结果将显示在 CodeQL 查询结果视图中,位于主编辑器视图旁边。

    查询结果将列在两列中,分别对应于查询的 select 子句中的表达式。第一列对应于表达式 ma,并链接到项目源代码中出现 ma 的位置。第二列是警报消息。

../../_images/basic-java-query-results-1.png

如果找到任何匹配的代码,请单击 ma 列中的链接以在代码查看器中查看 .equals 表达式。

../../_images/basic-java-query-results-2.png

注意

如果要将实验查询移动到更永久的位置,则需要移动整个 Quick Queries 目录。该目录是一个 CodeQL 包,包含一个 qlpack.yml 文件,该文件将内容定义为针对 Java/Kotlin CodeQL 数据库的查询。有关 CodeQL 包的更多信息,请参阅“管理 CodeQL 查询包和库包。”

关于查询结构

在初始的 import 语句之后,此简单查询包含三个部分,它们的作用与 SQL 查询的 FROM、WHERE 和 SELECT 部分类似。

查询部分 目的 详细信息
import java 导入 Java 和 Kotlin 的标准 CodeQL 库。 每个查询都以一个或多个 import 语句开头。
from MethodAccess ma 定义查询的变量。声明的格式为:<type> <variable name>

我们使用

  • 一个 MethodAccess 变量来表示调用表达式
where ma.getMethod().hasName("equals") and ma.getArgument(0).(StringLiteral).getValue() = "" 定义对变量的条件。

ma.getMethod().hasName("equals")ma 限制为仅调用名为 equals 的方法。

ma.getArgument(0).(StringLiteral).getValue() = "" 表示参数必须是文字 ""

select ma, "This comparison to empty string is inefficient, use isEmpty() instead."

定义对每个匹配项报告的内容。

用于查找代码质量低下的实例的查询的 select 语句始终采用以下格式:select <program element>, "<alert message>"

报告生成的 .equals 表达式以及一个解释问题的字符串。

扩展查询

查询编写本质上是一个迭代过程。您编写一个简单的查询,然后在运行它时,您会发现之前没有考虑过的示例,或者发现改进的机会。

删除误报结果

浏览基本查询的结果表明它可以改进。例如,您可能会发现以下代码的结果

public class TestJava {
    void myJavaFun(Object o) {
        boolean b = o.equals("");
    }
}

在这种情况下,不能简单地使用 o.isEmpty() 来代替,因为 o 的类型为 Object 而不是 String。对此的一种解决方案是修改查询,使其仅返回表达式被测试的类型为 String 的结果

  1. 扩展 where 子句以包括以下额外条件

    ma.getQualifier().getType() instanceof TypeString
    

    现在,where 子句为

    where
      ma.getQualifier().getType() instanceof TypeString and
      ma.getMethod().hasName("equals") and
      ma.getArgument(0).(StringLiteral).getValue() = ""
    
  2. 重新运行查询。

    现在结果减少了,因为不再包括类型不同的 .equals 表达式。

  • ©GitHub, Inc.
  • 条款
  • 隐私