QL 简介¶
通过一些简单的练习和示例来学习 QL 和 CodeQL 的基础知识。
基本语法¶
QL 的基本语法对于任何使用过 SQL 的人来说都很熟悉,但它在某种程度上有所不同。
QL 是一种逻辑编程语言,因此它由逻辑公式构成。QL 使用常见的逻辑连接词(例如 and、or 和 not)、量词(例如 forall 和 exists)以及其他重要的逻辑概念,例如谓词。
QL 还支持递归和聚合。这使你可以使用简单的 QL 语法编写复杂的递归查询,并直接使用聚合,例如 count、sum 和 average。
注意
你可以在 GitHub Codespaces 中使用 CodeQL 模板(测试版)来尝试这些教程中的 QL 概念和与编程语言无关的示例。该模板包含一个指导性的 QL 使用入门,让你轻松上手。
当你准备好对实际代码库运行 CodeQL 查询时,你需要在 Visual Studio Code 中安装 CodeQL 扩展。有关说明,请参阅 GitHub 文档中的 安装适用于 Visual Studio Code 的 CodeQL。
运行查询¶
你可以使用 适用于 VS Code 的 CodeQL 或 GitHub Codespaces 上的 CodeQL 模板 来尝试以下示例和练习。
这是一个基本查询的示例
select "hello world"
此查询返回字符串 "hello world"。
更复杂的查询通常类似于以下内容
from /* ... variable declarations ... */
where /* ... logical formulas ... */
select /* ... expressions ... */
例如,此查询的结果为数字 42
from int x, int y
where x = 6 and y = 7
select x * y
请注意,int 指定 x 和 y 的 **类型** 为“整数”。这意味着 x 和 y 仅限于整数值。其他一些常见的类型包括:boolean (true 或 false)、date、float 和 string。
简单练习¶
你可以使用一些可用于 int、date、float、boolean 和 string 类型的基本函数来编写简单的查询。若要应用函数,请将其附加到参数。例如,1.toString() 将值 1 转换为字符串。请注意,在你开始键入函数时,会显示一个弹出窗口,方便你选择所需的函数。还要注意,你可以连续应用多个函数。例如,100.log().sqrt() 首先取 100 的自然对数,然后计算结果的平方根。
具有多个结果的示例查询¶
以上练习都显示了只有一个结果的查询,但实际上,许多查询具有多个结果。例如,以下查询计算 1 到 10 之间的所有 毕达哥拉斯三元组
from int x, int y, int z
where x in [1..10] and y in [1..10] and z in [1..10] and
x*x + y*y = z*z
select x, y, z
为了简化查询,我们可以引入一个类 SmallInt,它表示 1 到 10 之间的整数。我们还可以定义一个谓词 square(),该谓词作用于该类中的整数。通过这种方式定义类和谓词,可以轻松地重复使用代码,而无需每次都重复代码。
class SmallInt extends int {
SmallInt() { this in [1..10] }
int square() { result = this*this }
}
from SmallInt x, SmallInt y, SmallInt z
where x.square() + y.square() = z.square()
select x, y, z
示例 CodeQL 查询¶
之前的示例使用了 QL 中的内置基本类型。虽然我们选择了一个查询项目,但我们没有使用该项目的数据库中的信息。以下示例查询 *确实* 使用了这些数据库,让你了解如何使用 CodeQL 来分析项目。
使用 CodeQL 库的查询可以找出代码库中的错误,并发现重要安全漏洞的变体。访问 GitHub 安全实验室 阅读有关我们最近在开源项目中发现的漏洞示例。
在运行以下示例之前,你需要安装适用于 Visual Studio Code 的 CodeQL 扩展。有关更多信息,请参阅 GitHub 文档中的 安装适用于 Visual Studio Code 的 CodeQL。你还需要导入并选择相应的编程语言的数据库。
若要导入特定编程语言的 CodeQL 库,请在查询开头键入 import <language>。
import python
from Function f
where count(f.getAnArg()) > 7
select f
该 from 子句定义了一个变量 f,它表示一个 Python 函数。该 where 部分将函数 f 限制为那些具有超过 7 个参数的函数。最后,该 select 子句列出这些函数。
import javascript
from Comment c
where c.getText().regexpMatch("(?si).*\\bTODO\\b.*")
select c
该 from 子句定义了一个变量 c,它表示一个 JavaScript 注释。该 where 部分将注释 c 限制为那些包含单词 "TODO" 的注释。该 select 子句列出这些注释。
import java
from Parameter p
where not exists(p.getAnAccess())
select p
该 from 子句定义了一个变量 p,它表示一个 Java 参数。该 where 部分通过将参数 p 限制为那些未访问的参数来查找未使用的参数。最后,该 select 子句列出这些参数。
答案¶
练习 1¶
from string s
where s = "lgtm"
select s.length()
通常有多种方法来定义查询。例如,我们还可以以更简短的形式编写上述查询
select "lgtm".length()
练习 2¶
from float x, float y
where x = 3.pow(5) and y = 245.6
select x.minimum(y).sin()
练习 3¶
from boolean b
where b = false
select b.booleanNot()
练习 4¶
from date start, date end
where start = "10/06/2017".toDate() and end = "28/09/2017".toDate()
select start.daysTo(end)