CodeQL 文档

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 标准 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 类提供以下成员谓词:

AstNode 中的主要谓词
谓词 描述
getEnclosingModule() 获取封闭的模块(如果有)。
getEnclosingMethod() 获取封闭的方法(如果有)。
getLocation() 获取此节点的位置。
getAChild() 获取此节点的子节点。
getParent() 获取此 AstNode 的父节点(如果此节点不是根节点)。
getDesugared 获取此 AST 节点的去语法糖版本(如果有)。
isSynthesized() 如果此节点是合成的,以表示源代码中不存在的隐式 AST 节点,则为 true。

模块

模块代表 Ruby 程序的主要结构元素,包括模块 (Module)、命名空间 (Namespace) 和类 (ClassDeclaration)。

可调用类
CodeQL 类 描述和选定的谓词
Module

运行时 moduleclass 值的表示。

  • getADeclaration() - 获取声明
  • getSuperClass() - 获取此模块的超类(如果有)。
  • getAPrependedModule() - 获取预先添加的模块。
  • getAnIncludedModule() - 获取包含的模块。
Namespace

类或模块定义。

  • getName() - 获取模块/类的名称。
  • getAMethod()getMethod(name) - 获取此命名空间中的方法。
  • getAClass()getClass(name) - 获取此命名空间中的类。
  • getAModule()getModule(name) - 获取此命名空间中的模块。
ClassDeclaration 类定义。
SingletonClass 对象上的单例类定义。
ModuleDeclaration 模块定义。
Toplevel 表示整个 Ruby 源文件的节点。

以下示例列出了 ApiController 类中的所有方法:

import codeql.ruby.AST

from ClassDeclaration m
where m.getName() = "ApiController"
select m, m.getAMethod()

可调用对象

可调用对象 是可以调用的元素,包括方法和代码块。

可调用类
CodeQL 类 描述和主要谓词
Callable

可调用对象。

  • getAParameter() - 获取此可调用对象的参数。
  • getParameter(n) - 获取此可调用对象的第 n 个参数。
Private private 的调用。
Method

方法。

  • getName() - 获取此方法的名称。
SingletonMethod 单例方法。
Lambda lambda(匿名方法)。
Block 代码块。
DoBlock doend 括起来的代码块。
BraceBlock 使用花括号定义的代码块。

参数 是传递到可调用对象的值。与其他 CodeQL 语言模型不同,Ruby 中的参数本身不是变量,但可以向可调用对象中引入变量。参数的变量由 getAVariable() 谓词给出。

参数类
CodeQL 类 描述和主要谓词
Parameter

参数。

  • getCallable() - 获取此参数所属的可调用对象。
  • getPosition() - 获取此参数的基于零的索引位置。
  • getAVariable()getVariable(name) - 获取由此参数引入的变量。
PatternParameter 使用模式定义的参数。
TuplePatternParameter 使用元组模式定义的参数。
NamedParameter

命名参数。

  • getName()hasName(name) - 获取此参数的名称。
  • getAnAccess() - 获取对该参数的访问。
  • getDefiningAccess() - 获取定义底层局部变量的访问。
SimpleParameter 简单(正常)参数。
BlockParameter 作为代码块的参数。
HashSplatParameter 哈希展开(或双展开)参数。
KeywordParameter

关键字参数,包括参数为可选时使用的默认值。

  • getDefaultValue() - 获取默认值,即在调用者未提供参数时分配给参数的值。
OptionalParameter

可选参数。

  • getDefaultValue() - 获取默认值,即在调用者未提供参数时分配给参数的值。
SplatParameter 展开参数。

示例

import codeql.ruby.AST

from Method m
where m.getName() = "show"
select m.getParameter(0)

语句

语句是代码块的元素。产生值的语句称为表达式,具有 CodeQL 类 Expr。其余语句类型(不产生值)列在下面。

语句类
CodeQL 类 描述和主要谓词
Stmt

所有语句的基类。

  • getAControlFlowNode() - 获取此语句的控制流节点(如果有)。
  • getEnclosingCallable() - 获取封闭的可调用对象(如果有)。
EmptyStmt 空语句。
BeginExpr begin 语句。
BeginBlock BEGIN 代码块。
EndBlock END 代码块。
UndefStmt undef 语句。
AliasStmt alias 语句。
ReturningStmt 可能返回值的语句:returnbreaknext
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

表达式。

这是所有表达式的根类。

  • getValueText() - 获取此表达式的文本(常量)值(如果有)。
Self 对当前对象的引用。
Pair 配对表达式。
RescueClause rescue 子句。
RescueModifierExpr 带有 rescue 修饰符的表达式。
StringConcatenation

字符串字面量的连接。

  • getConcatenatedValueText() - 获取连接所有字符串字面量的结果(当且仅当它们不包含任何插值时)。
语句序列
CodeQL 类 描述
StmtSequence

表达式序列。

  • getAStmt()getStmt(n) - 获取此序列中的语句。
  • isEmpty() - 如果此序列中没有语句,则为 true。
  • getNumberOfStatements() - 获取此序列中的语句数。
BodyStmt

表示方法、类、模块或 do 代码块主体的一系列语句。

  • getARescue()getRescue(n) - 获取此代码块中的 rescue 子句。
  • getElse() - 获取此代码块中的 else 子句(如果有)。
  • getEnsure() - 获取此代码块中的 ensure 子句(如果有)。
