codeql语法简介
QL Language Reference
CodeQL for 编程语言
QL的语法来自于Datalog,Datalog来源于Prolog。
参考资料:
http://www.lsv.fr/~segoufin/Papers/Mypapers/DB-chapter.pd
值
表达式
字符串
: "abc\""
范围
: [3 .. 7]
集合
: [1,2,3]
this
:QL可以通过class
来定义类,类是结构化的逻辑表达
result
: 在predict
中,result用于绑定结果
super
: QL中可以使用extends
来表达逻辑继承
class A extends int {
A() { this = 1 }
int getANumber() { result = 2 }
}
class B extends int {
B() { this = 1 }
int getANumber() { result = 3 }
}
class C extends A, B {
// Need to define `int getANumber()`; otherwise it would be ambiguous
int getANumber() {
result = B.super.getANumber()
}
}
from C c
select c, c.getANumber()
结果:1,3
变量
变量具有类型和值。在QL中,类型是指一组值的集合,变量可以认为是该种类型下的所有值,再加上上下文中的其他限定条件,从而构成了变量的值的集合。
有限变量:变量的值有限。
无限变量:变量的值有无限个。
QL要求返回的值的集合是有限的。
变量绑定(binding):
形式 | 是否绑定 | |
---|---|---|
x=1 | 是 | |
x!=1 | 否 | |
x+y=1 | y绑定,则x绑定 | |
x > y | x或y都没有绑定 |
Formula(规则)
exists(var decl|formula A|formula B)
, decl同时满足formula A
和formula B
forall(var decl|formula A|formula B)
, 规则:forall(A | B) = not exists(A and not B)
A | B | not exists(A and not B) |
---|---|---|
empty | any | true |
any | A | true |
any | contains A | true |
not empty | contains no A at all | false |
any | a subset of A | false |
any | contains just a strict subset of A i.e. A∩B is not empty | false |
结论: forall(A | B)
当且仅当B是A的超集。子集 implies
超集.
forex(var decl|formula 1|formula 2)
, 和forall
一样,但是必须保证formula 1
和formula 2
中都至少有1个值。
- 比如:
forall(int i | i = 1 and i = 2 | i = 3)
是true
,为什么?因为i = 1 and i = 2
是一个空集,所以i=3
一定是这个空集的超集,通常这并不是所期望的。
if-then-else
:
string visibility(Class c){
if c.isPublic()
then result = "public"
else result = "private"
}
A implies B
: 即(not A) or B
。
结果是一个集合,而不是true
或false
。集合的元素包括not A
, 以及B
中的元素。如果一个元素e ∈ A
,且e ∈ A implies B
, 则e ∈ B
。也就是说,A implies B
的所有元素,要么是不满足A的,要么是满足A且满足B的。集合的构成是排除所有满足A但不满足B的元素。
例子: x%2=0 implies x%4=0
, 满足x%4 = 0
一定满足x%2=0
。这里要排除的是那些满足A但不满足B的元素。比如3属于这个集合,但是2不属于这个集合,4属于这个集合,8也属于这个集合。
x%4=0 implies x%2=0
为空,设C为满足x%4=0
但不满足x%2=0
的集合,x
可以表达式4i
,也可表达为2j+1
, i
,j
是任意整数。从而存在i,j使4i=2j+1
, 但这样的i,j并不存在,因为2(2i-j)=1
,左边一定是偶数,右边是常数1。
class SmallInt extends int {
SmallInt() { this = [1 .. 10] }
}
from SmallInt x
where x % 2 = 0 implies x % 4 = 0
select x
结果:非2的倍数(奇数),或者4的倍数
A implies B
即不满足A的,或者满足A且满足B的
递归
如果一个predict
直接地,或间接地依赖自身,则它是递归的。
递归的predict
必须包含至少一个基础条件,它不是递归的。QL会找到最小的那个基础条件,然后重复应用其他规则,直到结果不再变化。
下面的例子是[0,100]
int getANumber() {
result = 0
or
result <= 100 and result = getANumber() + 1
}
select getANumber()
模块和库
module Example {
class Positive extends int{
Positive(){
this > 0
}
}
}
文件,.ql
(查询模块)和.qll
(库模块)文件本身自动声明了一个模块,名称是文件名本身,模块体则由文件内容构成。
.ql
和.qll
的区别:
- 不能被引入,即
import
- 必须至少包含一条查询语句,即
select
import
语法:
import <module_name>
import <module_name> as <name>
private import <module_name>
QL首先尝试<module_name>.qll
,如果找不到,则在文件自身寻找<module_name>
.
不import
,直接引用:
<module_expression>::<name>
。
注意:.qll
内部本身还能定义module
,所以如果是嵌套的,则使用import <module_name>::<inner_module>
select
和query
在.ql
中可以定义select
或query
来返回结果,定义query
的好处是,能够被其他模块引用
query int getProduct(int x, int y) {
x = 3 and
y in [0 .. 2] and
result = x * y
}