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)