ParenthesizedExpr 带括号的表达式序列,通常包含一个表达式。

字面量是直接评估为给定值的表达式。CodeQL Ruby 库对所有类型的 Ruby 字面量进行建模。

字面量
CodeQL 类 描述
字面量

一个字面量。它是所有字面量的基类。

  • getValueText() - 获取此字面量的源文本,如果它是一个简单字面量。
数值字面量 一个数值字面量。字面量类型包括 IntegerLiteralFloatLiteralRationalLiteralComplexLiteral
Nil 字面量 一个 nil 字面量。
布尔字面量 一个布尔值。类 TrueLiteralFalseLiteral 分别对应 truefalse
字符串组件 字符串的一个组件。可以是 StringTextComponentStringEscapeSequenceComponentStringInterpolationComponent
正则表达式字面量 一个正则表达式字面量。
符号字面量 一个符号字面量。
子shell 字面量 一个子shell 字面量。
字符字面量 一个字符字面量。
数组字面量 一个数组字面量。
哈希字面量 一个哈希字面量。
范围字面量 一个范围字面量。
方法名 一个方法名字面量。

以下示例定义了一个包含文本“username”的字符串字面量类

class UsernameLiteral extends Literal
{
  UsernameLiteral() { this.getValueText().toLowerCase().matches("%username%") }
}

操作是通常执行某种计算的表达式类型。大多数操作是 MethodCalls,因为它们通常会调用底层操作。

操作
CodeQL 类 描述
操作 一个操作。
一元操作

一个一元操作。

一元操作的类型包括 UnaryLogicalOperationNotExprUnaryPlusExprUnaryMinusExprSplatExprHashSplatExprUnaryBitwiseOperationComplementExpr

DefinedExpr 对特殊运算符 defined? 的调用
二元操作 一个二元操作,包括许多其他操作类别,例如 BinaryArithmeticOperationBinaryBitwiseOperationComparisonOperationSpaceshipExprAssignment
二元算术操作 一个二元算术操作。包括:AddExprSubExprMulExprDivExprModuloExprExponentExpr
二元逻辑操作 一个二元逻辑操作。包括:LogicalAndExprLogicalOrExpr
二元位操作 一个二元位操作。包括:LShiftExprRShiftExprBitwiseAndExprBitwiseOrExprBitwiseXorExpr
比较操作 一个比较操作,包括类 EqualityOperationEqExprNEExprCaseEqExprRelationalOperationGTExprGEExprLTExprLEExpr
RegExpMatchExpr 一个正则表达式匹配表达式。
NoRegExpMatchExpr 一个正则表达式不匹配表达式。
赋值

一个赋值。赋值可以是简单赋值 (AssignExpr),也可以是赋值操作 (AssignOperation)。

赋值算术操作 (AssignArithmeticOperation) 包括 AssignAddExprAssignSubExprAssignMulExprAssignDivExprAssignModuloExprAssignExponentExpr

赋值逻辑操作 (AssignLogicalOperation) 包括 AssignLogicalAndExprAssignLogicalOrExpr

赋值位操作 (AssignBitwiseOperation) 包括 AssignLShiftExprAssignRShiftExprAssignBitwiseAndExprAssignBitwiseOrExprAssignBitwiseXorExpr

以下示例查找“链式赋值”(形如 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 类 描述和主要谓词
调用

一个调用。

  • getArgument(n)getAnArgument()getKeywordArgument(keyword) - 获取此调用的参数。
  • getATarget() - 获取此调用的潜在目标(如果有)。
方法调用

一个方法调用。

  • getReceiver() - 获取此调用的接收者(如果有)。它是被调用的对象。
  • getMethodName() - 获取被调用的方法名称。
  • getBlock() - 获取此方法调用的块(如果有)。
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 一个控制表达式,例如 caseifunless、三元运算符 (?:)、whileuntil(包括表达式修改器变体)和 for
条件表达式

一个条件表达式。

  • getCondition() - 获取条件表达式。
IfExpr

一个 ifelsif 表达式。

  • getThen() - 获取 then 分支。
  • getElse() - 获取 elseifelse 分支。
UnlessExpr 一个 unless 表达式。
IfModifierExpr 使用 if 修改的表达式。
UnlessModifierExpr 使用 unless 修改的表达式。
TernaryIfExpr 使用三元运算符 (?:) 的条件表达式。
CaseExpr 一个 case 表达式。
WhenExpr 一个 case 表达式的 when 分支。
循环 一个循环。也就是说,一个 for 循环、一个 whileuntil 循环,或它们的表达式修改器变体。
条件循环

使用条件表达式的循环。也就是说,一个 whileuntil 循环,或它们的表达式修改器变体。

  • getCondition() - 获取此循环的条件表达式。
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 类,否则请使用子类之一 LocalVariableInstanceVariableClassVariableGlobalVariable

局部变量的作用域是一个函数或块,实例变量的作用域是一个对象(类似成员变量),变量的作用域是一个类,并在该类的所有实例之间共享(类似静态变量),而全局变量的作用域是整个程序。

变量类
CodeQL 类 描述和主要谓词
Variable

在作用域中声明的变量。

  • getName()hasName(name) - 获取此变量的名称。
  • getDeclaringScope() - 获取声明此变量的作用域。
  • getAnAccess() - 获取对此变量的访问。
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

对变量的访问。

  • getVariable() - 获取被访问的变量。
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."
  • ©GitHub, Inc.
  • 条款
  • 隐私