Ruby 的 CodeQL 库¶
分析 Ruby 程序时,您可以使用 Ruby 的 CodeQL 库中大量类。
概述¶
CodeQL 附带了一个用于分析 Ruby 代码的扩展库。该库中的类以面向对象的形式呈现 CodeQL 数据库中的数据,并提供抽象和谓词来帮助您完成常见的分析任务。
该库实现为一组 CodeQL 模块,即扩展名为 .qll
的文件。模块 ruby.qll 导入大多数其他标准库模块,因此您可以通过以下语句来包含完整的库:
import codeql.ruby.AST
CodeQL 库根据您要编写的查询类型对 Ruby 代码的各个方面进行建模。例如,抽象语法树 (AST) 库用于定位程序元素,以匹配源代码中的语法元素。这可以用来查找值、模式和结构。
控制流图 (CFG) 使用以下语句导入:
import codeql.ruby.CFG
CFG 对语句和表达式之间的控制流进行建模,例如一个表达式是否可以流向另一个表达式,或者一个表达式是否“支配”另一个表达式,这意味着通往一个表达式的所有路径必须首先流经另一个表达式。
数据流库使用以下语句导入:
import codeql.ruby.DataFlow
数据流跟踪数据在程序中的流动,包括函数调用(过程间数据流)。数据流对于安全查询特别有用,在安全查询中,不受信任的数据会流向程序的脆弱部分以利用它。与数据流相关的是污点跟踪库,它可以找到数据如何影响程序中的其他值,即使它不是完全复制的。
API 图库用于定位库中的方法。当定位在安全查询中可以用作数据来源或接收器的特定函数或参数时,这特别有用。
概括地说,主要的 Ruby 模块是:
导入 | 描述 |
---|---|
ruby |
标准 Ruby 库 |
codeql.ruby.AST |
抽象语法树库(也由 ruby.qll 导入) |
codeql.ruby.ApiGraphs |
API 图库 |
codeql.ruby.CFG |
控制流图库 |
codeql.ruby.DataFlow |
数据流库 |
codeql.ruby.TaintTracking |
污点跟踪库 |
本文中的 CodeQL 示例仅是摘录,并不代表完整的查询。
抽象语法¶
抽象语法树 (AST) 表示源代码的元素,这些元素被组织成树形结构。Visual Studio Code 中的 AST 查看器 显示 AST 节点,包括相关的 CodeQL 类和谓词。
所有 CodeQL AST 类都继承自 AstNode 类,该类为所有 AST 类提供以下成员谓词:
谓词 | 描述 |
---|---|
getEnclosingModule() |
获取封闭的模块(如果有)。 |
getEnclosingMethod() |
获取封闭的方法(如果有)。 |
getLocation() |
获取此节点的位置。 |
getAChild() |
获取此节点的子节点。 |
getParent() |
获取此 AstNode 的父节点(如果此节点不是根节点)。 |
getDesugared |
获取此 AST 节点的去语法糖版本(如果有)。 |
isSynthesized() |
如果此节点是合成的,以表示源代码中不存在的隐式 AST 节点,则为 true。 |
模块¶
模块代表 Ruby 程序的主要结构元素,包括模块 (Module
)、命名空间 (Namespace
) 和类 (ClassDeclaration
)。
CodeQL 类 | 描述和选定的谓词 |
---|---|
Module |
运行时 module 或 class 值的表示。
|
Namespace |
类或模块定义。
|
ClassDeclaration |
类定义。 |
SingletonClass |
对象上的单例类定义。 |
ModuleDeclaration |
模块定义。 |
Toplevel |
表示整个 Ruby 源文件的节点。 |
以下示例列出了 ApiController 类中的所有方法:
import codeql.ruby.AST
from ClassDeclaration m
where m.getName() = "ApiController"
select m, m.getAMethod()
可调用对象¶
可调用对象 是可以调用的元素,包括方法和代码块。
CodeQL 类 | 描述和主要谓词 |
---|---|
Callable |
可调用对象。
|
Private |
对 private 的调用。 |
Method |
方法。
|
SingletonMethod |
单例方法。 |
Lambda |
lambda(匿名方法)。 |
Block |
代码块。 |
DoBlock |
用 do 和 end 括起来的代码块。 |
BraceBlock |
使用花括号定义的代码块。 |
参数 是传递到可调用对象的值。与其他 CodeQL 语言模型不同,Ruby 中的参数本身不是变量,但可以向可调用对象中引入变量。参数的变量由 getAVariable() 谓词给出。
CodeQL 类 | 描述和主要谓词 |
---|---|
Parameter |
参数。
|
PatternParameter |
使用模式定义的参数。 |
TuplePatternParameter |
使用元组模式定义的参数。 |
NamedParameter |
命名参数。
|
SimpleParameter |
简单(正常)参数。 |
BlockParameter |
作为代码块的参数。 |
HashSplatParameter |
哈希展开(或双展开)参数。 |
KeywordParameter |
关键字参数,包括参数为可选时使用的默认值。
|
OptionalParameter |
可选参数。
|
SplatParameter |
展开参数。 |
示例
import codeql.ruby.AST
from Method m
where m.getName() = "show"
select m.getParameter(0)
语句¶
语句是代码块的元素。产生值的语句称为表达式,具有 CodeQL 类 Expr。其余语句类型(不产生值)列在下面。
CodeQL 类 | 描述和主要谓词 |
---|---|
Stmt |
所有语句的基类。
|
EmptyStmt |
空语句。 |
BeginExpr |
begin 语句。 |
BeginBlock |
BEGIN 代码块。 |
EndBlock |
END 代码块。 |
UndefStmt |
undef 语句。 |
AliasStmt |
alias 语句。 |
ReturningStmt |
可能返回值的语句:return、break 和 next。 |
ReturnStmt |
return 语句。 |
BreakStmt |
break 语句。 |
NextStmt |
next 语句。 |
RedoStmt |
redo 语句。 |
RetryStmt |
retry 语句。 |
以下示例查找所有由 return 语句返回的字面量。
import codeql.ruby.AST
from ReturnStmt return, Literal lit
where lit.getParent() = return
select lit, "Returning a literal " + lit.getValueText()
表达式¶
表达式是评估为值的语句类型。CodeQL 类 Expr 是所有表达式类型的基类。
CodeQL 类 | 描述和主要谓词 |
---|---|
Expr |
表达式。 这是所有表达式的根类。
|
Self |
对当前对象的引用。 |
Pair |
配对表达式。 |
RescueClause |
rescue 子句。 |
RescueModifierExpr |
带有 rescue 修饰符的表达式。 |
StringConcatenation |
字符串字面量的连接。
|
CodeQL 类 | 描述 |
---|---|
StmtSequence |
表达式序列。
|
BodyStmt |
表示方法、类、模块或 do 代码块主体的一系列语句。
|
ParenthesizedExpr |
带括号的表达式序列,通常包含一个表达式。 |
字面量是直接评估为给定值的表达式。CodeQL Ruby 库对所有类型的 Ruby 字面量进行建模。
CodeQL 类 | 描述 |
---|---|
字面量 |
一个字面量。它是所有字面量的基类。
|
数值字面量 |
一个数值字面量。字面量类型包括 IntegerLiteral 、FloatLiteral 、RationalLiteral 和 ComplexLiteral 。 |
Nil 字面量 |
一个 nil 字面量。 |
布尔字面量 |
一个布尔值。类 TrueLiteral 和 FalseLiteral 分别对应 true 和 false。 |
字符串组件 |
字符串的一个组件。可以是 StringTextComponent 、StringEscapeSequenceComponent 或 StringInterpolationComponent 。 |
正则表达式字面量 |
一个正则表达式字面量。 |
符号字面量 |
一个符号字面量。 |
子shell 字面量 |
一个子shell 字面量。 |
字符字面量 |
一个字符字面量。 |
数组字面量 |
一个数组字面量。 |
哈希字面量 |
一个哈希字面量。 |
范围字面量 |
一个范围字面量。 |
方法名 |
一个方法名字面量。 |
以下示例定义了一个包含文本“username”的字符串字面量类
class UsernameLiteral extends Literal
{
UsernameLiteral() { this.getValueText().toLowerCase().matches("%username%") }
}
操作是通常执行某种计算的表达式类型。大多数操作是 MethodCalls
,因为它们通常会调用底层操作。
CodeQL 类 | 描述 |
---|---|
操作 |
一个操作。 |
一元操作 |
一个一元操作。 一元操作的类型包括 |
DefinedExpr |
对特殊运算符 defined? 的调用 |
二元操作 |
一个二元操作,包括许多其他操作类别,例如 BinaryArithmeticOperation 、BinaryBitwiseOperation 、ComparisonOperation 、SpaceshipExpr 和 Assignment 。 |
二元算术操作 |
一个二元算术操作。包括:AddExpr 、SubExpr 、MulExpr 、DivExpr 、ModuloExpr 和 ExponentExpr 。 |
二元逻辑操作 |
一个二元逻辑操作。包括:LogicalAndExpr 和 LogicalOrExpr 。 |
二元位操作 |
一个二元位操作。包括:LShiftExpr 、RShiftExpr 、BitwiseAndExpr 、BitwiseOrExpr 和 BitwiseXorExpr 。 |
比较操作 |
一个比较操作,包括类 EqualityOperation 、EqExpr 、NEExpr 、CaseEqExpr 、RelationalOperation 、GTExpr 、GEExpr 、LTExpr 和 LEExpr 。 |
RegExpMatchExpr |
一个正则表达式匹配表达式。 |
NoRegExpMatchExpr |
一个正则表达式不匹配表达式。 |
赋值 |
一个赋值。赋值可以是简单赋值 ( 赋值算术操作 ( 赋值逻辑操作 ( 赋值位操作 ( |
以下示例查找“链式赋值”(形如 A=B=C
)
import codeql.ruby.AST
from Assignment op
where op.getRightOperand() instanceof Assignment
select op, "This is a chained assignment."
调用将控制权传递给另一个函数,包括显式方法调用 (MethodCall
),但也包括其他类型的调用,例如 super 调用或 yield 调用。
CodeQL 类 | 描述和主要谓词 |
---|---|
调用 |
一个调用。
|
方法调用 |
一个方法调用。
|
Setter 方法调用 |
对 Setter 方法的调用。 |
元素引用 |
一个元素引用;对 [] 方法的调用。 |
Yield 调用 |
对 yield 的调用。 |
Super 调用 |
对 super 的调用。 |
块参数 |
方法调用中的块参数。 |
以下示例查找对名为 delete 的方法的所有方法调用。
import codeql.ruby.AST
from MethodCall call
where call.getMethodName() = "delete"
select call, "Call to 'delete'."
控制表达式是用于控制流程的表达式。它们被归类为表达式,因为它们可以生成值。
CodeQL 类 | 描述和主要谓词 |
---|---|
ControlExpr |
一个控制表达式,例如 case、if、unless、三元运算符 (?:)、while、until(包括表达式修改器变体)和 for。 |
条件表达式 |
一个条件表达式。
|
IfExpr |
一个 if 或 elsif 表达式。
|
UnlessExpr |
一个 unless 表达式。 |
IfModifierExpr |
使用 if 修改的表达式。 |
UnlessModifierExpr |
使用 unless 修改的表达式。 |
TernaryIfExpr |
使用三元运算符 (?:) 的条件表达式。 |
CaseExpr |
一个 case 表达式。 |
WhenExpr |
一个 case 表达式的 when 分支。 |
循环 |
一个循环。也就是说,一个 for 循环、一个 while 或 until 循环,或它们的表达式修改器变体。 |
条件循环 |
使用条件表达式的循环。也就是说,一个 while 或 until 循环,或它们的表达式修改器变体。
|
WhileExpr |
一个 while 循环。 |
UntilExpr |
一个 until 循环。 |
WhileModifierExpr |
使用 while 修改器循环的表达式。 |
UntilModifierExpr |
使用 until 修改器循环的表达式。 |
ForExpr |
一个 for 循环。 |
以下示例查找缺少 then 分支的 if 表达式。
import codeql.ruby.AST
from IfExpr expr
where not exists(expr.getThen())
select expr, "This if-expression is redundant."
变量¶
变量是在 Ruby 程序中保存值的名称。如果您想查询任何类型的变量,请使用 Variable
类,否则请使用子类之一 LocalVariable
、InstanceVariable
、ClassVariable
或 GlobalVariable
。
局部变量的作用域是一个函数或块,实例变量的作用域是一个对象(类似成员变量),类变量的作用域是一个类,并在该类的所有实例之间共享(类似静态变量),而全局变量的作用域是整个程序。
CodeQL 类 | 描述和主要谓词 |
---|---|
Variable |
在作用域中声明的变量。
|
LocalVariable |
一个局部变量。 |
InstanceVariable |
一个实例变量。 |
ClassVariable |
一个类变量。 |
GlobalVariable |
一个全局变量。 |
以下示例查找类 StaticController 中的所有类变量
import codeql.ruby.AST
from ClassDeclaration cd, ClassVariable v
where
v.getDeclaringScope() = cd and
cd.getName() = "StaticController"
select v, "This is a static variable in 'StaticController'."
变量访问是源代码中对变量的使用。请注意,变量和对变量的使用是不同的概念。变量使用 Variable
类进行建模,而对变量的使用使用 VariableAccess
类进行建模。 Variable.getAnAccess()
获取变量的访问。
变量访问分为两种类型:对变量的读取(ReadAccess
)和对变量的写入(WriteAccess
)。访问是一种表达式类型,因此扩展了 Expr
类。
CodeQL 类 | 描述和主要谓词 |
---|---|
VariableAccess |
对变量的访问。
|
VariableReadAccess |
对变量的访问,其中读取其值。 |
VariableWriteAccess |
对变量的访问,其中更新其值。 |
LocalVariableAccess |
对局部变量的访问。 |
LocalVariableWriteAccess |
对局部变量的访问,其中更新其值。 |
LocalVariableReadAccess |
对局部变量的访问,其中读取其值。 |
GlobalVariableAccess |
对全局变量的访问,其中更新其值。 |
InstanceVariableAccess |
对全局变量的访问,其中读取其值。 |
InstanceVariableReadAccess |
对实例变量的访问。 |
InstanceVariableWriteAccess |
访问一个值被更新的实例变量。 |
ClassVariableAccess |
访问一个类变量。 |
ClassVariableWriteAccess |
访问一个值被更新的类变量。 |
ClassVariableReadAccess |
访问一个值被读取的类变量。 |
以下示例查找类StaticController中对类变量的写入。
import codeql.ruby.AST
from ClassVariableWriteAccess write, ClassDeclaration cd, ClassVariable v
where
v.getDeclaringScope() = cd and
cd.getName() = "StaticController" and
write.getVariable() = v
select write, "'StaticController' class variable is written here."