Merge remote-tracking branch 'origin/main' into nickrolfe/regexp_g_anchor

This commit is contained in:
Nick Rolfe
2021-12-02 12:07:13 +00:00
586 changed files with 45439 additions and 23627 deletions

View File

@@ -1 +1,13 @@
import codeql.ruby.DataFlow::DataFlow
import codeql.ruby.dataflow.internal.DataFlowPrivate
import codeql.ruby.dataflow.internal.DataFlowImplConsistency::Consistency
private class MyConsistencyConfiguration extends ConsistencyConfiguration {
override predicate postWithInFlowExclude(Node n) { n instanceof SummaryNode }
override predicate argHasPostUpdateExclude(ArgumentNode n) {
n instanceof BlockArgumentNode
or
n instanceof SummaryNode
}
}

View File

@@ -1,22 +1,10 @@
import ruby
import codeql.ruby.dataflow.SSA
import codeql.ruby.controlflow.ControlFlowGraph
import codeql.ruby.dataflow.internal.SsaImplCommon::Consistency
query predicate nonUniqueDef(CfgNode read, Ssa::Definition def) {
read = def.getARead() and
exists(Ssa::Definition other | read = other.getARead() and other != def)
}
query predicate readWithoutDef(LocalVariableReadAccess read) {
exists(CfgNode node |
node = read.getAControlFlowNode() and
not node = any(Ssa::Definition def).getARead()
)
}
query predicate deadDef(Ssa::Definition def, LocalVariable v) {
v = def.getSourceVariable() and
not v.isCaptured() and
not exists(def.getARead()) and
not def = any(Ssa::PhiNode phi).getAnInput()
class MyRelevantDefinition extends RelevantDefinition, Ssa::Definition {
override predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
) {
this.getLocation().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
}
}

View File

@@ -18,6 +18,27 @@ private import ast.internal.Scope
private import ast.internal.Synthesis
private import ast.internal.TreeSitter
cached
private module Cached {
cached
ModuleBase getEnclosingModule(Scope s) {
result = s
or
not s instanceof ModuleBase and result = getEnclosingModule(s.getOuterScope())
}
cached
MethodBase getEnclosingMethod(Scope s) {
result = s
or
not s instanceof MethodBase and
not s instanceof ModuleBase and
result = getEnclosingMethod(s.getOuterScope())
}
}
private import Cached
/**
* A node in the abstract syntax tree. This class is the base class for all Ruby
* program elements.
@@ -39,20 +60,10 @@ class AstNode extends TAstNode {
final string getPrimaryQlClasses() { result = concat(this.getAPrimaryQlClass(), ",") }
/** Gets the enclosing module, if any. */
ModuleBase getEnclosingModule() {
exists(Scope::Range s |
s = scopeOf(toGeneratedInclSynth(this)) and
toGeneratedInclSynth(result) = s.getEnclosingModule()
)
}
ModuleBase getEnclosingModule() { result = getEnclosingModule(scopeOfInclSynth(this)) }
/** Gets the enclosing method, if any. */
MethodBase getEnclosingMethod() {
exists(Scope::Range s |
s = scopeOf(toGeneratedInclSynth(this)) and
toGeneratedInclSynth(result) = s.getEnclosingMethod()
)
}
MethodBase getEnclosingMethod() { result = getEnclosingMethod(scopeOfInclSynth(this)) }
/** Gets a textual representation of this node. */
cached

View File

@@ -417,6 +417,12 @@ module HTTP {
/** Gets a node which returns the body of the response */
DataFlow::Node getResponseBody() { result = super.getResponseBody() }
/**
* Gets a node that contributes to the URL of the request.
* Depending on the framework, a request may have multiple nodes which contribute to the URL.
*/
DataFlow::Node getURL() { result = super.getURL() }
/** Gets a string that identifies the framework used for this request. */
string getFramework() { result = super.getFramework() }
@@ -442,6 +448,12 @@ module HTTP {
/** Gets a node which returns the body of the response */
abstract DataFlow::Node getResponseBody();
/**
* Gets a node that contributes to the URL of the request.
* Depending on the framework, a request may have multiple nodes which contribute to the URL.
*/
abstract DataFlow::Node getURL();
/** Gets a string that identifies the framework used for this request. */
abstract string getFramework();
@@ -584,6 +596,37 @@ module OrmInstantiation {
}
}
/**
* A data-flow node that may set or unset Cross-site request forgery protection.
*
* Extend this class to refine existing API models. If you want to model new APIs,
* extend `CSRFProtectionSetting::Range` instead.
*/
class CSRFProtectionSetting extends DataFlow::Node instanceof CSRFProtectionSetting::Range {
/**
* Gets the boolean value corresponding to if CSRF protection is enabled
* (`true`) or disabled (`false`) by this node.
*/
boolean getVerificationSetting() { result = super.getVerificationSetting() }
}
/** Provides a class for modeling new CSRF protection setting APIs. */
module CSRFProtectionSetting {
/**
* A data-flow node that may set or unset Cross-site request forgery protection.
*
* Extend this class to model new APIs. If you want to refine existing API models,
* extend `CSRFProtectionSetting` instead.
*/
abstract class Range extends DataFlow::Node {
/**
* Gets the boolean value corresponding to if CSRF protection is enabled
* (`true`) or disabled (`false`) by this node.
*/
abstract boolean getVerificationSetting();
}
}
/** Provides classes for modeling path-related APIs. */
module Path {
/**

View File

@@ -6,6 +6,7 @@ private import codeql.ruby.frameworks.ActionController
private import codeql.ruby.frameworks.ActiveRecord
private import codeql.ruby.frameworks.ActiveStorage
private import codeql.ruby.frameworks.ActionView
private import codeql.ruby.frameworks.Rails
private import codeql.ruby.frameworks.StandardLibrary
private import codeql.ruby.frameworks.Files
private import codeql.ruby.frameworks.HttpClients

View File

@@ -1,5 +1,6 @@
private import codeql.ruby.AST
private import internal.AST
private import internal.Control
private import internal.TreeSitter
/**
@@ -308,13 +309,36 @@ class TernaryIfExpr extends ConditionalExpr, TTernaryIfExpr {
}
}
class CaseExpr extends ControlExpr, TCaseExpr {
private Ruby::Case g;
CaseExpr() { this = TCaseExpr(g) }
final override string getAPrimaryQlClass() { result = "CaseExpr" }
/**
* A `case` statement. There are three forms of `case` statements:
* ```rb
* # a value-less case expression acting like an if-elsif expression:
* case
* when x == 0 then puts "zero"
* when x > 0 then puts "positive"
* else puts "negative"
* end
*
* # a case expression that matches a value using `when` clauses:
* case value
* when 1, 2 then puts "a is one or two"
* when 3 then puts "a is three"
* else puts "I don't know what a is"
* end
*
* # a case expression that matches a value against patterns using `in` clauses:
* config = {db: {user: 'admin', password: 'abc123'}}
* case config
* in db: {user:} # matches subhash and puts matched value in variable user
* puts "Connect with user '#{user}'"
* in connection: {username: } unless username == 'admin'
* puts "Connect with user '#{username}'"
* else
* puts "Unrecognized structure of config"
* end
* ```
*/
class CaseExpr extends ControlExpr instanceof CaseExprImpl {
/**
* Gets the expression being compared, if any. For example, `foo` in the following example.
* ```rb
@@ -334,22 +358,25 @@ class CaseExpr extends ControlExpr, TCaseExpr {
* end
* ```
*/
final Expr getValue() { toGenerated(result) = g.getValue() }
final Expr getValue() { result = super.getValue() }
/**
* Gets the `n`th branch of this case expression, either a `WhenExpr` or a
* `StmtSequence`.
* Gets the `n`th branch of this case expression, either a `WhenExpr`, an
* `InClause`, or a `StmtSequence`.
*/
final Expr getBranch(int n) { toGenerated(result) = g.getChild(n) }
final Expr getBranch(int n) { result = super.getBranch(n) }
/**
* Gets a branch of this case expression, either a `WhenExpr` or an
* `ElseExpr`.
* Gets a branch of this case expression, either a `WhenExpr`, an
* `InClause`, or a `StmtSequence`.
*/
final Expr getABranch() { result = this.getBranch(_) }
/** Gets the `n`th `when` branch of this case expression. */
deprecated final WhenExpr getWhenBranch(int n) { result = this.getBranch(n) }
/** Gets a `when` branch of this case expression. */
final WhenExpr getAWhenBranch() { result = this.getABranch() }
deprecated final WhenExpr getAWhenBranch() { result = this.getABranch() }
/** Gets the `else` branch of this case expression, if any. */
final StmtSequence getElseBranch() { result = this.getABranch() }
@@ -359,14 +386,18 @@ class CaseExpr extends ControlExpr, TCaseExpr {
*/
final int getNumberOfBranches() { result = count(this.getBranch(_)) }
final override string getAPrimaryQlClass() { result = "CaseExpr" }
final override string toString() { result = "case ..." }
override AstNode getAChild(string pred) {
result = super.getAChild(pred)
result = ControlExpr.super.getAChild(pred)
or
pred = "getValue" and result = this.getValue()
or
pred = "getBranch" and result = this.getBranch(_)
or
pred = "getElseBranch" and result = this.getElseBranch()
}
}
@@ -422,6 +453,81 @@ class WhenExpr extends Expr, TWhenExpr {
}
}
/**
* An `in` clause of a `case` expression.
* ```rb
* case foo
* in [ a ] then a
* end
* ```
*/
class InClause extends Expr, TInClause {
private Ruby::InClause g;
InClause() { this = TInClause(g) }
final override string getAPrimaryQlClass() { result = "InClause" }
/** Gets the body of this case-in expression. */
final Stmt getBody() { toGenerated(result) = g.getBody() }
/**
* Gets the pattern in this case-in expression. In the
* following example, the pattern is `Point{ x:, y: }`.
* ```rb
* case foo
* in Point{ x:, y: }
* x + y
* end
* ```
*/
final CasePattern getPattern() { toGenerated(result) = g.getPattern() }
/**
* Gets the pattern guard condition in this case-in expression. In the
* following example, there are two pattern guard conditions `x > 10` and `x < 0`.
* ```rb
* case foo
* in [ x ] if x > 10 then ...
* in [ x ] unless x < 0 then ...
* end
* ```
*/
final Expr getCondition() { toGenerated(result) = g.getGuard().getAFieldOrChild() }
/**
* Holds if the pattern guard in this case-in expression is an `if` condition. For example:
* ```rb
* case foo
* in [ x ] if x > 10 then ...
* end
* ```
*/
predicate hasIfCondition() { g.getGuard() instanceof Ruby::IfGuard }
/**
* Holds if the pattern guard in this case-in expression is an `unless` condition. For example:
* ```rb
* case foo
* in [ x ] unless x < 10 then ...
* end
* ```
*/
predicate hasUnlessCondition() { g.getGuard() instanceof Ruby::UnlessGuard }
final override string toString() { result = "in ... then ..." }
override AstNode getAChild(string pred) {
result = super.getAChild(pred)
or
pred = "getBody" and result = this.getBody()
or
pred = "getPattern" and result = this.getPattern()
or
pred = "getCondition" and result = this.getCondition()
}
}
/**
* A loop. That is, a `for` loop, a `while` or `until` loop, or their
* expression-modifier variants.
@@ -583,7 +689,7 @@ class ForExpr extends Loop, TForExpr {
final override string getAPrimaryQlClass() { result = "ForExpr" }
/** Gets the body of this `for` loop. */
final override Stmt getBody() { toGenerated(result) = g.getBody() }
final override StmtSequence getBody() { toGenerated(result) = g.getBody() }
/** Gets the pattern representing the iteration argument. */
final Pattern getPattern() { toGenerated(result) = g.getPattern() }

View File

@@ -1,6 +1,7 @@
private import codeql.ruby.AST
private import codeql.ruby.CFG
private import internal.AST
private import internal.Expr
private import internal.TreeSitter
/**
@@ -91,90 +92,19 @@ class StmtSequence extends Expr, TStmtSequence {
}
}
private class StmtSequenceSynth extends StmtSequence, TStmtSequenceSynth {
final override Stmt getStmt(int n) { synthChild(this, n, result) }
final override string toString() { result = "..." }
}
private class Then extends StmtSequence, TThen {
private Ruby::Then g;
Then() { this = TThen(g) }
override Stmt getStmt(int n) { toGenerated(result) = g.getChild(n) }
final override string toString() { result = "then ..." }
}
private class Else extends StmtSequence, TElse {
private Ruby::Else g;
Else() { this = TElse(g) }
override Stmt getStmt(int n) { toGenerated(result) = g.getChild(n) }
final override string toString() { result = "else ..." }
}
private class Do extends StmtSequence, TDo {
private Ruby::Do g;
Do() { this = TDo(g) }
override Stmt getStmt(int n) { toGenerated(result) = g.getChild(n) }
final override string toString() { result = "do ..." }
}
private class Ensure extends StmtSequence, TEnsure {
private Ruby::Ensure g;
Ensure() { this = TEnsure(g) }
override Stmt getStmt(int n) { toGenerated(result) = g.getChild(n) }
final override string toString() { result = "ensure ..." }
}
/**
* A sequence of statements representing the body of a method, class, module,
* or do-block. That is, any body that may also include rescue/ensure/else
* statements.
*/
class BodyStmt extends StmtSequence, TBodyStmt {
// Not defined by dispatch, as it should not be exposed
private Ruby::AstNode getChild(int i) {
result = any(Ruby::Method g | this = TMethod(g)).getChild(i)
or
result = any(Ruby::SingletonMethod g | this = TSingletonMethod(g)).getChild(i)
or
exists(Ruby::Lambda g | this = TLambda(g) |
result = g.getBody().(Ruby::DoBlock).getChild(i) or
result = g.getBody().(Ruby::Block).getChild(i)
)
or
result = any(Ruby::DoBlock g | this = TDoBlock(g)).getChild(i)
or
result = any(Ruby::Program g | this = TToplevel(g)).getChild(i) and
not result instanceof Ruby::BeginBlock
or
result = any(Ruby::Class g | this = TClassDeclaration(g)).getChild(i)
or
result = any(Ruby::SingletonClass g | this = TSingletonClass(g)).getChild(i)
or
result = any(Ruby::Module g | this = TModuleDeclaration(g)).getChild(i)
or
result = any(Ruby::Begin g | this = TBeginExpr(g)).getChild(i)
}
final override Stmt getStmt(int n) {
result =
rank[n + 1](AstNode node, int i |
toGenerated(node) = this.getChild(i) and
not node instanceof Else and
not node instanceof RescueClause and
not node instanceof Ensure
toGenerated(result) =
rank[n + 1](Ruby::AstNode node, int i |
node = getBodyStmtChild(this, i) and
not node instanceof Ruby::Else and
not node instanceof Ruby::Rescue and
not node instanceof Ruby::Ensure
|
node order by i
)
@@ -183,17 +113,25 @@ class BodyStmt extends StmtSequence, TBodyStmt {
/** Gets the `n`th rescue clause in this block. */
final RescueClause getRescue(int n) {
result =
rank[n + 1](RescueClause node, int i | toGenerated(node) = this.getChild(i) | node order by i)
rank[n + 1](RescueClause node, int i |
toGenerated(node) = getBodyStmtChild(this, i)
|
node order by i
)
}
/** Gets a rescue clause in this block. */
final RescueClause getARescue() { result = this.getRescue(_) }
/** Gets the `else` clause in this block, if any. */
final StmtSequence getElse() { result = unique(Else s | toGenerated(s) = getChild(_)) }
final StmtSequence getElse() {
result = unique(Else s | toGenerated(s) = getBodyStmtChild(this, _))
}
/** Gets the `ensure` clause in this block, if any. */
final StmtSequence getEnsure() { result = unique(Ensure s | toGenerated(s) = getChild(_)) }
final StmtSequence getEnsure() {
result = unique(Ensure s | toGenerated(s) = getBodyStmtChild(this, _))
}
final predicate hasEnsure() { exists(this.getEnsure()) }

View File

@@ -223,6 +223,40 @@ private class FalseLiteral extends BooleanLiteral, TFalseLiteral {
final override predicate isFalse() { any() }
}
/**
* An `__ENCODING__` literal.
*/
class EncodingLiteral extends Literal, TEncoding {
final override string getAPrimaryQlClass() { result = "EncodingLiteral" }
final override string toString() { result = "__ENCODING__" }
// TODO: return the encoding defined by a magic encoding: comment, if any.
override string getValueText() { result = "UTF-8" }
}
/**
* A `__LINE__` literal.
*/
class LineLiteral extends Literal, TLine {
final override string getAPrimaryQlClass() { result = "LineLiteral" }
final override string toString() { result = "__LINE__" }
override string getValueText() { result = this.getLocation().getStartLine().toString() }
}
/**
* A `__FILE__` literal.
*/
class FileLiteral extends Literal, TFile {
final override string getAPrimaryQlClass() { result = "FileLiteral" }
final override string toString() { result = "__FILE__" }
override string getValueText() { result = this.getLocation().getFile().getAbsolutePath() }
}
/**
* The base class for a component of a string: `StringTextComponent`,
* `StringEscapeSequenceComponent`, or `StringInterpolationComponent`.

View File

@@ -2,6 +2,7 @@ private import codeql.ruby.AST
private import codeql.ruby.controlflow.ControlFlowGraph
private import internal.AST
private import internal.TreeSitter
private import internal.Method
/** A callable. */
class Callable extends StmtSequence, Expr, Scope, TCallable {
@@ -212,16 +213,6 @@ class DoBlock extends Block, BodyStmt, TDoBlock {
* ```
*/
class BraceBlock extends Block, TBraceBlock {
private Ruby::Block g;
BraceBlock() { this = TBraceBlock(g) }
final override Parameter getParameter(int n) {
toGenerated(result) = g.getParameters().getChild(n)
}
final override Stmt getStmt(int i) { toGenerated(result) = g.getChild(i) }
final override string toString() { result = "{ ... }" }
final override string getAPrimaryQlClass() { result = "BraceBlock" }

View File

@@ -57,7 +57,11 @@ class NamedParameter extends Parameter, TNamedParameter {
final VariableAccess getAnAccess() { result = this.getVariable().getAnAccess() }
/** Gets the access that defines the underlying local variable. */
final VariableAccess getDefiningAccess() { result = this.getVariable().getDefiningAccess() }
final VariableAccess getDefiningAccess() {
result = this.getVariable().getDefiningAccess()
or
result = this.(SimpleParameterSynthImpl).getDefininingAccess()
}
override AstNode getAChild(string pred) {
result = super.getAChild(pred)
@@ -68,14 +72,12 @@ class NamedParameter extends Parameter, TNamedParameter {
}
/** A simple (normal) parameter. */
class SimpleParameter extends NamedParameter, PatternParameter, VariablePattern, TSimpleParameter {
private Ruby::Identifier g;
class SimpleParameter extends NamedParameter, PatternParameter, VariablePattern, TSimpleParameter instanceof SimpleParameterImpl {
final override string getName() { result = SimpleParameterImpl.super.getNameImpl() }
SimpleParameter() { this = TSimpleParameter(g) }
final override string getName() { result = g.getValue() }
final override LocalVariable getVariable() { result = TLocalVariableReal(_, _, g) }
final override LocalVariable getVariable() {
result = SimpleParameterImpl.super.getVariableImpl()
}
final override LocalVariable getAVariable() { result = this.getVariable() }
@@ -129,6 +131,23 @@ class HashSplatParameter extends NamedParameter, THashSplatParameter {
final override string getName() { result = g.getName().getValue() }
}
/**
* A `nil` hash splat (`**nil`) indicating that there are no keyword parameters or keyword patterns.
* For example:
* ```rb
* def foo(bar, **nil)
* case bar
* in { x:, **nil } then puts x
* end
* end
* ```
*/
class HashSplatNilParameter extends Parameter, THashSplatNilParameter {
final override string getAPrimaryQlClass() { result = "HashSplatNilParameter" }
final override string toString() { result = "**nil" }
}
/**
* A keyword parameter, including a default value if the parameter is optional.
* For example, in the following example, `foo` is a keyword parameter with a

View File

@@ -4,6 +4,7 @@ private import internal.AST
private import internal.Pattern
private import internal.TreeSitter
private import internal.Variable
private import internal.Parameter
/** A pattern. */
class Pattern extends AstNode {
@@ -15,6 +16,8 @@ class Pattern extends AstNode {
implicitParameterAssignmentNode(toGenerated(this), _)
or
this = getSynthChild(any(AssignExpr ae), 0)
or
this instanceof SimpleParameterImpl
}
/** Gets a variable used in (or introduced by) this pattern. */
@@ -94,3 +97,322 @@ class TuplePattern extends Pattern, TTuplePattern {
override AstNode getAChild(string pred) { pred = "getElement" and result = this.getElement(_) }
}
private class TPatternNode =
TArrayPattern or TFindPattern or THashPattern or TAlternativePattern or TAsPattern or
TVariableReferencePattern;
private class TPattern =
TPatternNode or TLiteral or TLambda or TConstantAccess or TLocalVariableAccess or
TUnaryArithmeticOperation;
/**
* A pattern used in a `case-in` expression. For example
* ```rb
* case expr
* in [ x ] then ...
* in Point(a:, b:) then ...
* in Integer => x then ...
* end
* ```
*/
class CasePattern extends AstNode, TPattern {
CasePattern() { casePattern(toGenerated(this)) }
}
/**
* An array pattern, for example:
* ```rb
* in []
* in ["first", Integer => x, "last"]
* in ["a", Integer => x, *]
* in ["a", Integer => x, ]
* in [1, 2, *x, 7, 8]
* in [*init, 7, 8]
* in List["a", Integer => x, *tail]
* ```
*/
class ArrayPattern extends CasePattern, TArrayPattern {
private Ruby::ArrayPattern g;
ArrayPattern() { this = TArrayPattern(g) }
/** Gets the class this pattern matches objects against, if any. */
ConstantReadAccess getClass() { toGenerated(result) = g.getClass() }
/**
* Gets the `n`th element of this list pattern's prefix, i.e. the elements `1, ^two, 3`
* in the following examples:
* ```
* in [ 1, ^two, 3 ]
* in [ 1, ^two, 3, ]
* in [ 1, ^two, 3, *, 4 , 5]
* in [ 1, ^two, 3, *more]
* ```
*/
CasePattern getPrefixElement(int n) {
toGenerated(result) = g.getChild(n) and
(
n < this.restIndex()
or
not exists(restIndex())
)
}
/**
* Gets the `n`th element of this list pattern's suffix, i.e. the elements `4, 5`
* in the following examples:
* ```
* in [ *, 4, 5 ]
* in [ 1, 2, 3, *middle, 4 , 5]
* ```
*/
CasePattern getSuffixElement(int n) { toGenerated(result) = g.getChild(n + this.restIndex() + 1) }
/**
* Gets the variable of the rest token, if any. For example `middle` in `the following array pattern.
* ```rb
* [ 1, 2, 3, *middle, 4 , 5]
* ```
*/
LocalVariableWriteAccess getRestVariableAccess() {
toGenerated(result) = g.getChild(restIndex()).(Ruby::SplatParameter).getName()
}
/**
* Holds if this pattern permits any unmatched remaining elements, i.e. the pattern does not have a trailing `,`
* and does not contain a rest token (`*` or `*name`) either.
*/
predicate allowsUnmatchedElements() { not exists(this.restIndex()) }
private int restIndex() { g.getChild(result) instanceof Ruby::SplatParameter }
final override string getAPrimaryQlClass() { result = "ArrayPattern" }
final override string toString() { result = "[ ..., * ]" }
final override AstNode getAChild(string pred) {
result = super.getAChild(pred)
or
pred = "getClass" and result = this.getClass()
or
pred = "getPrefixElement" and result = this.getPrefixElement(_)
or
pred = "getSuffixElement" and result = this.getSuffixElement(_)
or
pred = "getRestVariableAccess" and result = this.getRestVariableAccess()
}
}
/**
* A find pattern, for example:
* ```rb
* in [*, "a", Integer => x, *]
* in List[*init, "a", Integer => x, *tail]
* in List[*, "a", Integer => x, *]
* ```
*/
class FindPattern extends CasePattern, TFindPattern {
private Ruby::FindPattern g;
FindPattern() { this = TFindPattern(g) }
/** Gets the class this pattern matches objects against, if any. */
ConstantReadAccess getClass() { toGenerated(result) = g.getClass() }
/** Gets the `n`th element of this list pattern. */
CasePattern getElement(int n) { toGenerated(result) = g.getChild(n + 1) }
/** Gets an element of this list pattern. */
CasePattern getAnElement() { result = this.getElement(_) }
/**
* Gets the variable for the prefix of this list pattern, if any. For example `init` in:
* ```rb
* in List[*init, "a", Integer => x, *tail]
* ```
*/
LocalVariableWriteAccess getPrefixVariableAccess() {
toGenerated(result) = g.getChild(0).(Ruby::SplatParameter).getName()
}
/**
* Gets the variable for the suffix of this list pattern, if any. For example `tail` in:
* ```rb
* in List[*init, "a", Integer => x, *tail]
* ```
*/
LocalVariableWriteAccess getSuffixVariableAccess() {
toGenerated(result) = max(int i | | g.getChild(i) order by i).(Ruby::SplatParameter).getName()
}
final override string getAPrimaryQlClass() { result = "FindPattern" }
final override string toString() { result = "[ *,...,* ]" }
final override AstNode getAChild(string pred) {
result = super.getAChild(pred)
or
pred = "getClass" and result = this.getClass()
or
pred = "getElement" and result = this.getElement(_)
or
pred = "getPrefixVariableAccess" and result = this.getPrefixVariableAccess()
or
pred = "getSuffixVariableAccess" and result = this.getSuffixVariableAccess()
}
}
/**
* A hash pattern, for example:
* ```rb
* in {}
* in { a: 1 }
* in { a: 1, **rest }
* in { a: 1, **nil }
* in Node{ label: , children: [] }
* ```
*/
class HashPattern extends CasePattern, THashPattern {
private Ruby::HashPattern g;
HashPattern() { this = THashPattern(g) }
/** Gets the class this pattern matches objects against, if any. */
ConstantReadAccess getClass() { toGenerated(result) = g.getClass() }
private Ruby::KeywordPattern keyValuePair(int n) { result = g.getChild(n) }
/** Gets the key of the `n`th pair. */
StringlikeLiteral getKey(int n) { toGenerated(result) = keyValuePair(n).getKey() }
/** Gets the value of the `n`th pair. */
CasePattern getValue(int n) { toGenerated(result) = keyValuePair(n).getValue() }
/** Gets the value for a given key name. */
CasePattern getValueByKey(string key) {
exists(int i | key = this.getKey(i).getValueText() and result = this.getValue(i))
}
/**
* Gets the variable of the keyword rest token, if any. For example `rest` in:
* ```rb
* in { a: 1, **rest }
* ```
*/
LocalVariableWriteAccess getRestVariableAccess() {
toGenerated(result) =
max(int i | | g.getChild(i) order by i).(Ruby::HashSplatParameter).getName()
}
/**
* Holds if this pattern is terminated by `**nil` indicating that the pattern does not permit
* any unmatched remaining pairs.
*/
predicate allowsUnmatchedElements() { g.getChild(_) instanceof Ruby::HashSplatNil }
final override string getAPrimaryQlClass() { result = "HashPattern" }
final override string toString() { result = "{ ..., ** }" }
final override AstNode getAChild(string pred) {
result = super.getAChild(pred)
or
pred = "getClass" and result = this.getClass()
or
pred = "getKey" and result = this.getKey(_)
or
pred = "getValue" and result = this.getValue(_)
or
pred = "getRestVariableAccess" and result = this.getRestVariableAccess()
}
}
/**
* A composite pattern matching one of the given sub-patterns, for example:
* ```rb
* in 1 | 2 | 3
* ```
*/
class AlternativePattern extends CasePattern, TAlternativePattern {
private Ruby::AlternativePattern g;
AlternativePattern() { this = TAlternativePattern(g) }
/** Gets the `n`th alternative of this pattern. */
CasePattern getAlternative(int n) { toGenerated(result) = g.getAlternatives(n) }
/** Gets an alternative of this pattern. */
CasePattern getAnAlternative() { result = this.getAlternative(_) }
final override string getAPrimaryQlClass() { result = "AlternativePattern" }
final override string toString() { result = "... | ..." }
final override AstNode getAChild(string pred) {
result = super.getAChild(pred)
or
pred = "getAlternative" and result = this.getAlternative(_)
}
}
/**
* A pattern match that binds to the specified local variable, for example `Integer => a`
* in the following:
* ```rb
* case 1
* in Integer => a then puts "#{a} is an integer value"
* end
* ```
*/
class AsPattern extends CasePattern, TAsPattern {
private Ruby::AsPattern g;
AsPattern() { this = TAsPattern(g) }
/** Gets the underlying pattern. */
CasePattern getPattern() { toGenerated(result) = g.getValue() }
/** Gets the variable access for this pattern. */
LocalVariableWriteAccess getVariableAccess() { toGenerated(result) = g.getName() }
final override string getAPrimaryQlClass() { result = "AsPattern" }
final override string toString() { result = "... => ..." }
final override AstNode getAChild(string pred) {
result = super.getAChild(pred)
or
pred = "getPattern" and result = this.getPattern()
or
pred = "getVariableAccess" and result = this.getVariableAccess()
}
}
/**
* A variable reference in a pattern, i.e. `^x` in the following example:
* ```rb
* x = 10
* case expr
* in ^x then puts "ok"
* end
* ```
*/
class VariableReferencePattern extends CasePattern, TVariableReferencePattern {
private Ruby::VariableReferencePattern g;
VariableReferencePattern() { this = TVariableReferencePattern(g) }
/** Gets the variable access corresponding to this variable reference pattern. */
LocalVariableReadAccess getVariableAccess() { toGenerated(result) = g.getName() }
final override string getAPrimaryQlClass() { result = "VariableReferencePattern" }
final override string toString() { result = "^..." }
final override AstNode getAChild(string pred) {
result = super.getAChild(pred)
or
pred = "getVariableAccess" and result = this.getVariableAccess()
}
}

View File

@@ -3,22 +3,20 @@ private import internal.AST
private import internal.Scope
private import internal.TreeSitter
class Scope extends AstNode, TScopeType {
private Scope::Range range;
/**
* A variable scope. This is either a top-level (file), a module, a class,
* or a callable.
*/
class Scope extends AstNode, TScopeType instanceof ScopeImpl {
/** Gets the outer scope, if any. */
Scope getOuterScope() { result = super.getOuterScopeImpl() }
Scope() { range = toGenerated(this) }
/** Gets a variable declared in this scope. */
Variable getAVariable() { result = super.getAVariableImpl() }
/** Gets the scope in which this scope is nested, if any. */
Scope getOuterScope() { toGenerated(result) = range.getOuterScope() }
/** Gets a variable that is declared in this scope. */
final Variable getAVariable() { result.getDeclaringScope() = this }
/** Gets the variable declared in this scope with the given name, if any. */
final Variable getVariable(string name) {
result = this.getAVariable() and
result.getName() = name
}
/** Gets the variable named `name` declared in this scope. */
Variable getVariable(string name) { result = super.getVariableImpl(name) }
}
/** A scope in which a `self` variable exists. */
class SelfScope extends Scope, TSelfScopeType { }

View File

@@ -5,6 +5,7 @@ private import codeql.Locations
private import internal.AST
private import internal.TreeSitter
private import internal.Variable
private import internal.Parameter
/** A variable declared in a scope. */
class Variable instanceof VariableImpl {
@@ -50,7 +51,7 @@ class LocalVariable extends Variable, TLocalVariable {
*
* `x` is a captured variable, whereas `y` is not.
*/
predicate isCaptured() { this.getAnAccess().isCapturedAccess() }
final predicate isCaptured() { this.getAnAccess().isCapturedAccess() }
}
/** A global variable. */
@@ -110,7 +111,11 @@ class VariableAccess extends Expr instanceof VariableAccessImpl {
* the access to `elements` in the parameter list is an implicit assignment,
* as is the first access to `e`.
*/
predicate isImplicitWrite() { implicitWriteAccess(toGenerated(this)) }
predicate isImplicitWrite() {
implicitWriteAccess(toGenerated(this))
or
this = any(SimpleParameterSynthImpl p).getDefininingAccess()
}
final override string toString() { result = VariableAccessImpl.super.toString() }
}
@@ -147,7 +152,7 @@ class LocalVariableAccess extends VariableAccess instanceof LocalVariableAccessI
* the access to `x` in the first `puts x` is a captured access, while
* the access to `x` in the second `puts x` is not.
*/
predicate isCapturedAccess() { isCapturedAccess(this) }
final predicate isCapturedAccess() { isCapturedAccess(this) }
}
/** An access to a local variable where the value is updated. */
@@ -195,10 +200,4 @@ class SelfVariableAccess extends LocalVariableAccess instanceof SelfVariableAcce
}
/** An access to the `self` variable where the value is read. */
class SelfVariableReadAccess extends SelfVariableAccess, VariableReadAccess {
// We override the definition in `LocalVariableAccess` because it gives the
// wrong result for synthesised `self` variables.
override predicate isCapturedAccess() {
this.getVariable().getDeclaringScope() != this.getCfgScope()
}
}
class SelfVariableReadAccess extends SelfVariableAccess, VariableReadAccess { }

View File

@@ -2,6 +2,7 @@ import codeql.Locations
private import TreeSitter
private import codeql.ruby.ast.internal.Call
private import codeql.ruby.ast.internal.Parameter
private import codeql.ruby.ast.internal.Pattern
private import codeql.ruby.ast.internal.Variable
private import codeql.ruby.AST as AST
private import Synthesis
@@ -30,6 +31,7 @@ private module Cached {
TAddExprReal(Ruby::Binary g) { g instanceof @ruby_binary_plus } or
TAddExprSynth(AST::AstNode parent, int i) { mkSynthChild(AddExprKind(), parent, i) } or
TAliasStmt(Ruby::Alias g) or
TAlternativePattern(Ruby::AlternativePattern g) or
TArgumentList(Ruby::AstNode g) {
(
g.getParent() instanceof Ruby::Break or
@@ -44,6 +46,7 @@ private module Cached {
g instanceof Ruby::RightAssignmentList
)
} or
TArrayPattern(Ruby::ArrayPattern g) or
TAssignAddExpr(Ruby::OperatorAssignment g) { g instanceof @ruby_operator_assignment_plusequal } or
TAssignBitwiseAndExpr(Ruby::OperatorAssignment g) {
g instanceof @ruby_operator_assignment_ampersandequal
@@ -77,6 +80,7 @@ private module Cached {
g instanceof @ruby_operator_assignment_ranglerangleequal
} or
TAssignSubExpr(Ruby::OperatorAssignment g) { g instanceof @ruby_operator_assignment_minusequal } or
TAsPattern(Ruby::AsPattern g) or
TBareStringLiteral(Ruby::BareString g) or
TBareSymbolLiteral(Ruby::BareSymbol g) or
TBeginBlock(Ruby::BeginBlock g) or
@@ -93,10 +97,12 @@ private module Cached {
} or
TBlockArgument(Ruby::BlockArgument g) or
TBlockParameter(Ruby::BlockParameter g) or
TBraceBlock(Ruby::Block g) { not g.getParent() instanceof Ruby::Lambda } or
TBraceBlockSynth(AST::AstNode parent, int i) { mkSynthChild(BraceBlockKind(), parent, i) } or
TBraceBlockReal(Ruby::Block g) { not g.getParent() instanceof Ruby::Lambda } or
TBreakStmt(Ruby::Break g) or
TCaseEqExpr(Ruby::Binary g) { g instanceof @ruby_binary_equalequalequal } or
TCaseExpr(Ruby::Case g) or
TCaseMatch(Ruby::CaseMatch g) or
TCharacterLiteral(Ruby::Character g) or
TClassDeclaration(Ruby::Class g) or
TClassVariableAccessReal(Ruby::ClassVariable g, AST::ClassVariable v) {
@@ -123,15 +129,17 @@ private module Cached {
TElse(Ruby::Else g) or
TElsif(Ruby::Elsif g) or
TEmptyStmt(Ruby::EmptyStatement g) or
TEncoding(Ruby::Encoding g) or
TEndBlock(Ruby::EndBlock g) or
TEnsure(Ruby::Ensure g) or
TEqExpr(Ruby::Binary g) { g instanceof @ruby_binary_equalequal } or
TExponentExprReal(Ruby::Binary g) { g instanceof @ruby_binary_starstar } or
TExponentExprSynth(AST::AstNode parent, int i) { mkSynthChild(ExponentExprKind(), parent, i) } or
TFalseLiteral(Ruby::False g) or
TFile(Ruby::File g) or
TFindPattern(Ruby::FindPattern g) or
TFloatLiteral(Ruby::Float g) { not any(Ruby::Rational r).getChild() = g } or
TForExpr(Ruby::For g) or
TForIn(Ruby::In g) or // TODO REMOVE
TForwardParameter(Ruby::ForwardParameter g) or
TForwardArgument(Ruby::ForwardArgument g) or
TGEExpr(Ruby::Binary g) { g instanceof @ruby_binary_rangleequal } or
@@ -144,12 +152,17 @@ private module Cached {
} or
THashKeySymbolLiteral(Ruby::HashKeySymbol g) or
THashLiteral(Ruby::Hash g) or
THashPattern(Ruby::HashPattern g) or
THashSplatExpr(Ruby::HashSplatArgument g) or
THashSplatParameter(Ruby::HashSplatParameter g) or
THashSplatNilParameter(Ruby::HashSplatNil g) { not g.getParent() instanceof Ruby::HashPattern } or
THashSplatParameter(Ruby::HashSplatParameter g) {
not g.getParent() instanceof Ruby::HashPattern
} or
THereDoc(Ruby::HeredocBeginning g) or
TIdentifierMethodCall(Ruby::Identifier g) { isIdentifierMethodCall(g) } or
TIf(Ruby::If g) or
TIfModifierExpr(Ruby::IfModifier g) or
TInClause(Ruby::InClause g) or
TInstanceVariableAccessReal(Ruby::InstanceVariable g, AST::InstanceVariable v) {
InstanceVariableAccess::range(g, v)
} or
@@ -166,6 +179,7 @@ private module Cached {
TLShiftExprSynth(AST::AstNode parent, int i) { mkSynthChild(LShiftExprKind(), parent, i) } or
TLTExpr(Ruby::Binary g) { g instanceof @ruby_binary_langle } or
TLambda(Ruby::Lambda g) or
TLine(Ruby::Line g) or
TLeftAssignmentList(Ruby::LeftAssignmentList g) or
TLocalVariableAccessReal(Ruby::Identifier g, TLocalVariableReal v) {
LocalVariableAccess::range(g, v)
@@ -229,6 +243,10 @@ private module Cached {
vcall(g)
or
explicitAssignmentNode(g, _)
or
casePattern(g)
or
classReferencePattern(g)
)
} or
TScopeResolutionMethodCall(Ruby::ScopeResolution g, Ruby::Identifier i) {
@@ -238,14 +256,20 @@ private module Cached {
TSelfSynth(AST::AstNode parent, int i, AST::SelfVariable v) {
mkSynthChild(SelfKind(v), parent, i)
} or
TSimpleParameter(Ruby::Identifier g) { g instanceof Parameter::Range } or
TSimpleParameterReal(Ruby::Identifier g) { g instanceof Parameter::Range } or
TSimpleParameterSynth(AST::AstNode parent, int i) {
mkSynthChild(SimpleParameterKind(), parent, i)
} or
TSimpleSymbolLiteral(Ruby::SimpleSymbol g) or
TSingletonClass(Ruby::SingletonClass g) or
TSingletonMethod(Ruby::SingletonMethod g) or
TSpaceshipExpr(Ruby::Binary g) { g instanceof @ruby_binary_langleequalrangle } or
TSplatExprReal(Ruby::SplatArgument g) or
TSplatExprSynth(AST::AstNode parent, int i) { mkSynthChild(SplatExprKind(), parent, i) } or
TSplatParameter(Ruby::SplatParameter g) or
TSplatParameter(Ruby::SplatParameter g) {
not g.getParent() instanceof Ruby::ArrayPattern and
not g.getParent() instanceof Ruby::FindPattern
} or
TStmtSequenceSynth(AST::AstNode parent, int i) { mkSynthChild(StmtSequenceKind(), parent, i) } or
TStringArrayLiteral(Ruby::StringArray g) or
TStringConcatenation(Ruby::ChainedString g) or
@@ -266,6 +290,10 @@ private module Cached {
vcall(g)
or
explicitAssignmentNode(g, _)
or
casePattern(g)
or
classReferencePattern(g)
} or
TTokenMethodName(MethodName::Token g) { MethodName::range(g) } or
TTokenSuperCall(Ruby::Super g) { vcall(g) } or
@@ -279,52 +307,57 @@ private module Cached {
TUnlessModifierExpr(Ruby::UnlessModifier g) or
TUntilExpr(Ruby::Until g) or
TUntilModifierExpr(Ruby::UntilModifier g) or
TVariableReferencePattern(Ruby::VariableReferencePattern g) or
TWhenExpr(Ruby::When g) or
TWhileExpr(Ruby::While g) or
TWhileModifierExpr(Ruby::WhileModifier g) or
TYieldCall(Ruby::Yield g)
class TAstNodeReal =
TAddExprReal or TAliasStmt or TArgumentList or TAssignAddExpr or TAssignBitwiseAndExpr or
TAssignBitwiseOrExpr or TAssignBitwiseXorExpr or TAssignDivExpr or TAssignExponentExpr or
TAssignExprReal or TAssignLShiftExpr or TAssignLogicalAndExpr or TAssignLogicalOrExpr or
TAssignModuloExpr or TAssignMulExpr or TAssignRShiftExpr or TAssignSubExpr or
TBareStringLiteral or TBareSymbolLiteral or TBeginBlock or TBeginExpr or
TBitwiseAndExprReal or TBitwiseOrExprReal or TBitwiseXorExprReal or TBlockArgument or
TBlockParameter or TBraceBlock or TBreakStmt or TCaseEqExpr or TCaseExpr or
TAddExprReal or TAliasStmt or TAlternativePattern or TArgumentList or TArrayPattern or
TAsPattern or TAssignAddExpr or TAssignBitwiseAndExpr or TAssignBitwiseOrExpr or
TAssignBitwiseXorExpr or TAssignDivExpr or TAssignExponentExpr or TAssignExprReal or
TAssignLShiftExpr or TAssignLogicalAndExpr or TAssignLogicalOrExpr or TAssignModuloExpr or
TAssignMulExpr or TAssignRShiftExpr or TAssignSubExpr or TBareStringLiteral or
TBareSymbolLiteral or TBeginBlock or TBeginExpr or TBitwiseAndExprReal or
TBitwiseOrExprReal or TBitwiseXorExprReal or TBlockArgument or TBlockParameter or
TBraceBlockReal or TBreakStmt or TCaseEqExpr or TCaseExpr or TCaseMatch or
TCharacterLiteral or TClassDeclaration or TClassVariableAccessReal or TComplementExpr or
TComplexLiteral or TDefinedExpr or TDelimitedSymbolLiteral or TDestructuredLeftAssignment or
TDivExprReal or TDo or TDoBlock or TElementReference or TElse or TElsif or TEmptyStmt or
TEndBlock or TEnsure or TEqExpr or TExponentExprReal or TFalseLiteral or TFloatLiteral or
TForExpr or TForIn or TForwardParameter or TForwardArgument or TGEExpr or TGTExpr or
TGlobalVariableAccessReal or THashKeySymbolLiteral or THashLiteral or THashSplatExpr or
THashSplatParameter or THereDoc or TIdentifierMethodCall or TIf or TIfModifierExpr or
TInstanceVariableAccessReal or TIntegerLiteralReal or TKeywordParameter or TLEExpr or
TLShiftExprReal or TLTExpr or TLambda or TLeftAssignmentList or TLocalVariableAccessReal or
TLogicalAndExprReal or TLogicalOrExprReal or TMethod or TModuleDeclaration or
TModuloExprReal or TMulExprReal or TNEExpr or TNextStmt or TNilLiteral or
TNoRegExpMatchExpr or TNotExpr or TOptionalParameter or TPair or TParenthesizedExpr or
TRShiftExprReal or TRangeLiteralReal or TRationalLiteral or TRedoStmt or TRegExpLiteral or
TRegExpMatchExpr or TRegularArrayLiteral or TRegularMethodCall or TRegularStringLiteral or
TRegularSuperCall or TRescueClause or TRescueModifierExpr or TRetryStmt or TReturnStmt or
TEncoding or TEndBlock or TEnsure or TEqExpr or TExponentExprReal or TFalseLiteral or
TFile or TFindPattern or TFloatLiteral or TForExpr or TForwardParameter or
TForwardArgument or TGEExpr or TGTExpr or TGlobalVariableAccessReal or
THashKeySymbolLiteral or THashLiteral or THashPattern or THashSplatExpr or
THashSplatNilParameter or THashSplatParameter or THereDoc or TIdentifierMethodCall or TIf or
TIfModifierExpr or TInClause or TInstanceVariableAccessReal or TIntegerLiteralReal or
TKeywordParameter or TLEExpr or TLShiftExprReal or TLTExpr or TLambda or
TLeftAssignmentList or TLine or TLocalVariableAccessReal or TLogicalAndExprReal or
TLogicalOrExprReal or TMethod or TModuleDeclaration or TModuloExprReal or TMulExprReal or
TNEExpr or TNextStmt or TNilLiteral or TNoRegExpMatchExpr or TNotExpr or
TOptionalParameter or TPair or TParenthesizedExpr or TRShiftExprReal or TRangeLiteralReal or
TRationalLiteral or TRedoStmt or TRegExpLiteral or TRegExpMatchExpr or
TRegularArrayLiteral or TRegularMethodCall or TRegularStringLiteral or TRegularSuperCall or
TRescueClause or TRescueModifierExpr or TRetryStmt or TReturnStmt or
TScopeResolutionConstantAccess or TScopeResolutionMethodCall or TSelfReal or
TSimpleParameter or TSimpleSymbolLiteral or TSingletonClass or TSingletonMethod or
TSimpleParameterReal or TSimpleSymbolLiteral or TSingletonClass or TSingletonMethod or
TSpaceshipExpr or TSplatExprReal or TSplatParameter or TStringArrayLiteral or
TStringConcatenation or TStringEscapeSequenceComponent or TStringInterpolationComponent or
TStringTextComponent or TSubExprReal or TSubshellLiteral or TSymbolArrayLiteral or
TTernaryIfExpr or TThen or TTokenConstantAccess or TTokenMethodName or TTokenSuperCall or
TToplevel or TTrueLiteral or TTuplePatternParameter or TUnaryMinusExpr or TUnaryPlusExpr or
TUndefStmt or TUnlessExpr or TUnlessModifierExpr or TUntilExpr or TUntilModifierExpr or
TWhenExpr or TWhileExpr or TWhileModifierExpr or TYieldCall;
TVariableReferencePattern or TWhenExpr or TWhileExpr or TWhileModifierExpr or TYieldCall;
class TAstNodeSynth =
TAddExprSynth or TAssignExprSynth or TBitwiseAndExprSynth or TBitwiseOrExprSynth or
TBitwiseXorExprSynth or TClassVariableAccessSynth or TConstantReadAccessSynth or
TDivExprSynth or TExponentExprSynth or TGlobalVariableAccessSynth or
TInstanceVariableAccessSynth or TIntegerLiteralSynth or TLShiftExprSynth or
TLocalVariableAccessSynth or TLogicalAndExprSynth or TLogicalOrExprSynth or
TMethodCallSynth or TModuloExprSynth or TMulExprSynth or TRShiftExprSynth or
TRangeLiteralSynth or TSelfSynth or TSplatExprSynth or TStmtSequenceSynth or TSubExprSynth;
TBitwiseXorExprSynth or TBraceBlockSynth or TClassVariableAccessSynth or
TConstantReadAccessSynth or TDivExprSynth or TExponentExprSynth or
TGlobalVariableAccessSynth or TInstanceVariableAccessSynth or TIntegerLiteralSynth or
TLShiftExprSynth or TLocalVariableAccessSynth or TLogicalAndExprSynth or
TLogicalOrExprSynth or TMethodCallSynth or TModuloExprSynth or TMulExprSynth or
TRShiftExprSynth or TRangeLiteralSynth or TSelfSynth or TSimpleParameterSynth or
TSplatExprSynth or TStmtSequenceSynth or TSubExprSynth;
/**
* Gets the underlying TreeSitter entity for a given AST node. This does not
@@ -335,7 +368,10 @@ private module Cached {
Ruby::AstNode toGenerated(TAstNodeReal n) {
n = TAddExprReal(result) or
n = TAliasStmt(result) or
n = TAlternativePattern(result) or
n = TArgumentList(result) or
n = TArrayPattern(result) or
n = TAsPattern(result) or
n = TAssignAddExpr(result) or
n = TAssignBitwiseAndExpr(result) or
n = TAssignBitwiseOrExpr(result) or
@@ -343,9 +379,9 @@ private module Cached {
n = TAssignDivExpr(result) or
n = TAssignExponentExpr(result) or
n = TAssignExprReal(result) or
n = TAssignLShiftExpr(result) or
n = TAssignLogicalAndExpr(result) or
n = TAssignLogicalOrExpr(result) or
n = TAssignLShiftExpr(result) or
n = TAssignModuloExpr(result) or
n = TAssignMulExpr(result) or
n = TAssignRShiftExpr(result) or
@@ -359,10 +395,11 @@ private module Cached {
n = TBitwiseXorExprReal(result) or
n = TBlockArgument(result) or
n = TBlockParameter(result) or
n = TBraceBlock(result) or
n = TBraceBlockReal(result) or
n = TBreakStmt(result) or
n = TCaseEqExpr(result) or
n = TCaseExpr(result) or
n = TCaseMatch(result) or
n = TCharacterLiteral(result) or
n = TClassDeclaration(result) or
n = TClassVariableAccessReal(result, _) or
@@ -372,44 +409,50 @@ private module Cached {
n = TDelimitedSymbolLiteral(result) or
n = TDestructuredLeftAssignment(result) or
n = TDivExprReal(result) or
n = TDo(result) or
n = TDoBlock(result) or
n = TDo(result) or
n = TElementReference(result) or
n = TElse(result) or
n = TElsif(result) or
n = TEmptyStmt(result) or
n = TEncoding(result) or
n = TEndBlock(result) or
n = TEnsure(result) or
n = TEqExpr(result) or
n = TExponentExprReal(result) or
n = TFalseLiteral(result) or
n = TFile(result) or
n = TFindPattern(result) or
n = TFloatLiteral(result) or
n = TForExpr(result) or
n = TForIn(result) or // TODO REMOVE
n = TForwardArgument(result) or
n = TForwardParameter(result) or
n = TGEExpr(result) or
n = TGTExpr(result) or
n = TGlobalVariableAccessReal(result, _) or
n = TGTExpr(result) or
n = THashKeySymbolLiteral(result) or
n = THashLiteral(result) or
n = THashPattern(result) or
n = THashSplatExpr(result) or
n = THashSplatNilParameter(result) or
n = THashSplatParameter(result) or
n = THereDoc(result) or
n = TIdentifierMethodCall(result) or
n = TIf(result) or
n = TIfModifierExpr(result) or
n = TIf(result) or
n = TInClause(result) or
n = TInstanceVariableAccessReal(result, _) or
n = TIntegerLiteralReal(result) or
n = TKeywordParameter(result) or
n = TLEExpr(result) or
n = TLShiftExprReal(result) or
n = TLTExpr(result) or
n = TLambda(result) or
n = TLEExpr(result) or
n = TLeftAssignmentList(result) or
n = TLine(result) or
n = TLocalVariableAccessReal(result, _) or
n = TLogicalAndExprReal(result) or
n = TLogicalOrExprReal(result) or
n = TLShiftExprReal(result) or
n = TLTExpr(result) or
n = TMethod(result) or
n = TModuleDeclaration(result) or
n = TModuloExprReal(result) or
@@ -422,7 +465,6 @@ private module Cached {
n = TOptionalParameter(result) or
n = TPair(result) or
n = TParenthesizedExpr(result) or
n = TRShiftExprReal(result) or
n = TRangeLiteralReal(result) or
n = TRationalLiteral(result) or
n = TRedoStmt(result) or
@@ -436,10 +478,11 @@ private module Cached {
n = TRescueModifierExpr(result) or
n = TRetryStmt(result) or
n = TReturnStmt(result) or
n = TRShiftExprReal(result) or
n = TScopeResolutionConstantAccess(result, _) or
n = TScopeResolutionMethodCall(result, _) or
n = TSelfReal(result) or
n = TSimpleParameter(result) or
n = TSimpleParameterReal(result) or
n = TSimpleSymbolLiteral(result) or
n = TSingletonClass(result) or
n = TSingletonMethod(result) or
@@ -469,6 +512,7 @@ private module Cached {
n = TUnlessModifierExpr(result) or
n = TUntilExpr(result) or
n = TUntilModifierExpr(result) or
n = TVariableReferencePattern(result) or
n = TWhenExpr(result) or
n = TWhileExpr(result) or
n = TWhileModifierExpr(result) or
@@ -488,6 +532,8 @@ private module Cached {
or
result = TBitwiseXorExprSynth(parent, i)
or
result = TBraceBlockSynth(parent, i)
or
result = TClassVariableAccessSynth(parent, i, _)
or
result = TConstantReadAccessSynth(parent, i, _)
@@ -522,6 +568,8 @@ private module Cached {
or
result = TSelfSynth(parent, i, _)
or
result = TSimpleParameterSynth(parent, i)
or
result = TSplatExprSynth(parent, i)
or
result = TStmtSequenceSynth(parent, i)
@@ -575,6 +623,8 @@ TAstNodeReal fromGenerated(Ruby::AstNode n) { n = toGenerated(result) }
class TCall = TMethodCall or TYieldCall;
class TCase = TCaseExpr or TCaseMatch;
class TMethodCall =
TMethodCallSynth or TIdentifierMethodCall or TScopeResolutionMethodCall or TRegularMethodCall or
TElementReference or TSuperCall or TUnaryOperation or TBinaryOperation;
@@ -584,7 +634,7 @@ class TSuperCall = TTokenSuperCall or TRegularSuperCall;
class TConstantAccess =
TTokenConstantAccess or TScopeResolutionConstantAccess or TNamespace or TConstantReadAccessSynth;
class TControlExpr = TConditionalExpr or TCaseExpr or TLoop;
class TControlExpr = TConditionalExpr or TCaseExpr or TCaseMatch or TLoop;
class TConditionalExpr =
TIfExpr or TUnlessExpr or TIfModifierExpr or TUnlessModifierExpr or TTernaryIfExpr;
@@ -598,10 +648,10 @@ class TLoop = TConditionalLoop or TForExpr;
class TSelf = TSelfReal or TSelfSynth;
class TExpr =
TSelf or TArgumentList or TRescueClause or TRescueModifierExpr or TPair or TStringConcatenation or
TCall or TBlockArgument or TConstantAccess or TControlExpr or TWhenExpr or TLiteral or
TCallable or TVariableAccess or TStmtSequence or TOperation or TSimpleParameter or
TForwardArgument;
TSelf or TArgumentList or TInClause or TRescueClause or TRescueModifierExpr or TPair or
TStringConcatenation or TCall or TBlockArgument or TConstantAccess or TControlExpr or
TWhenExpr or TLiteral or TCallable or TVariableAccess or TStmtSequence or TOperation or
TSimpleParameter or TForwardArgument;
class TSplatExpr = TSplatExprReal or TSplatExprSynth;
@@ -612,8 +662,9 @@ class TStmtSequence =
class TBodyStmt = TBeginExpr or TModuleBase or TMethod or TLambda or TDoBlock or TSingletonMethod;
class TLiteral =
TNumericLiteral or TNilLiteral or TBooleanLiteral or TStringlikeLiteral or TCharacterLiteral or
TArrayLiteral or THashLiteral or TRangeLiteral or TTokenMethodName;
TEncoding or TFile or TLine or TNumericLiteral or TNilLiteral or TBooleanLiteral or
TStringlikeLiteral or TCharacterLiteral or TArrayLiteral or THashLiteral or TRangeLiteral or
TTokenMethodName;
class TNumericLiteral = TIntegerLiteral or TFloatLiteral or TRationalLiteral or TComplexLiteral;
@@ -639,6 +690,8 @@ class TCallable = TMethodBase or TLambda or TBlock;
class TMethodBase = TMethod or TSingletonMethod;
class TBraceBlock = TBraceBlockReal or TBraceBlockSynth;
class TBlock = TDoBlock or TBraceBlock;
class TModuleBase = TToplevel or TNamespace or TSingletonClass;
@@ -727,8 +780,10 @@ class TStmt =
class TReturningStmt = TReturnStmt or TBreakStmt or TNextStmt;
class TParameter =
TPatternParameter or TBlockParameter or THashSplatParameter or TKeywordParameter or
TOptionalParameter or TSplatParameter or TForwardParameter;
TPatternParameter or TBlockParameter or THashSplatParameter or THashSplatNilParameter or
TKeywordParameter or TOptionalParameter or TSplatParameter or TForwardParameter;
class TSimpleParameter = TSimpleParameterReal or TSimpleParameterSynth;
class TPatternParameter = TSimpleParameter or TTuplePatternParameter;

View File

@@ -50,7 +50,7 @@ class MethodCallSynth extends MethodCallImpl, TMethodCallSynth {
final override int getNumberOfArgumentsImpl() { this = TMethodCallSynth(_, _, _, _, result) }
final override Block getBlockImpl() { none() }
final override Block getBlockImpl() { synthChild(this, -2, result) }
}
class IdentifierMethodCall extends MethodCallImpl, TIdentifierMethodCall {

View File

@@ -0,0 +1,36 @@
private import TreeSitter
private import codeql.ruby.AST
private import codeql.ruby.ast.internal.AST
abstract class CaseExprImpl extends ControlExpr, TCase {
abstract Expr getValue();
abstract Expr getBranch(int n);
}
class CaseWhenExpr extends CaseExprImpl, TCaseExpr {
private Ruby::Case g;
CaseWhenExpr() { this = TCaseExpr(g) }
final override Expr getValue() { toGenerated(result) = g.getValue() }
final override Expr getBranch(int n) {
toGenerated(result) = g.getChild(n) or
toGenerated(result) = g.getChild(n)
}
}
class CaseMatch extends CaseExprImpl, TCaseMatch {
private Ruby::CaseMatch g;
CaseMatch() { this = TCaseMatch(g) }
final override Expr getValue() { toGenerated(result) = g.getValue() }
final override Expr getBranch(int n) {
toGenerated(result) = g.getClauses(n)
or
n = count(g.getClauses(_)) and toGenerated(result) = g.getElse()
}
}

View File

@@ -0,0 +1,75 @@
private import codeql.ruby.AST
private import codeql.ruby.CFG
private import AST
private import TreeSitter
class StmtSequenceSynth extends StmtSequence, TStmtSequenceSynth {
final override Stmt getStmt(int n) { synthChild(this, n, result) }
final override string toString() { result = "..." }
}
class Then extends StmtSequence, TThen {
private Ruby::Then g;
Then() { this = TThen(g) }
override Stmt getStmt(int n) { toGenerated(result) = g.getChild(n) }
final override string toString() { result = "then ..." }
}
class Else extends StmtSequence, TElse {
private Ruby::Else g;
Else() { this = TElse(g) }
override Stmt getStmt(int n) { toGenerated(result) = g.getChild(n) }
final override string toString() { result = "else ..." }
}
class Do extends StmtSequence, TDo {
private Ruby::Do g;
Do() { this = TDo(g) }
override Stmt getStmt(int n) { toGenerated(result) = g.getChild(n) }
final override string toString() { result = "do ..." }
}
class Ensure extends StmtSequence, TEnsure {
private Ruby::Ensure g;
Ensure() { this = TEnsure(g) }
override Stmt getStmt(int n) { toGenerated(result) = g.getChild(n) }
final override string toString() { result = "ensure ..." }
}
// Not defined by dispatch, as it should not be exposed
Ruby::AstNode getBodyStmtChild(TBodyStmt b, int i) {
result = any(Ruby::Method g | b = TMethod(g)).getChild(i)
or
result = any(Ruby::SingletonMethod g | b = TSingletonMethod(g)).getChild(i)
or
exists(Ruby::Lambda g | b = TLambda(g) |
result = g.getBody().(Ruby::DoBlock).getChild(i) or
result = g.getBody().(Ruby::Block).getChild(i)
)
or
result = any(Ruby::DoBlock g | b = TDoBlock(g)).getChild(i)
or
result = any(Ruby::Program g | b = TToplevel(g)).getChild(i) and
not result instanceof Ruby::BeginBlock
or
result = any(Ruby::Class g | b = TClassDeclaration(g)).getChild(i)
or
result = any(Ruby::SingletonClass g | b = TSingletonClass(g)).getChild(i)
or
result = any(Ruby::Module g | b = TModuleDeclaration(g)).getChild(i)
or
result = any(Ruby::Begin g | b = TBeginExpr(g)).getChild(i)
}

View File

@@ -0,0 +1,28 @@
private import codeql.ruby.AST
private import AST
private import TreeSitter
class BraceBlockReal extends BraceBlock, TBraceBlockReal {
private Ruby::Block g;
BraceBlockReal() { this = TBraceBlockReal(g) }
final override Parameter getParameter(int n) {
toGenerated(result) = g.getParameters().getChild(n)
}
final override Stmt getStmt(int i) { toGenerated(result) = g.getChild(i) }
}
/**
* A synthesized block, such as the block synthesized from the body of
* a `for` loop.
*/
class BraceBlockSynth extends BraceBlock, TBraceBlockSynth {
final override Parameter getParameter(int n) { synthChild(this, n, result) }
final override Stmt getStmt(int i) {
i >= 0 and
synthChild(this, i + this.getNumberOfParameters(), result)
}
}

View File

@@ -1,6 +1,7 @@
private import codeql.ruby.AST
private import AST
private import TreeSitter
private import Variable
module Parameter {
class Range extends Ruby::AstNode {
@@ -17,3 +18,29 @@ module Parameter {
int getPosition() { result = pos }
}
}
abstract class SimpleParameterImpl extends AstNode, TSimpleParameter {
abstract LocalVariable getVariableImpl();
abstract string getNameImpl();
}
class SimpleParameterRealImpl extends SimpleParameterImpl, TSimpleParameterReal {
private Ruby::Identifier g;
SimpleParameterRealImpl() { this = TSimpleParameterReal(g) }
override LocalVariable getVariableImpl() { result = TLocalVariableReal(_, _, g) }
override string getNameImpl() { result = g.getValue() }
}
class SimpleParameterSynthImpl extends SimpleParameterImpl, TSimpleParameterSynth {
SimpleParameterSynthImpl() { this = TSimpleParameterSynth(_, _) }
LocalVariableAccessSynth getDefininingAccess() { synthChild(this, 0, result) }
override LocalVariable getVariableImpl() { result = TLocalVariableSynth(this, _) }
override string getNameImpl() { result = this.getVariableImpl().getName() }
}

View File

@@ -30,3 +30,32 @@ class LeftAssignmentListImpl extends TuplePatternImpl, Ruby::LeftAssignmentList
)
}
}
/**
* Holds if `node` is a case pattern.
*/
predicate casePattern(Ruby::AstNode node) {
node = any(Ruby::InClause parent).getPattern()
or
node = any(Ruby::ArrayPattern parent).getChild(_).(Ruby::UnderscorePatternExpr)
or
node = any(Ruby::FindPattern parent).getChild(_).(Ruby::UnderscorePatternExpr)
or
node = any(Ruby::AlternativePattern parent).getAlternatives(_)
or
node = any(Ruby::AsPattern parent).getValue()
or
node = any(Ruby::KeywordPattern parent).getValue()
}
/**
* Holds if `node` is a class reference used in an
* array, find, or hash pattern.
*/
predicate classReferencePattern(Ruby::AstNode node) {
node = any(Ruby::ArrayPattern p).getClass()
or
node = any(Ruby::FindPattern p).getClass()
or
node = any(Ruby::HashPattern p).getClass()
}

View File

@@ -1,7 +1,9 @@
private import TreeSitter
private import codeql.ruby.AST
private import codeql.ruby.ast.Scope
private import codeql.ruby.ast.internal.AST
private import codeql.ruby.ast.internal.Parameter
private import codeql.ruby.ast.internal.Variable
class TScopeType = TMethodBase or TModuleLike or TBlockLike;
@@ -15,6 +17,8 @@ private class TBlockLike = TDoBlock or TLambda or TBlock or TEndBlock;
private class TModuleLike = TToplevel or TModuleDeclaration or TClassDeclaration or TSingletonClass;
private class TScopeReal = TMethodBase or TModuleLike or TDoBlock or TLambda or TBraceBlock;
module Scope {
class TypeRange = Callable::TypeRange or ModuleBase::TypeRange or @ruby_end_block;
@@ -102,29 +106,105 @@ Ruby::HeredocBody getHereDocBody(Ruby::HeredocBeginning g) {
)
}
private Ruby::AstNode specialParentOf(Ruby::AstNode n) {
n =
[
result.(Ruby::Module).getName(), result.(Ruby::Class).getName(),
result.(Ruby::Class).getSuperclass(), result.(Ruby::SingletonClass).getValue(),
result.(Ruby::Method).getName(), result.(Ruby::SingletonMethod).getName(),
result.(Ruby::SingletonMethod).getObject()
]
}
private Ruby::AstNode parentOf(Ruby::AstNode n) {
n = getHereDocBody(result)
or
exists(Ruby::AstNode parent | parent = n.getParent() |
if
n =
[
parent.(Ruby::Module).getName(), parent.(Ruby::Class).getName(),
parent.(Ruby::Class).getSuperclass(), parent.(Ruby::SingletonClass).getValue(),
parent.(Ruby::Method).getName(), parent.(Ruby::SingletonMethod).getName(),
parent.(Ruby::SingletonMethod).getObject()
]
then result = parent.getParent()
else result = parent
)
result = specialParentOf(n).getParent()
or
not exists(specialParentOf(n)) and
result = n.getParent()
}
/** Gets the enclosing scope of a node */
cached
Scope::Range scopeOf(Ruby::AstNode n) {
exists(Ruby::AstNode p | p = parentOf(n) |
p = result
or
not p instanceof Scope::Range and result = scopeOf(p)
)
private AstNode specialParentOfInclSynth(AstNode n) {
n =
[
result.(Namespace).getScopeExpr(), result.(ClassDeclaration).getSuperclassExpr(),
result.(SingletonMethod).getObject()
]
}
private AstNode parentOfInclSynth(AstNode n) {
(
result = specialParentOfInclSynth(n).getParent()
or
not exists(specialParentOfInclSynth(n)) and
result = n.getParent()
) and
(synthChild(_, _, n) implies synthChild(result, _, n))
}
cached
private module Cached {
/** Gets the enclosing scope of a node */
cached
Scope::Range scopeOf(Ruby::AstNode n) {
exists(Ruby::AstNode p | p = parentOf(n) |
p = result
or
not p instanceof Scope::Range and result = scopeOf(p)
)
}
/**
* Gets the enclosing scope of a node. Unlike `scopeOf`, this predicate
* operates on the external AST API, and therefore takes synthesized nodes
* and synthesized scopes into account.
*/
cached
Scope scopeOfInclSynth(AstNode n) {
exists(AstNode p | p = parentOfInclSynth(n) |
p = result
or
not p instanceof Scope and result = scopeOfInclSynth(p)
)
}
}
import Cached
abstract class ScopeImpl extends AstNode, TScopeType {
final Scope getOuterScopeImpl() { result = scopeOfInclSynth(this) }
abstract Variable getAVariableImpl();
final Variable getVariableImpl(string name) {
result = this.getAVariableImpl() and
result.getName() = name
}
}
private class ScopeRealImpl extends ScopeImpl, TScopeReal {
private Scope::Range range;
ScopeRealImpl() { range = toGenerated(this) }
override Variable getAVariableImpl() { result.getDeclaringScope() = this }
}
// We desugar for loops by implementing them as calls to `each` with a block
// argument. Though this is how the desugaring is described in the MRI parser,
// in practice there is not a real nested scope created, so variables that
// may appear to be local to the loop body (e.g. the iteration variable) are
// scoped to the outer scope rather than the loop body.
private class ScopeSynthImpl extends ScopeImpl, TBraceBlockSynth {
ScopeSynthImpl() { this = TBraceBlockSynth(_, _) }
override Variable getAVariableImpl() {
// Synthesized variables introduced as parameters to this scope
// As this variable is also synthetic, it is genuinely local to this scope.
exists(SimpleParameterSynthImpl p |
p = TSimpleParameterSynth(this, _) and
p.getVariableImpl() = result
)
}
}

View File

@@ -3,6 +3,7 @@
private import AST
private import TreeSitter
private import codeql.ruby.ast.internal.Call
private import codeql.ruby.ast.internal.Expr
private import codeql.ruby.ast.internal.Variable
private import codeql.ruby.ast.internal.Pattern
private import codeql.ruby.ast.internal.Scope
@@ -15,6 +16,7 @@ newtype SynthKind =
BitwiseAndExprKind() or
BitwiseOrExprKind() or
BitwiseXorExprKind() or
BraceBlockKind() or
ClassVariableAccessKind(ClassVariable v) or
DivExprKind() or
ExponentExprKind() or
@@ -33,6 +35,7 @@ newtype SynthKind =
MulExprKind() or
RangeLiteralKind(boolean inclusive) { inclusive in [false, true] } or
RShiftExprKind() or
SimpleParameterKind() or
SplatExprKind() or
StmtSequenceKind() or
SelfKind(SelfVariable v) or
@@ -814,3 +817,94 @@ private module ArrayLiteralDesugar {
final override predicate constantReadAccess(string name) { name = "::Array" }
}
}
/**
* ```rb
* for x in xs
* <loop_body>
* end
* ```
* desugars to, roughly,
* ```rb
* xs.each { |__synth__0| x = __synth__0; <loop_body> }
* ```
*
* Note that for-loops, unlike blocks, do not create a new variable scope, so
* variables within this block inherit the enclosing scope. The exception to
* this is the synthesized variable declared by the block parameter, which is
* scoped to the synthesized block.
*/
private module ForLoopDesugar {
pragma[nomagic]
private predicate forLoopSynthesis(AstNode parent, int i, Child child) {
exists(ForExpr for |
// each call
parent = for and
i = -1 and
child = SynthChild(MethodCallKind("each", false, 0))
or
exists(MethodCall eachCall | eachCall = TMethodCallSynth(for, -1, "each", false, 0) |
// receiver
parent = eachCall and
i = 0 and
child = childRef(for.getValue()) // value is the Enumerable
or
parent = eachCall and
i = -2 and
child = SynthChild(BraceBlockKind())
or
exists(Block block | block = TBraceBlockSynth(eachCall, -2) |
// block params
parent = block and
i = 0 and
child = SynthChild(SimpleParameterKind())
or
exists(SimpleParameter param | param = TSimpleParameterSynth(block, 0) |
parent = param and
i = 0 and
child = SynthChild(LocalVariableAccessSynthKind(TLocalVariableSynth(param, 0)))
or
// assignment to pattern from for loop to synth parameter
parent = block and
i = 1 and
child = SynthChild(AssignExprKind())
or
parent = TAssignExprSynth(block, 1) and
(
i = 0 and
child = childRef(for.getPattern())
or
i = 1 and
child = SynthChild(LocalVariableAccessSynthKind(TLocalVariableSynth(param, 0)))
)
)
or
// rest of block body
parent = block and
child = childRef(for.getBody().(Do).getStmt(i - 2))
)
)
)
}
private class ForLoopSynthesis extends Synthesis {
final override predicate child(AstNode parent, int i, Child child) {
forLoopSynthesis(parent, i, child)
}
final override predicate methodCall(string name, boolean setter, int arity) {
name = "each" and
setter = false and
arity = 0
}
final override predicate localVariable(AstNode n, int i) {
n instanceof TSimpleParameterSynth and
i = 0
}
final override predicate excludeFromControlFlowTree(AstNode n) {
n = any(ForExpr for).getBody()
}
}
}

View File

@@ -53,12 +53,26 @@ module Ruby {
class UnderscoreArg extends @ruby_underscore_arg, AstNode { }
class UnderscoreExpression extends @ruby_underscore_expression, AstNode { }
class UnderscoreLhs extends @ruby_underscore_lhs, AstNode { }
class UnderscoreMethodName extends @ruby_underscore_method_name, AstNode { }
class UnderscorePatternConstant extends @ruby_underscore_pattern_constant, AstNode { }
class UnderscorePatternExpr extends @ruby_underscore_pattern_expr, AstNode { }
class UnderscorePatternExprBasic extends @ruby_underscore_pattern_expr_basic, AstNode { }
class UnderscorePatternPrimitive extends @ruby_underscore_pattern_primitive, AstNode { }
class UnderscorePatternTopExprBody extends @ruby_underscore_pattern_top_expr_body, AstNode { }
class UnderscorePrimary extends @ruby_underscore_primary, AstNode { }
class UnderscoreSimpleNumeric extends @ruby_underscore_simple_numeric, AstNode { }
class UnderscoreStatement extends @ruby_underscore_statement, AstNode { }
class UnderscoreVariable extends @ruby_underscore_variable, AstNode { }
@@ -83,6 +97,23 @@ module Ruby {
}
}
/** A class representing `alternative_pattern` nodes. */
class AlternativePattern extends @ruby_alternative_pattern, AstNode {
/** Gets the name of the primary QL class for this element. */
override string getAPrimaryQlClass() { result = "AlternativePattern" }
/** Gets the location of this element. */
override L::Location getLocation() { ruby_alternative_pattern_def(this, result) }
/** Gets the node corresponding to the field `alternatives`. */
UnderscorePatternExprBasic getAlternatives(int i) {
ruby_alternative_pattern_alternatives(this, i, result)
}
/** Gets a field or child node of this node. */
override AstNode getAFieldOrChild() { ruby_alternative_pattern_alternatives(this, _, result) }
}
/** A class representing `argument_list` nodes. */
class ArgumentList extends @ruby_argument_list, AstNode {
/** Gets the name of the primary QL class for this element. */
@@ -113,6 +144,46 @@ module Ruby {
override AstNode getAFieldOrChild() { ruby_array_child(this, _, result) }
}
/** A class representing `array_pattern` nodes. */
class ArrayPattern extends @ruby_array_pattern, AstNode {
/** Gets the name of the primary QL class for this element. */
override string getAPrimaryQlClass() { result = "ArrayPattern" }
/** Gets the location of this element. */
override L::Location getLocation() { ruby_array_pattern_def(this, result) }
/** Gets the node corresponding to the field `class`. */
UnderscorePatternConstant getClass() { ruby_array_pattern_class(this, result) }
/** Gets the `i`th child of this node. */
AstNode getChild(int i) { ruby_array_pattern_child(this, i, result) }
/** Gets a field or child node of this node. */
override AstNode getAFieldOrChild() {
ruby_array_pattern_class(this, result) or ruby_array_pattern_child(this, _, result)
}
}
/** A class representing `as_pattern` nodes. */
class AsPattern extends @ruby_as_pattern, AstNode {
/** Gets the name of the primary QL class for this element. */
override string getAPrimaryQlClass() { result = "AsPattern" }
/** Gets the location of this element. */
override L::Location getLocation() { ruby_as_pattern_def(this, _, _, result) }
/** Gets the node corresponding to the field `name`. */
Identifier getName() { ruby_as_pattern_def(this, result, _, _) }
/** Gets the node corresponding to the field `value`. */
UnderscorePatternExpr getValue() { ruby_as_pattern_def(this, _, result, _) }
/** Gets a field or child node of this node. */
override AstNode getAFieldOrChild() {
ruby_as_pattern_def(this, result, _, _) or ruby_as_pattern_def(this, _, result, _)
}
}
/** A class representing `assignment` nodes. */
class Assignment extends @ruby_assignment, AstNode {
/** Gets the name of the primary QL class for this element. */
@@ -202,7 +273,7 @@ module Ruby {
override L::Location getLocation() { ruby_binary_def(this, _, _, _, result) }
/** Gets the node corresponding to the field `left`. */
AstNode getLeft() { ruby_binary_def(this, result, _, _, _) }
UnderscoreExpression getLeft() { ruby_binary_def(this, result, _, _, _) }
/** Gets the node corresponding to the field `operator`. */
string getOperator() {
@@ -260,7 +331,7 @@ module Ruby {
}
/** Gets the node corresponding to the field `right`. */
AstNode getRight() { ruby_binary_def(this, _, _, result, _) }
UnderscoreExpression getRight() { ruby_binary_def(this, _, _, result, _) }
/** Gets a field or child node of this node. */
override AstNode getAFieldOrChild() {
@@ -397,6 +468,31 @@ module Ruby {
}
}
/** A class representing `case_match` nodes. */
class CaseMatch extends @ruby_case_match, AstNode {
/** Gets the name of the primary QL class for this element. */
override string getAPrimaryQlClass() { result = "CaseMatch" }
/** Gets the location of this element. */
override L::Location getLocation() { ruby_case_match_def(this, _, result) }
/** Gets the node corresponding to the field `clauses`. */
InClause getClauses(int i) { ruby_case_match_clauses(this, i, result) }
/** Gets the node corresponding to the field `else`. */
Else getElse() { ruby_case_match_else(this, result) }
/** Gets the node corresponding to the field `value`. */
UnderscoreStatement getValue() { ruby_case_match_def(this, result, _) }
/** Gets a field or child node of this node. */
override AstNode getAFieldOrChild() {
ruby_case_match_clauses(this, _, result) or
ruby_case_match_else(this, result) or
ruby_case_match_def(this, result, _)
}
}
/** A class representing `chained_string` nodes. */
class ChainedString extends @ruby_chained_string, AstNode {
/** Gets the name of the primary QL class for this element. */
@@ -638,6 +734,12 @@ module Ruby {
override string getAPrimaryQlClass() { result = "EmptyStatement" }
}
/** A class representing `encoding` tokens. */
class Encoding extends @ruby_token_encoding, Token {
/** Gets the name of the primary QL class for this element. */
override string getAPrimaryQlClass() { result = "Encoding" }
}
/** A class representing `end_block` nodes. */
class EndBlock extends @ruby_end_block, AstNode {
/** Gets the name of the primary QL class for this element. */
@@ -710,6 +812,32 @@ module Ruby {
override string getAPrimaryQlClass() { result = "False" }
}
/** A class representing `file` tokens. */
class File extends @ruby_token_file, Token {
/** Gets the name of the primary QL class for this element. */
override string getAPrimaryQlClass() { result = "File" }
}
/** A class representing `find_pattern` nodes. */
class FindPattern extends @ruby_find_pattern, AstNode {
/** Gets the name of the primary QL class for this element. */
override string getAPrimaryQlClass() { result = "FindPattern" }
/** Gets the location of this element. */
override L::Location getLocation() { ruby_find_pattern_def(this, result) }
/** Gets the node corresponding to the field `class`. */
UnderscorePatternConstant getClass() { ruby_find_pattern_class(this, result) }
/** Gets the `i`th child of this node. */
AstNode getChild(int i) { ruby_find_pattern_child(this, i, result) }
/** Gets a field or child node of this node. */
override AstNode getAFieldOrChild() {
ruby_find_pattern_class(this, result) or ruby_find_pattern_child(this, _, result)
}
}
/** A class representing `float` tokens. */
class Float extends @ruby_token_float, Token {
/** Gets the name of the primary QL class for this element. */
@@ -780,6 +908,26 @@ module Ruby {
override string getAPrimaryQlClass() { result = "HashKeySymbol" }
}
/** A class representing `hash_pattern` nodes. */
class HashPattern extends @ruby_hash_pattern, AstNode {
/** Gets the name of the primary QL class for this element. */
override string getAPrimaryQlClass() { result = "HashPattern" }
/** Gets the location of this element. */
override L::Location getLocation() { ruby_hash_pattern_def(this, result) }
/** Gets the node corresponding to the field `class`. */
UnderscorePatternConstant getClass() { ruby_hash_pattern_class(this, result) }
/** Gets the `i`th child of this node. */
AstNode getChild(int i) { ruby_hash_pattern_child(this, i, result) }
/** Gets a field or child node of this node. */
override AstNode getAFieldOrChild() {
ruby_hash_pattern_class(this, result) or ruby_hash_pattern_child(this, _, result)
}
}
/** A class representing `hash_splat_argument` nodes. */
class HashSplatArgument extends @ruby_hash_splat_argument, AstNode {
/** Gets the name of the primary QL class for this element. */
@@ -795,6 +943,12 @@ module Ruby {
override AstNode getAFieldOrChild() { ruby_hash_splat_argument_def(this, result, _) }
}
/** A class representing `hash_splat_nil` tokens. */
class HashSplatNil extends @ruby_token_hash_splat_nil, Token {
/** Gets the name of the primary QL class for this element. */
override string getAPrimaryQlClass() { result = "HashSplatNil" }
}
/** A class representing `hash_splat_parameter` nodes. */
class HashSplatParameter extends @ruby_hash_splat_parameter, AstNode {
/** Gets the name of the primary QL class for this element. */
@@ -874,6 +1028,21 @@ module Ruby {
}
}
/** A class representing `if_guard` nodes. */
class IfGuard extends @ruby_if_guard, AstNode {
/** Gets the name of the primary QL class for this element. */
override string getAPrimaryQlClass() { result = "IfGuard" }
/** Gets the location of this element. */
override L::Location getLocation() { ruby_if_guard_def(this, _, result) }
/** Gets the node corresponding to the field `condition`. */
UnderscoreExpression getCondition() { ruby_if_guard_def(this, result, _) }
/** Gets a field or child node of this node. */
override AstNode getAFieldOrChild() { ruby_if_guard_def(this, result, _) }
}
/** A class representing `if_modifier` nodes. */
class IfModifier extends @ruby_if_modifier, AstNode {
/** Gets the name of the primary QL class for this element. */
@@ -886,7 +1055,7 @@ module Ruby {
UnderscoreStatement getBody() { ruby_if_modifier_def(this, result, _, _) }
/** Gets the node corresponding to the field `condition`. */
AstNode getCondition() { ruby_if_modifier_def(this, _, result, _) }
UnderscoreExpression getCondition() { ruby_if_modifier_def(this, _, result, _) }
/** Gets a field or child node of this node. */
override AstNode getAFieldOrChild() {
@@ -909,6 +1078,31 @@ module Ruby {
override AstNode getAFieldOrChild() { ruby_in_def(this, result, _) }
}
/** A class representing `in_clause` nodes. */
class InClause extends @ruby_in_clause, AstNode {
/** Gets the name of the primary QL class for this element. */
override string getAPrimaryQlClass() { result = "InClause" }
/** Gets the location of this element. */
override L::Location getLocation() { ruby_in_clause_def(this, _, result) }
/** Gets the node corresponding to the field `body`. */
Then getBody() { ruby_in_clause_body(this, result) }
/** Gets the node corresponding to the field `guard`. */
AstNode getGuard() { ruby_in_clause_guard(this, result) }
/** Gets the node corresponding to the field `pattern`. */
UnderscorePatternTopExprBody getPattern() { ruby_in_clause_def(this, result, _) }
/** Gets a field or child node of this node. */
override AstNode getAFieldOrChild() {
ruby_in_clause_body(this, result) or
ruby_in_clause_guard(this, result) or
ruby_in_clause_def(this, result, _)
}
}
/** A class representing `instance_variable` tokens. */
class InstanceVariable extends @ruby_token_instance_variable, Token {
/** Gets the name of the primary QL class for this element. */
@@ -956,6 +1150,26 @@ module Ruby {
}
}
/** A class representing `keyword_pattern` nodes. */
class KeywordPattern extends @ruby_keyword_pattern, AstNode {
/** Gets the name of the primary QL class for this element. */
override string getAPrimaryQlClass() { result = "KeywordPattern" }
/** Gets the location of this element. */
override L::Location getLocation() { ruby_keyword_pattern_def(this, _, result) }
/** Gets the node corresponding to the field `key`. */
AstNode getKey() { ruby_keyword_pattern_def(this, result, _) }
/** Gets the node corresponding to the field `value`. */
UnderscorePatternExpr getValue() { ruby_keyword_pattern_value(this, result) }
/** Gets a field or child node of this node. */
override AstNode getAFieldOrChild() {
ruby_keyword_pattern_def(this, result, _) or ruby_keyword_pattern_value(this, result)
}
}
/** A class representing `lambda` nodes. */
class Lambda extends @ruby_lambda, AstNode {
/** Gets the name of the primary QL class for this element. */
@@ -1006,6 +1220,12 @@ module Ruby {
override AstNode getAFieldOrChild() { ruby_left_assignment_list_child(this, _, result) }
}
/** A class representing `line` tokens. */
class Line extends @ruby_token_line, Token {
/** Gets the name of the primary QL class for this element. */
override string getAPrimaryQlClass() { result = "Line" }
}
/** A class representing `method` nodes. */
class Method extends @ruby_method, AstNode {
/** Gets the name of the primary QL class for this element. */
@@ -1136,7 +1356,7 @@ module Ruby {
}
/** Gets the node corresponding to the field `right`. */
AstNode getRight() { ruby_operator_assignment_def(this, _, _, result, _) }
UnderscoreExpression getRight() { ruby_operator_assignment_def(this, _, _, result, _) }
/** Gets a field or child node of this node. */
override AstNode getAFieldOrChild() {
@@ -1240,10 +1460,10 @@ module Ruby {
override L::Location getLocation() { ruby_range_def(this, _, result) }
/** Gets the node corresponding to the field `begin`. */
UnderscoreArg getBegin() { ruby_range_begin(this, result) }
AstNode getBegin() { ruby_range_begin(this, result) }
/** Gets the node corresponding to the field `end`. */
UnderscoreArg getEnd() { ruby_range_end(this, result) }
AstNode getEnd() { ruby_range_end(this, result) }
/** Gets the node corresponding to the field `operator`. */
string getOperator() {
@@ -1339,10 +1559,10 @@ module Ruby {
override L::Location getLocation() { ruby_rescue_modifier_def(this, _, _, result) }
/** Gets the node corresponding to the field `body`. */
UnderscoreStatement getBody() { ruby_rescue_modifier_def(this, result, _, _) }
AstNode getBody() { ruby_rescue_modifier_def(this, result, _, _) }
/** Gets the node corresponding to the field `handler`. */
AstNode getHandler() { ruby_rescue_modifier_def(this, _, result, _) }
UnderscoreExpression getHandler() { ruby_rescue_modifier_def(this, _, result, _) }
/** Gets a field or child node of this node. */
override AstNode getAFieldOrChild() {
@@ -1422,7 +1642,7 @@ module Ruby {
AstNode getName() { ruby_scope_resolution_def(this, result, _) }
/** Gets the node corresponding to the field `scope`. */
UnderscorePrimary getScope() { ruby_scope_resolution_scope(this, result) }
AstNode getScope() { ruby_scope_resolution_scope(this, result) }
/** Gets a field or child node of this node. */
override AstNode getAFieldOrChild() {
@@ -1602,7 +1822,7 @@ module Ruby {
override L::Location getLocation() { ruby_superclass_def(this, _, result) }
/** Gets the child of this node. */
AstNode getChild() { ruby_superclass_def(this, result, _) }
UnderscoreExpression getChild() { ruby_superclass_def(this, result, _) }
/** Gets a field or child node of this node. */
override AstNode getAFieldOrChild() { ruby_superclass_def(this, result, _) }
@@ -1722,6 +1942,21 @@ module Ruby {
}
}
/** A class representing `unless_guard` nodes. */
class UnlessGuard extends @ruby_unless_guard, AstNode {
/** Gets the name of the primary QL class for this element. */
override string getAPrimaryQlClass() { result = "UnlessGuard" }
/** Gets the location of this element. */
override L::Location getLocation() { ruby_unless_guard_def(this, _, result) }
/** Gets the node corresponding to the field `condition`. */
UnderscoreExpression getCondition() { ruby_unless_guard_def(this, result, _) }
/** Gets a field or child node of this node. */
override AstNode getAFieldOrChild() { ruby_unless_guard_def(this, result, _) }
}
/** A class representing `unless_modifier` nodes. */
class UnlessModifier extends @ruby_unless_modifier, AstNode {
/** Gets the name of the primary QL class for this element. */
@@ -1734,7 +1969,7 @@ module Ruby {
UnderscoreStatement getBody() { ruby_unless_modifier_def(this, result, _, _) }
/** Gets the node corresponding to the field `condition`. */
AstNode getCondition() { ruby_unless_modifier_def(this, _, result, _) }
UnderscoreExpression getCondition() { ruby_unless_modifier_def(this, _, result, _) }
/** Gets a field or child node of this node. */
override AstNode getAFieldOrChild() {
@@ -1774,7 +2009,7 @@ module Ruby {
UnderscoreStatement getBody() { ruby_until_modifier_def(this, result, _, _) }
/** Gets the node corresponding to the field `condition`. */
AstNode getCondition() { ruby_until_modifier_def(this, _, result, _) }
UnderscoreExpression getCondition() { ruby_until_modifier_def(this, _, result, _) }
/** Gets a field or child node of this node. */
override AstNode getAFieldOrChild() {
@@ -1782,6 +2017,21 @@ module Ruby {
}
}
/** A class representing `variable_reference_pattern` nodes. */
class VariableReferencePattern extends @ruby_variable_reference_pattern, AstNode {
/** Gets the name of the primary QL class for this element. */
override string getAPrimaryQlClass() { result = "VariableReferencePattern" }
/** Gets the location of this element. */
override L::Location getLocation() { ruby_variable_reference_pattern_def(this, _, result) }
/** Gets the node corresponding to the field `name`. */
Identifier getName() { ruby_variable_reference_pattern_def(this, result, _) }
/** Gets a field or child node of this node. */
override AstNode getAFieldOrChild() { ruby_variable_reference_pattern_def(this, result, _) }
}
/** A class representing `when` nodes. */
class When extends @ruby_when, AstNode {
/** Gets the name of the primary QL class for this element. */
@@ -1834,7 +2084,7 @@ module Ruby {
UnderscoreStatement getBody() { ruby_while_modifier_def(this, result, _, _) }
/** Gets the node corresponding to the field `condition`. */
AstNode getCondition() { ruby_while_modifier_def(this, _, result, _) }
UnderscoreExpression getCondition() { ruby_while_modifier_def(this, _, result, _) }
/** Gets a field or child node of this node. */
override AstNode getAFieldOrChild() {

View File

@@ -3,6 +3,7 @@ private import codeql.Locations
private import codeql.ruby.AST
private import codeql.ruby.ast.internal.AST
private import codeql.ruby.ast.internal.Parameter
private import codeql.ruby.ast.internal.Pattern
private import codeql.ruby.ast.internal.Scope
private import codeql.ruby.ast.internal.Synthesis
@@ -28,6 +29,16 @@ predicate explicitAssignmentNode(Ruby::AstNode n, Ruby::AstNode assignment) {
/** Holds if `n` is inside an implicit assignment. */
predicate implicitAssignmentNode(Ruby::AstNode n) {
casePattern(n) and n instanceof Ruby::Identifier
or
n = any(Ruby::AsPattern p).getName()
or
n = any(Ruby::ArrayPattern parent).getChild(_).(Ruby::SplatParameter).getName()
or
n = any(Ruby::FindPattern parent).getChild(_).(Ruby::SplatParameter).getName()
or
n = any(Ruby::HashPattern parent).getChild(_).(Ruby::HashSplatParameter).getName()
or
n = any(Ruby::ExceptionVariable ev).getChild()
or
n = any(Ruby::For for).getPattern()
@@ -177,6 +188,8 @@ private module Cached {
or
i = any(Ruby::Case x).getValue()
or
i = any(Ruby::CaseMatch x).getValue()
or
i = any(Ruby::Class x).getChild(_)
or
i = any(Ruby::Conditional x).getCondition()
@@ -327,7 +340,7 @@ private module Cached {
cached
predicate isCapturedAccess(LocalVariableAccess access) {
toGenerated(access.getVariable().getDeclaringScope()) != scopeOf(toGenerated(access))
access.getVariable().getDeclaringScope() != access.getCfgScope()
}
cached
@@ -498,11 +511,10 @@ module LocalVariableAccess {
predicate range(Ruby::Identifier id, TLocalVariableReal v) {
access(id, v) and
(
explicitWriteAccess(id, _)
or
implicitWriteAccess(id)
or
vcall(id)
explicitWriteAccess(id, _) or
implicitWriteAccess(id) or
vcall(id) or
id = any(Ruby::VariableReferencePattern vr).getName()
)
}
}
@@ -524,7 +536,7 @@ private class LocalVariableAccessReal extends LocalVariableAccessImpl, TLocalVar
final override string toString() { result = g.getValue() }
}
private class LocalVariableAccessSynth extends LocalVariableAccessImpl, TLocalVariableAccessSynth {
class LocalVariableAccessSynth extends LocalVariableAccessImpl, TLocalVariableAccessSynth {
private LocalVariable v;
LocalVariableAccessSynth() { this = TLocalVariableAccessSynth(_, _, v) }

View File

@@ -320,7 +320,11 @@ module ExprNodes {
/** Gets the the keyword argument whose key is `keyword` of this call. */
final ExprCfgNode getKeywordArgument(string keyword) {
e.hasCfgChild(e.getKeywordArgument(keyword), this, result)
exists(PairCfgNode n |
e.hasCfgChild(e.getAnArgument(), this, n) and
n.getKey().getExpr().(SymbolLiteral).getValueText() = keyword and
result = n.getValue()
)
}
/** Gets the number of arguments of this call. */
@@ -426,6 +430,27 @@ module ExprNodes {
ParenthesizedExprCfgNode() { this.getExpr() instanceof ParenthesizedExpr }
}
private class PairChildMapping extends ExprChildMapping, Pair {
override predicate relevantChild(Expr e) { e = this.getKey() or e = this.getValue() }
}
/** A control-flow node that wraps a `Pair` AST expression. */
class PairCfgNode extends ExprCfgNode {
override PairChildMapping e;
final override Pair getExpr() { result = ExprCfgNode.super.getExpr() }
/**
* Gets the key expression of this pair.
*/
final ExprCfgNode getKey() { e.hasCfgChild(e.getKey(), this, result) }
/**
* Gets the value expression of this pair.
*/
final ExprCfgNode getValue() { e.hasCfgChild(e.getValue(), this, result) }
}
/** A control-flow node that wraps a `VariableReadAccess` AST expression. */
class VariableReadAccessCfgNode extends ExprCfgNode {
override VariableReadAccess e;

View File

@@ -27,7 +27,7 @@ class CfgScope extends Scope instanceof CfgScope::Range_ {
*
* Only nodes that can be reached from an entry point are included in the CFG.
*/
class CfgNode extends TNode {
class CfgNode extends TCfgNode {
/** Gets a textual representation of this control flow node. */
string toString() { none() }

View File

@@ -14,7 +14,6 @@ private import SuccessorTypes
private newtype TCompletion =
TSimpleCompletion() or
TBooleanCompletion(boolean b) { b in [false, true] } or
TEmptinessCompletion(boolean isEmpty) { isEmpty in [false, true] } or
TMatchingCompletion(boolean isMatch) { isMatch in [false, true] } or
TReturnCompletion() or
TBreakCompletion() or
@@ -54,9 +53,6 @@ private predicate nestedEnsureCompletion(Completion outer, int nestLevel) {
pragma[noinline]
private predicate completionIsValidForStmt(AstNode n, Completion c) {
n = TForIn(_) and
c instanceof EmptinessCompletion
or
n instanceof BreakStmt and
c = TBreakCompletion()
or
@@ -196,7 +192,7 @@ private predicate inBooleanContext(AstNode n) {
or
exists(CaseExpr c, WhenExpr w |
not exists(c.getValue()) and
c.getAWhenBranch() = w and
c.getABranch() = w and
w.getPattern(_) = n
)
}
@@ -218,7 +214,7 @@ private predicate inMatchingContext(AstNode n) {
or
exists(CaseExpr c, WhenExpr w |
exists(c.getValue()) and
c.getAWhenBranch() = w and
c.getABranch() = w and
w.getPattern(_) = n
)
or
@@ -242,8 +238,8 @@ class SimpleCompletion extends NonNestedNormalCompletion, TSimpleCompletion {
/**
* A completion that represents evaluation of an expression, whose value determines
* the successor. Either a Boolean completion (`BooleanCompletion`), an emptiness
* completion (`EmptinessCompletion`), or a matching completion (`MatchingCompletion`).
* the successor. Either a Boolean completion (`BooleanCompletion`), or a matching
* completion (`MatchingCompletion`).
*/
abstract class ConditionalCompletion extends NonNestedNormalCompletion {
boolean value;
@@ -280,18 +276,6 @@ class FalseCompletion extends BooleanCompletion {
FalseCompletion() { this.getValue() = false }
}
/**
* A completion that represents evaluation of an emptiness test, for example
* a test in a `for in` statement.
*/
class EmptinessCompletion extends ConditionalCompletion, TEmptinessCompletion {
EmptinessCompletion() { this = TEmptinessCompletion(value) }
override EmptinessSuccessor getAMatchingSuccessorType() { result.getValue() = value }
override string toString() { if value = true then result = "empty" else result = "non-empty" }
}
/**
* A completion that represents evaluation of a matching test, for example
* a test in a `rescue` statement.

View File

@@ -61,6 +61,9 @@ module CfgScope {
final override predicate exit(AstNode last, Completion c) {
last(this.(Trees::EndBlockTree).getLastBodyChild(), last, c)
or
last(this.(Trees::EndBlockTree).getBodyChild(_, _), last, c) and
not c instanceof NormalCompletion
}
}
@@ -79,6 +82,9 @@ module CfgScope {
final override predicate exit(AstNode last, Completion c) {
last(this.(Trees::BraceBlockTree).getLastBodyChild(), last, c)
or
last(this.(Trees::BraceBlockTree).getBodyChild(_, _), last, c) and
not c instanceof NormalCompletion
}
}
}
@@ -91,25 +97,6 @@ predicate succEntry(CfgScope::Range_ scope, AstNode first) { scope.entry(first)
pragma[nomagic]
predicate succExit(CfgScope::Range_ scope, AstNode last, Completion c) { scope.exit(last, c) }
// TODO: remove this class; it should be replaced with an implicit non AST node
private class ForIn extends AstNode, ASTInternal::TForIn {
final override string toString() { result = "In" }
}
// TODO: remove this class; it should be replaced with an implicit non AST node
private class ForRange extends ForExpr {
override AstNode getAChild(string pred) {
result = super.getAChild(pred)
or
pred = "<in>" and
result = this.getIn()
}
ForIn getIn() {
result = ASTInternal::TForIn(ASTInternal::toGenerated(this).(Ruby::For).getValue())
}
}
/** Defines the CFG by dispatch on the various AST types. */
module Trees {
private class AliasStmtTree extends StandardPreOrderTree, AliasStmt {
@@ -391,7 +378,7 @@ module Trees {
override ControlFlowTree getChildElement(int i) { result = this.getArgument(i) }
}
private class CaseTree extends PreOrderTree, CaseExpr {
private class CaseTree extends PreOrderTree, CaseExpr, ASTInternal::TCaseExpr {
final override predicate propagatesAbnormal(AstNode child) {
child = this.getValue() or child = this.getABranch()
}
@@ -399,7 +386,7 @@ module Trees {
final override predicate last(AstNode last, Completion c) {
last(this.getValue(), last, c) and not exists(this.getABranch())
or
last(this.getAWhenBranch().getBody(), last, c)
last(this.getABranch().(WhenExpr).getBody(), last, c)
or
exists(int i, ControlFlowTree lastBranch |
lastBranch = this.getBranch(i) and
@@ -610,95 +597,14 @@ module Trees {
private class ForwardParameterTree extends LeafTree, ForwardParameter { }
private class ForInTree extends LeafTree, ForIn { }
/**
* Control flow of a for-in loop
*
* For example, this program fragment:
*
* ```rb
* for arg in args do
* puts arg
* end
* puts "done";
* ```
*
* has the following control flow graph:
*
* ```
* args
* |
* in------<-----
* / \ \
* / \ |
* / \ |
* / \ |
* empty non-empty |
* | \ |
* for \ |
* | arg |
* | | |
* puts "done" puts arg |
* \___/
* ```
*/
private class ForTree extends PostOrderTree, ForRange {
final override predicate propagatesAbnormal(AstNode child) {
child = this.getPattern() or child = this.getValue()
}
final override predicate first(AstNode first) { first(this.getValue(), first) }
/**
* for pattern in array do body end
* ```
* array +-> in +--[non empty]--> pattern -> body -> in
* |--[empty]--> for
* ```
*/
final override predicate succ(AstNode pred, AstNode succ, Completion c) {
last(this.getValue(), pred, c) and
first(this.getIn(), succ) and
c instanceof SimpleCompletion
or
last(this.getIn(), pred, c) and
first(this.getPattern(), succ) and
c.(EmptinessCompletion).getValue() = false
or
last(this.getPattern(), pred, c) and
first(this.getBody(), succ) and
c instanceof NormalCompletion
or
last(this.getBody(), pred, c) and
first(this.getIn(), succ) and
c.continuesLoop()
or
last(this.getBody(), pred, c) and
first(this.getBody(), succ) and
c instanceof RedoCompletion
or
succ = this and
(
last(this.getIn(), pred, c) and
c.(EmptinessCompletion).getValue() = true
or
last(this.getBody(), pred, c) and
not c.continuesLoop() and
not c instanceof BreakCompletion and
not c instanceof RedoCompletion
or
last(this.getBody(), pred, c.(NestedBreakCompletion).getAnInnerCompatibleCompletion())
)
}
}
private class GlobalVariableTree extends LeafTree, GlobalVariableAccess { }
private class HashLiteralTree extends StandardPostOrderTree, HashLiteral {
final override ControlFlowTree getChildElement(int i) { result = this.getElement(i) }
}
private class HashSplatNilParameterTree extends LeafTree, HashSplatNilParameter { }
private class HashSplatParameterTree extends NonDefaultValueParameterTree, HashSplatParameter { }
private class HereDocTree extends StandardPreOrderTree, HereDoc {
@@ -1137,6 +1043,9 @@ private Scope parent(Scope n) {
not n instanceof CfgScope::Range_
}
cached
private CfgScope getCfgScopeImpl(AstNode n) { result = parent*(scopeOfInclSynth(n)) }
/** Gets the CFG scope of node `n`. */
pragma[inline]
CfgScope getCfgScope(AstNode n) {
@@ -1148,13 +1057,6 @@ CfgScope getCfgScope(AstNode n) {
cached
private module Cached {
/** Gets the CFG scope of node `n`. */
cached
CfgScope getCfgScopeImpl(AstNode n) {
forceCachingInSameStage() and
result = parent*(ASTInternal::fromGenerated(scopeOf(ASTInternal::toGeneratedInclSynth(n))))
}
cached
newtype TSuccessorType =
TSuccessorSuccessor() or

View File

@@ -788,7 +788,7 @@ private module Cached {
* The control flow graph is pruned for unreachable nodes.
*/
cached
newtype TNode =
newtype TCfgNode =
TEntryNode(CfgScope scope) { succEntrySplits(scope, _, _, _) } or
TAnnotatedExitNode(CfgScope scope, boolean normal) {
exists(Reachability::SameSplitsBlock b, SuccessorType t | b.isReachable(_) |
@@ -807,7 +807,7 @@ private module Cached {
/** Gets a successor node of a given flow type, if any. */
cached
TNode getASuccessor(TNode pred, SuccessorType t) {
TCfgNode getASuccessor(TCfgNode pred, SuccessorType t) {
// Callable entry node -> callable body
exists(ControlFlowElement succElement, Splits succSplits, CfgScope scope |
result = TElementNode(succElement, succSplits) and
@@ -943,4 +943,9 @@ module Consistency {
strictcount(getASuccessor(node, t)) > 1 and
successor = getASuccessor(node, t)
}
query predicate deadEnd(Node node) {
not node instanceof TExitNode and
not exists(getASuccessor(node, _))
}
}

View File

@@ -139,6 +139,18 @@ module EnsureSplitting {
/** Holds if this node is the entry node in the `ensure` block it belongs to. */
predicate isEntryNode() { first(block.getEnsure(), this) }
BodyStmt getBlock() { result = block }
pragma[noinline]
predicate isEntered(AstNode pred, int nestLevel, Completion c) {
this.isEntryNode() and
nestLevel = this.getNestLevel() and
succ(pred, this, c) and
// the entry node may be reachable via a backwards loop edge; in this case
// the split has already been entered
not pred = block.getAnEnsureDescendant()
}
}
/**
@@ -205,18 +217,11 @@ module EnsureSplitting {
override string toString() { result = "ensure (" + nestLevel + ")" }
}
pragma[noinline]
private predicate hasEntry0(AstNode pred, EnsureNode succ, int nestLevel, Completion c) {
succ.isEntryNode() and
nestLevel = succ.getNestLevel() and
succ(pred, succ, c)
}
private class EnsureSplitImpl extends SplitImpl, EnsureSplit {
override EnsureSplitKind getKind() { result.getNestLevel() = this.getNestLevel() }
override predicate hasEntry(AstNode pred, AstNode succ, Completion c) {
hasEntry0(pred, succ, this.getNestLevel(), c) and
succ.(EnsureNode).isEntered(pred, this.getNestLevel(), c) and
this.getType().isSplitForEntryCompletion(c)
}
@@ -242,8 +247,8 @@ module EnsureSplitting {
* `inherited` indicates whether `c` is an inherited completion from the
* body.
*/
private predicate exit(Trees::BodyStmtTree block, AstNode pred, Completion c, boolean inherited) {
exists(EnsureSplitType type |
private predicate exit(AstNode pred, Completion c, boolean inherited) {
exists(Trees::BodyStmtTree block, EnsureSplitType type |
this.exit0(pred, block, this.getNestLevel(), c) and
type = this.getType()
|
@@ -294,7 +299,7 @@ module EnsureSplitting {
this.appliesToPredecessor(pred) and
exists(EnsureSplitImpl outer |
outer.getNestLevel() = this.getNestLevel() - 1 and
outer.exit(_, pred, c, inherited) and
outer.exit(pred, c, inherited) and
this.getType() instanceof NormalSuccessor and
inherited = true
)
@@ -303,18 +308,18 @@ module EnsureSplitting {
override predicate hasExit(AstNode pred, AstNode succ, Completion c) {
succ(pred, succ, c) and
(
this.exit(_, pred, c, _)
this.exit(pred, c, _)
or
this.exit(_, pred, c.(NestedBreakCompletion).getAnInnerCompatibleCompletion(), _)
this.exit(pred, c.(NestedBreakCompletion).getAnInnerCompatibleCompletion(), _)
)
}
override predicate hasExitScope(CfgScope scope, AstNode last, Completion c) {
succExit(scope, last, c) and
(
this.exit(_, last, c, _)
this.exit(last, c, _)
or
this.exit(_, last, c.(NestedBreakCompletion).getAnInnerCompatibleCompletion(), _)
this.exit(last, c.(NestedBreakCompletion).getAnInnerCompatibleCompletion(), _)
)
}
@@ -323,7 +328,7 @@ module EnsureSplitting {
succ(pred, succ, c) and
succ =
any(EnsureNode en |
if en.isEntryNode()
if en.isEntryNode() and en.getBlock() != pred.(EnsureNode).getBlock()
then
// entering a nested `ensure` block
en.getNestLevel() > this.getNestLevel()

View File

@@ -0,0 +1,16 @@
/** Provides commonly used dataflow sanitizers */
private import codeql.ruby.AST
private import codeql.ruby.DataFlow
/**
* A sanitizer for flow into a string interpolation component,
* provided that component does not form a prefix of the string.
*
* This is useful for URLs and paths, where the fixed prefix prevents the user from controlling the target.
*/
class PrefixedStringInterpolation extends DataFlow::Node {
PrefixedStringInterpolation() {
exists(StringlikeLiteral str, int n | str.getComponent(n) = this.asExpr().getExpr() and n > 0)
}
}

View File

@@ -289,6 +289,7 @@ private predicate outBarrier(NodeEx node, Configuration config) {
)
}
pragma[nomagic]
private predicate fullBarrier(NodeEx node, Configuration config) {
exists(Node n | node.asNode() = n |
config.isBarrier(n)
@@ -307,11 +308,23 @@ private predicate fullBarrier(NodeEx node, Configuration config) {
}
pragma[nomagic]
private predicate sourceNode(NodeEx node, Configuration config) { config.isSource(node.asNode()) }
private predicate sourceNode(NodeEx node, Configuration config) {
config.isSource(node.asNode()) and
not fullBarrier(node, config)
}
pragma[nomagic]
private predicate sinkNode(NodeEx node, Configuration config) { config.isSink(node.asNode()) }
/** Provides the relevant barriers for a step from `node1` to `node2`. */
pragma[inline]
private predicate stepFilter(NodeEx node1, NodeEx node2, Configuration config) {
not outBarrier(node1, config) and
not inBarrier(node2, config) and
not fullBarrier(node1, config) and
not fullBarrier(node2, config)
}
/**
* Holds if data can flow in one local step from `node1` to `node2`.
*/
@@ -320,16 +333,14 @@ private predicate localFlowStep(NodeEx node1, NodeEx node2, Configuration config
node1.asNode() = n1 and
node2.asNode() = n2 and
simpleLocalFlowStepExt(n1, n2) and
not outBarrier(node1, config) and
not inBarrier(node2, config) and
not fullBarrier(node1, config) and
not fullBarrier(node2, config)
stepFilter(node1, node2, config)
)
or
exists(Node n |
config.allowImplicitRead(n, _) and
node1.asNode() = n and
node2.isImplicitReadNode(n, false)
node2.isImplicitReadNode(n, false) and
not fullBarrier(node1, config)
)
}
@@ -342,16 +353,14 @@ private predicate additionalLocalFlowStep(NodeEx node1, NodeEx node2, Configurat
node2.asNode() = n2 and
config.isAdditionalFlowStep(n1, n2) and
getNodeEnclosingCallable(n1) = getNodeEnclosingCallable(n2) and
not outBarrier(node1, config) and
not inBarrier(node2, config) and
not fullBarrier(node1, config) and
not fullBarrier(node2, config)
stepFilter(node1, node2, config)
)
or
exists(Node n |
config.allowImplicitRead(n, _) and
node1.isImplicitReadNode(n, true) and
node2.asNode() = n
node2.asNode() = n and
not fullBarrier(node2, config)
)
}
@@ -363,10 +372,7 @@ private predicate jumpStep(NodeEx node1, NodeEx node2, Configuration config) {
node1.asNode() = n1 and
node2.asNode() = n2 and
jumpStepCached(n1, n2) and
not outBarrier(node1, config) and
not inBarrier(node2, config) and
not fullBarrier(node1, config) and
not fullBarrier(node2, config) and
stepFilter(node1, node2, config) and
not config.getAFeature() instanceof FeatureEqualSourceSinkCallContext
)
}
@@ -380,16 +386,14 @@ private predicate additionalJumpStep(NodeEx node1, NodeEx node2, Configuration c
node2.asNode() = n2 and
config.isAdditionalFlowStep(n1, n2) and
getNodeEnclosingCallable(n1) != getNodeEnclosingCallable(n2) and
not outBarrier(node1, config) and
not inBarrier(node2, config) and
not fullBarrier(node1, config) and
not fullBarrier(node2, config) and
stepFilter(node1, node2, config) and
not config.getAFeature() instanceof FeatureEqualSourceSinkCallContext
)
}
private predicate read(NodeEx node1, Content c, NodeEx node2, Configuration config) {
read(node1.asNode(), c, node2.asNode())
read(node1.asNode(), c, node2.asNode()) and
stepFilter(node1, node2, config)
or
exists(Node n |
node2.isImplicitReadNode(n, true) and
@@ -402,7 +406,8 @@ private predicate store(
NodeEx node1, TypedContent tc, NodeEx node2, DataFlowType contentType, Configuration config
) {
store(node1.asNode(), tc, node2.asNode(), contentType) and
read(_, tc.getContent(), _, config)
read(_, tc.getContent(), _, config) and
stepFilter(node1, node2, config)
}
pragma[nomagic]
@@ -451,63 +456,59 @@ private module Stage1 {
* argument in a call.
*/
predicate fwdFlow(NodeEx node, Cc cc, Configuration config) {
not fullBarrier(node, config) and
(
sourceNode(node, config) and
if hasSourceCallCtx(config) then cc = true else cc = false
sourceNode(node, config) and
if hasSourceCallCtx(config) then cc = true else cc = false
or
exists(NodeEx mid |
fwdFlow(mid, cc, config) and
localFlowStep(mid, node, config)
)
or
exists(NodeEx mid |
fwdFlow(mid, cc, config) and
additionalLocalFlowStep(mid, node, config)
)
or
exists(NodeEx mid |
fwdFlow(mid, _, config) and
jumpStep(mid, node, config) and
cc = false
)
or
exists(NodeEx mid |
fwdFlow(mid, _, config) and
additionalJumpStep(mid, node, config) and
cc = false
)
or
// store
exists(NodeEx mid |
useFieldFlow(config) and
fwdFlow(mid, cc, config) and
store(mid, _, node, _, config)
)
or
// read
exists(Content c |
fwdFlowRead(c, node, cc, config) and
fwdFlowConsCand(c, config)
)
or
// flow into a callable
exists(NodeEx arg |
fwdFlow(arg, _, config) and
viableParamArgEx(_, node, arg) and
cc = true and
not fullBarrier(node, config)
)
or
// flow out of a callable
exists(DataFlowCall call |
fwdFlowOut(call, node, false, config) and
cc = false
or
exists(NodeEx mid |
fwdFlow(mid, cc, config) and
localFlowStep(mid, node, config)
)
or
exists(NodeEx mid |
fwdFlow(mid, cc, config) and
additionalLocalFlowStep(mid, node, config)
)
or
exists(NodeEx mid |
fwdFlow(mid, _, config) and
jumpStep(mid, node, config) and
cc = false
)
or
exists(NodeEx mid |
fwdFlow(mid, _, config) and
additionalJumpStep(mid, node, config) and
cc = false
)
or
// store
exists(NodeEx mid |
useFieldFlow(config) and
fwdFlow(mid, cc, config) and
store(mid, _, node, _, config) and
not outBarrier(mid, config)
)
or
// read
exists(Content c |
fwdFlowRead(c, node, cc, config) and
fwdFlowConsCand(c, config) and
not inBarrier(node, config)
)
or
// flow into a callable
exists(NodeEx arg |
fwdFlow(arg, _, config) and
viableParamArgEx(_, node, arg) and
cc = true
)
or
// flow out of a callable
exists(DataFlowCall call |
fwdFlowOut(call, node, false, config) and
cc = false
or
fwdFlowOutFromArg(call, node, config) and
fwdFlowIsEntered(call, cc, config)
)
fwdFlowOutFromArg(call, node, config) and
fwdFlowIsEntered(call, cc, config)
)
}
@@ -547,7 +548,8 @@ private module Stage1 {
private predicate fwdFlowOut(DataFlowCall call, NodeEx out, Cc cc, Configuration config) {
exists(ReturnPosition pos |
fwdFlowReturnPosition(pos, cc, config) and
viableReturnPosOutEx(call, pos, out)
viableReturnPosOutEx(call, pos, out) and
not fullBarrier(out, config)
)
}
@@ -773,6 +775,7 @@ private module Stage1 {
* Holds if flow may enter through `p` and reach a return node making `p` a
* candidate for the origin of a summary.
*/
pragma[nomagic]
predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) {
exists(ReturnKindExt kind |
throughFlowNodeCand(p, config) and
@@ -1009,6 +1012,9 @@ private module Stage2 {
private predicate flowIntoCall = flowIntoCallNodeCand1/5;
bindingset[node, ap]
private predicate filter(NodeEx node, Ap ap) { any() }
bindingset[ap, contentType]
private predicate typecheckStore(Ap ap, DataFlowType contentType) { any() }
@@ -1017,14 +1023,23 @@ private module Stage2 {
PrevStage::revFlow(node, _, _, apa, config)
}
bindingset[result, apa]
private ApApprox unbindApa(ApApprox apa) {
exists(ApApprox apa0 |
apa = pragma[only_bind_into](apa0) and result = pragma[only_bind_into](apa0)
)
}
pragma[nomagic]
private predicate flowThroughOutOfCall(
DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config
DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow,
Configuration config
) {
flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and
PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and
PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _,
pragma[only_bind_into](config))
pragma[only_bind_into](config)) and
ccc.matchesCall(call)
}
/**
@@ -1037,6 +1052,13 @@ private module Stage2 {
*/
pragma[nomagic]
predicate fwdFlow(NodeEx node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
fwdFlow0(node, cc, argAp, ap, config) and
flowCand(node, unbindApa(getApprox(ap)), config) and
filter(node, ap)
}
pragma[nomagic]
private predicate fwdFlow0(NodeEx node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
flowCand(node, _, config) and
sourceNode(node, config) and
(if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
@@ -1107,7 +1129,7 @@ private module Stage2 {
) {
exists(DataFlowType contentType |
fwdFlow(node1, cc, argAp, ap1, config) and
PrevStage::storeStepCand(node1, getApprox(ap1), tc, node2, contentType, config) and
PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and
typecheckStore(ap1, contentType)
)
}
@@ -1142,9 +1164,8 @@ private module Stage2 {
exists(ArgNodeEx arg, boolean allowsFieldFlow |
fwdFlow(arg, outercc, argAp, ap, config) and
flowIntoCall(call, arg, p, allowsFieldFlow, config) and
innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc)
|
ap instanceof ApNil or allowsFieldFlow = true
innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
)
}
@@ -1159,9 +1180,8 @@ private module Stage2 {
fwdFlow(ret, innercc, argAp, ap, config) and
flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
inner = ret.getEnclosingCallable() and
ccOut = getCallContextReturn(inner, call, innercc)
|
ap instanceof ApNil or allowsFieldFlow = true
ccOut = getCallContextReturn(inner, call, innercc) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
)
}
@@ -1171,10 +1191,8 @@ private module Stage2 {
) {
exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc |
fwdFlow(ret, ccc, apSome(argAp), ap, config) and
flowThroughOutOfCall(call, ret, out, allowsFieldFlow, config) and
ccc.matchesCall(call)
|
ap instanceof ApNil or allowsFieldFlow = true
flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
)
}
@@ -1188,7 +1206,7 @@ private module Stage2 {
) {
exists(ParamNodeEx p |
fwdFlowIn(call, p, cc, _, argAp, ap, config) and
PrevStage::parameterMayFlowThrough(p, _, getApprox(ap), config)
PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config)
)
}
@@ -1230,6 +1248,11 @@ private module Stage2 {
callMayFlowThroughFwd(call, pragma[only_bind_into](config))
}
pragma[nomagic]
private predicate returnNodeMayFlowThrough(RetNodeEx ret, Ap ap, Configuration config) {
fwdFlow(ret, any(CcCall ccc), apSome(_), ap, config)
}
/**
* Holds if `node` with access path `ap` is part of a path from a source to a
* sink in the configuration `config`.
@@ -1306,7 +1329,7 @@ private module Stage2 {
// flow out of a callable
revFlowOut(_, node, _, _, ap, config) and
toReturn = true and
if fwdFlow(node, any(CcCall ccc), apSome(_), ap, config)
if returnNodeMayFlowThrough(node, ap, config)
then returnAp = apSome(ap)
else returnAp = apNone()
}
@@ -1341,9 +1364,8 @@ private module Stage2 {
) {
exists(NodeEx out, boolean allowsFieldFlow |
revFlow(out, toReturn, returnAp, ap, config) and
flowOutOfCall(call, ret, out, allowsFieldFlow, config)
|
ap instanceof ApNil or allowsFieldFlow = true
flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
)
}
@@ -1353,9 +1375,8 @@ private module Stage2 {
) {
exists(ParamNodeEx p, boolean allowsFieldFlow |
revFlow(p, false, returnAp, ap, config) and
flowIntoCall(_, arg, p, allowsFieldFlow, config)
|
ap instanceof ApNil or allowsFieldFlow = true
flowIntoCall(_, arg, p, allowsFieldFlow, config) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
)
}
@@ -1365,9 +1386,8 @@ private module Stage2 {
) {
exists(ParamNodeEx p, boolean allowsFieldFlow |
revFlow(p, true, apSome(returnAp), ap, config) and
flowThroughIntoCall(call, arg, p, allowsFieldFlow, config)
|
ap instanceof ApNil or allowsFieldFlow = true
flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
)
}
@@ -1707,12 +1727,14 @@ private module Stage3 {
pragma[nomagic]
private predicate flowThroughOutOfCall(
DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config
DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow,
Configuration config
) {
flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and
PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and
PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _,
pragma[only_bind_into](config))
pragma[only_bind_into](config)) and
ccc.matchesCall(call)
}
/**
@@ -1837,9 +1859,8 @@ private module Stage3 {
exists(ArgNodeEx arg, boolean allowsFieldFlow |
fwdFlow(arg, outercc, argAp, ap, config) and
flowIntoCall(call, arg, p, allowsFieldFlow, config) and
innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc)
|
ap instanceof ApNil or allowsFieldFlow = true
innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
)
}
@@ -1854,9 +1875,8 @@ private module Stage3 {
fwdFlow(ret, innercc, argAp, ap, config) and
flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
inner = ret.getEnclosingCallable() and
ccOut = getCallContextReturn(inner, call, innercc)
|
ap instanceof ApNil or allowsFieldFlow = true
ccOut = getCallContextReturn(inner, call, innercc) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
)
}
@@ -1866,10 +1886,8 @@ private module Stage3 {
) {
exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc |
fwdFlow(ret, ccc, apSome(argAp), ap, config) and
flowThroughOutOfCall(call, ret, out, allowsFieldFlow, config) and
ccc.matchesCall(call)
|
ap instanceof ApNil or allowsFieldFlow = true
flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
)
}
@@ -1925,6 +1943,11 @@ private module Stage3 {
callMayFlowThroughFwd(call, pragma[only_bind_into](config))
}
pragma[nomagic]
private predicate returnNodeMayFlowThrough(RetNodeEx ret, Ap ap, Configuration config) {
fwdFlow(ret, any(CcCall ccc), apSome(_), ap, config)
}
/**
* Holds if `node` with access path `ap` is part of a path from a source to a
* sink in the configuration `config`.
@@ -2001,7 +2024,7 @@ private module Stage3 {
// flow out of a callable
revFlowOut(_, node, _, _, ap, config) and
toReturn = true and
if fwdFlow(node, any(CcCall ccc), apSome(_), ap, config)
if returnNodeMayFlowThrough(node, ap, config)
then returnAp = apSome(ap)
else returnAp = apNone()
}
@@ -2036,9 +2059,8 @@ private module Stage3 {
) {
exists(NodeEx out, boolean allowsFieldFlow |
revFlow(out, toReturn, returnAp, ap, config) and
flowOutOfCall(call, ret, out, allowsFieldFlow, config)
|
ap instanceof ApNil or allowsFieldFlow = true
flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
)
}
@@ -2048,9 +2070,8 @@ private module Stage3 {
) {
exists(ParamNodeEx p, boolean allowsFieldFlow |
revFlow(p, false, returnAp, ap, config) and
flowIntoCall(_, arg, p, allowsFieldFlow, config)
|
ap instanceof ApNil or allowsFieldFlow = true
flowIntoCall(_, arg, p, allowsFieldFlow, config) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
)
}
@@ -2060,9 +2081,8 @@ private module Stage3 {
) {
exists(ParamNodeEx p, boolean allowsFieldFlow |
revFlow(p, true, apSome(returnAp), ap, config) and
flowThroughIntoCall(call, arg, p, allowsFieldFlow, config)
|
ap instanceof ApNil or allowsFieldFlow = true
flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
)
}
@@ -2473,12 +2493,14 @@ private module Stage4 {
pragma[nomagic]
private predicate flowThroughOutOfCall(
DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config
DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow,
Configuration config
) {
flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and
PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and
PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _,
pragma[only_bind_into](config))
pragma[only_bind_into](config)) and
ccc.matchesCall(call)
}
/**
@@ -2603,9 +2625,8 @@ private module Stage4 {
exists(ArgNodeEx arg, boolean allowsFieldFlow |
fwdFlow(arg, outercc, argAp, ap, config) and
flowIntoCall(call, arg, p, allowsFieldFlow, config) and
innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc)
|
ap instanceof ApNil or allowsFieldFlow = true
innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
)
}
@@ -2620,9 +2641,8 @@ private module Stage4 {
fwdFlow(ret, innercc, argAp, ap, config) and
flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
inner = ret.getEnclosingCallable() and
ccOut = getCallContextReturn(inner, call, innercc)
|
ap instanceof ApNil or allowsFieldFlow = true
ccOut = getCallContextReturn(inner, call, innercc) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
)
}
@@ -2632,10 +2652,8 @@ private module Stage4 {
) {
exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc |
fwdFlow(ret, ccc, apSome(argAp), ap, config) and
flowThroughOutOfCall(call, ret, out, allowsFieldFlow, config) and
ccc.matchesCall(call)
|
ap instanceof ApNil or allowsFieldFlow = true
flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
)
}
@@ -2691,6 +2709,11 @@ private module Stage4 {
callMayFlowThroughFwd(call, pragma[only_bind_into](config))
}
pragma[nomagic]
private predicate returnNodeMayFlowThrough(RetNodeEx ret, Ap ap, Configuration config) {
fwdFlow(ret, any(CcCall ccc), apSome(_), ap, config)
}
/**
* Holds if `node` with access path `ap` is part of a path from a source to a
* sink in the configuration `config`.
@@ -2767,7 +2790,7 @@ private module Stage4 {
// flow out of a callable
revFlowOut(_, node, _, _, ap, config) and
toReturn = true and
if fwdFlow(node, any(CcCall ccc), apSome(_), ap, config)
if returnNodeMayFlowThrough(node, ap, config)
then returnAp = apSome(ap)
else returnAp = apNone()
}
@@ -2802,9 +2825,8 @@ private module Stage4 {
) {
exists(NodeEx out, boolean allowsFieldFlow |
revFlow(out, toReturn, returnAp, ap, config) and
flowOutOfCall(call, ret, out, allowsFieldFlow, config)
|
ap instanceof ApNil or allowsFieldFlow = true
flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
)
}
@@ -2814,9 +2836,8 @@ private module Stage4 {
) {
exists(ParamNodeEx p, boolean allowsFieldFlow |
revFlow(p, false, returnAp, ap, config) and
flowIntoCall(_, arg, p, allowsFieldFlow, config)
|
ap instanceof ApNil or allowsFieldFlow = true
flowIntoCall(_, arg, p, allowsFieldFlow, config) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
)
}
@@ -2826,9 +2847,8 @@ private module Stage4 {
) {
exists(ParamNodeEx p, boolean allowsFieldFlow |
revFlow(p, true, apSome(returnAp), ap, config) and
flowThroughIntoCall(call, arg, p, allowsFieldFlow, config)
|
ap instanceof ApNil or allowsFieldFlow = true
flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
)
}

View File

@@ -289,6 +289,7 @@ private predicate outBarrier(NodeEx node, Configuration config) {
)
}
pragma[nomagic]
private predicate fullBarrier(NodeEx node, Configuration config) {
exists(Node n | node.asNode() = n |
config.isBarrier(n)
@@ -307,11 +308,23 @@ private predicate fullBarrier(NodeEx node, Configuration config) {
}
pragma[nomagic]
private predicate sourceNode(NodeEx node, Configuration config) { config.isSource(node.asNode()) }
private predicate sourceNode(NodeEx node, Configuration config) {
config.isSource(node.asNode()) and
not fullBarrier(node, config)
}
pragma[nomagic]
private predicate sinkNode(NodeEx node, Configuration config) { config.isSink(node.asNode()) }
/** Provides the relevant barriers for a step from `node1` to `node2`. */
pragma[inline]
private predicate stepFilter(NodeEx node1, NodeEx node2, Configuration config) {
not outBarrier(node1, config) and
not inBarrier(node2, config) and
not fullBarrier(node1, config) and
not fullBarrier(node2, config)
}
/**
* Holds if data can flow in one local step from `node1` to `node2`.
*/
@@ -320,16 +333,14 @@ private predicate localFlowStep(NodeEx node1, NodeEx node2, Configuration config
node1.asNode() = n1 and
node2.asNode() = n2 and
simpleLocalFlowStepExt(n1, n2) and
not outBarrier(node1, config) and
not inBarrier(node2, config) and
not fullBarrier(node1, config) and
not fullBarrier(node2, config)
stepFilter(node1, node2, config)
)
or
exists(Node n |
config.allowImplicitRead(n, _) and
node1.asNode() = n and
node2.isImplicitReadNode(n, false)
node2.isImplicitReadNode(n, false) and
not fullBarrier(node1, config)
)
}
@@ -342,16 +353,14 @@ private predicate additionalLocalFlowStep(NodeEx node1, NodeEx node2, Configurat
node2.asNode() = n2 and
config.isAdditionalFlowStep(n1, n2) and
getNodeEnclosingCallable(n1) = getNodeEnclosingCallable(n2) and
not outBarrier(node1, config) and
not inBarrier(node2, config) and
not fullBarrier(node1, config) and
not fullBarrier(node2, config)
stepFilter(node1, node2, config)
)
or
exists(Node n |
config.allowImplicitRead(n, _) and
node1.isImplicitReadNode(n, true) and
node2.asNode() = n
node2.asNode() = n and
not fullBarrier(node2, config)
)
}
@@ -363,10 +372,7 @@ private predicate jumpStep(NodeEx node1, NodeEx node2, Configuration config) {
node1.asNode() = n1 and
node2.asNode() = n2 and
jumpStepCached(n1, n2) and
not outBarrier(node1, config) and
not inBarrier(node2, config) and
not fullBarrier(node1, config) and
not fullBarrier(node2, config) and
stepFilter(node1, node2, config) and
not config.getAFeature() instanceof FeatureEqualSourceSinkCallContext
)
}
@@ -380,16 +386,14 @@ private predicate additionalJumpStep(NodeEx node1, NodeEx node2, Configuration c
node2.asNode() = n2 and
config.isAdditionalFlowStep(n1, n2) and
getNodeEnclosingCallable(n1) != getNodeEnclosingCallable(n2) and
not outBarrier(node1, config) and
not inBarrier(node2, config) and
not fullBarrier(node1, config) and
not fullBarrier(node2, config) and
stepFilter(node1, node2, config) and
not config.getAFeature() instanceof FeatureEqualSourceSinkCallContext
)
}
private predicate read(NodeEx node1, Content c, NodeEx node2, Configuration config) {
read(node1.asNode(), c, node2.asNode())
read(node1.asNode(), c, node2.asNode()) and
stepFilter(node1, node2, config)
or
exists(Node n |
node2.isImplicitReadNode(n, true) and
@@ -402,7 +406,8 @@ private predicate store(
NodeEx node1, TypedContent tc, NodeEx node2, DataFlowType contentType, Configuration config
) {
store(node1.asNode(), tc, node2.asNode(), contentType) and
read(_, tc.getContent(), _, config)
read(_, tc.getContent(), _, config) and
stepFilter(node1, node2, config)
}
pragma[nomagic]
@@ -451,63 +456,59 @@ private module Stage1 {
* argument in a call.
*/
predicate fwdFlow(NodeEx node, Cc cc, Configuration config) {
not fullBarrier(node, config) and
(
sourceNode(node, config) and
if hasSourceCallCtx(config) then cc = true else cc = false
sourceNode(node, config) and
if hasSourceCallCtx(config) then cc = true else cc = false
or
exists(NodeEx mid |
fwdFlow(mid, cc, config) and
localFlowStep(mid, node, config)
)
or
exists(NodeEx mid |
fwdFlow(mid, cc, config) and
additionalLocalFlowStep(mid, node, config)
)
or
exists(NodeEx mid |
fwdFlow(mid, _, config) and
jumpStep(mid, node, config) and
cc = false
)
or
exists(NodeEx mid |
fwdFlow(mid, _, config) and
additionalJumpStep(mid, node, config) and
cc = false
)
or
// store
exists(NodeEx mid |
useFieldFlow(config) and
fwdFlow(mid, cc, config) and
store(mid, _, node, _, config)
)
or
// read
exists(Content c |
fwdFlowRead(c, node, cc, config) and
fwdFlowConsCand(c, config)
)
or
// flow into a callable
exists(NodeEx arg |
fwdFlow(arg, _, config) and
viableParamArgEx(_, node, arg) and
cc = true and
not fullBarrier(node, config)
)
or
// flow out of a callable
exists(DataFlowCall call |
fwdFlowOut(call, node, false, config) and
cc = false
or
exists(NodeEx mid |
fwdFlow(mid, cc, config) and
localFlowStep(mid, node, config)
)
or
exists(NodeEx mid |
fwdFlow(mid, cc, config) and
additionalLocalFlowStep(mid, node, config)
)
or
exists(NodeEx mid |
fwdFlow(mid, _, config) and
jumpStep(mid, node, config) and
cc = false
)
or
exists(NodeEx mid |
fwdFlow(mid, _, config) and
additionalJumpStep(mid, node, config) and
cc = false
)
or
// store
exists(NodeEx mid |
useFieldFlow(config) and
fwdFlow(mid, cc, config) and
store(mid, _, node, _, config) and
not outBarrier(mid, config)
)
or
// read
exists(Content c |
fwdFlowRead(c, node, cc, config) and
fwdFlowConsCand(c, config) and
not inBarrier(node, config)
)
or
// flow into a callable
exists(NodeEx arg |
fwdFlow(arg, _, config) and
viableParamArgEx(_, node, arg) and
cc = true
)
or
// flow out of a callable
exists(DataFlowCall call |
fwdFlowOut(call, node, false, config) and
cc = false
or
fwdFlowOutFromArg(call, node, config) and
fwdFlowIsEntered(call, cc, config)
)
fwdFlowOutFromArg(call, node, config) and
fwdFlowIsEntered(call, cc, config)
)
}
@@ -547,7 +548,8 @@ private module Stage1 {
private predicate fwdFlowOut(DataFlowCall call, NodeEx out, Cc cc, Configuration config) {
exists(ReturnPosition pos |
fwdFlowReturnPosition(pos, cc, config) and
viableReturnPosOutEx(call, pos, out)
viableReturnPosOutEx(call, pos, out) and
not fullBarrier(out, config)
)
}
@@ -773,6 +775,7 @@ private module Stage1 {
* Holds if flow may enter through `p` and reach a return node making `p` a
* candidate for the origin of a summary.
*/
pragma[nomagic]
predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) {
exists(ReturnKindExt kind |
throughFlowNodeCand(p, config) and
@@ -1009,6 +1012,9 @@ private module Stage2 {
private predicate flowIntoCall = flowIntoCallNodeCand1/5;
bindingset[node, ap]
private predicate filter(NodeEx node, Ap ap) { any() }
bindingset[ap, contentType]
private predicate typecheckStore(Ap ap, DataFlowType contentType) { any() }
@@ -1017,14 +1023,23 @@ private module Stage2 {
PrevStage::revFlow(node, _, _, apa, config)
}
bindingset[result, apa]
private ApApprox unbindApa(ApApprox apa) {
exists(ApApprox apa0 |
apa = pragma[only_bind_into](apa0) and result = pragma[only_bind_into](apa0)
)
}
pragma[nomagic]
private predicate flowThroughOutOfCall(
DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config
DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow,
Configuration config
) {
flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and
PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and
PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _,
pragma[only_bind_into](config))
pragma[only_bind_into](config)) and
ccc.matchesCall(call)
}
/**
@@ -1037,6 +1052,13 @@ private module Stage2 {
*/
pragma[nomagic]
predicate fwdFlow(NodeEx node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
fwdFlow0(node, cc, argAp, ap, config) and
flowCand(node, unbindApa(getApprox(ap)), config) and
filter(node, ap)
}
pragma[nomagic]
private predicate fwdFlow0(NodeEx node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
flowCand(node, _, config) and
sourceNode(node, config) and
(if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
@@ -1107,7 +1129,7 @@ private module Stage2 {
) {
exists(DataFlowType contentType |
fwdFlow(node1, cc, argAp, ap1, config) and
PrevStage::storeStepCand(node1, getApprox(ap1), tc, node2, contentType, config) and
PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and
typecheckStore(ap1, contentType)
)
}
@@ -1142,9 +1164,8 @@ private module Stage2 {
exists(ArgNodeEx arg, boolean allowsFieldFlow |
fwdFlow(arg, outercc, argAp, ap, config) and
flowIntoCall(call, arg, p, allowsFieldFlow, config) and
innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc)
|
ap instanceof ApNil or allowsFieldFlow = true
innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
)
}
@@ -1159,9 +1180,8 @@ private module Stage2 {
fwdFlow(ret, innercc, argAp, ap, config) and
flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
inner = ret.getEnclosingCallable() and
ccOut = getCallContextReturn(inner, call, innercc)
|
ap instanceof ApNil or allowsFieldFlow = true
ccOut = getCallContextReturn(inner, call, innercc) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
)
}
@@ -1171,10 +1191,8 @@ private module Stage2 {
) {
exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc |
fwdFlow(ret, ccc, apSome(argAp), ap, config) and
flowThroughOutOfCall(call, ret, out, allowsFieldFlow, config) and
ccc.matchesCall(call)
|
ap instanceof ApNil or allowsFieldFlow = true
flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
)
}
@@ -1188,7 +1206,7 @@ private module Stage2 {
) {
exists(ParamNodeEx p |
fwdFlowIn(call, p, cc, _, argAp, ap, config) and
PrevStage::parameterMayFlowThrough(p, _, getApprox(ap), config)
PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config)
)
}
@@ -1230,6 +1248,11 @@ private module Stage2 {
callMayFlowThroughFwd(call, pragma[only_bind_into](config))
}
pragma[nomagic]
private predicate returnNodeMayFlowThrough(RetNodeEx ret, Ap ap, Configuration config) {
fwdFlow(ret, any(CcCall ccc), apSome(_), ap, config)
}
/**
* Holds if `node` with access path `ap` is part of a path from a source to a
* sink in the configuration `config`.
@@ -1306,7 +1329,7 @@ private module Stage2 {
// flow out of a callable
revFlowOut(_, node, _, _, ap, config) and
toReturn = true and
if fwdFlow(node, any(CcCall ccc), apSome(_), ap, config)
if returnNodeMayFlowThrough(node, ap, config)
then returnAp = apSome(ap)
else returnAp = apNone()
}
@@ -1341,9 +1364,8 @@ private module Stage2 {
) {
exists(NodeEx out, boolean allowsFieldFlow |
revFlow(out, toReturn, returnAp, ap, config) and
flowOutOfCall(call, ret, out, allowsFieldFlow, config)
|
ap instanceof ApNil or allowsFieldFlow = true
flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
)
}
@@ -1353,9 +1375,8 @@ private module Stage2 {
) {
exists(ParamNodeEx p, boolean allowsFieldFlow |
revFlow(p, false, returnAp, ap, config) and
flowIntoCall(_, arg, p, allowsFieldFlow, config)
|
ap instanceof ApNil or allowsFieldFlow = true
flowIntoCall(_, arg, p, allowsFieldFlow, config) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
)
}
@@ -1365,9 +1386,8 @@ private module Stage2 {
) {
exists(ParamNodeEx p, boolean allowsFieldFlow |
revFlow(p, true, apSome(returnAp), ap, config) and
flowThroughIntoCall(call, arg, p, allowsFieldFlow, config)
|
ap instanceof ApNil or allowsFieldFlow = true
flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
)
}
@@ -1707,12 +1727,14 @@ private module Stage3 {
pragma[nomagic]
private predicate flowThroughOutOfCall(
DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config
DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow,
Configuration config
) {
flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and
PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and
PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _,
pragma[only_bind_into](config))
pragma[only_bind_into](config)) and
ccc.matchesCall(call)
}
/**
@@ -1837,9 +1859,8 @@ private module Stage3 {
exists(ArgNodeEx arg, boolean allowsFieldFlow |
fwdFlow(arg, outercc, argAp, ap, config) and
flowIntoCall(call, arg, p, allowsFieldFlow, config) and
innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc)
|
ap instanceof ApNil or allowsFieldFlow = true
innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
)
}
@@ -1854,9 +1875,8 @@ private module Stage3 {
fwdFlow(ret, innercc, argAp, ap, config) and
flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
inner = ret.getEnclosingCallable() and
ccOut = getCallContextReturn(inner, call, innercc)
|
ap instanceof ApNil or allowsFieldFlow = true
ccOut = getCallContextReturn(inner, call, innercc) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
)
}
@@ -1866,10 +1886,8 @@ private module Stage3 {
) {
exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc |
fwdFlow(ret, ccc, apSome(argAp), ap, config) and
flowThroughOutOfCall(call, ret, out, allowsFieldFlow, config) and
ccc.matchesCall(call)
|
ap instanceof ApNil or allowsFieldFlow = true
flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
)
}
@@ -1925,6 +1943,11 @@ private module Stage3 {
callMayFlowThroughFwd(call, pragma[only_bind_into](config))
}
pragma[nomagic]
private predicate returnNodeMayFlowThrough(RetNodeEx ret, Ap ap, Configuration config) {
fwdFlow(ret, any(CcCall ccc), apSome(_), ap, config)
}
/**
* Holds if `node` with access path `ap` is part of a path from a source to a
* sink in the configuration `config`.
@@ -2001,7 +2024,7 @@ private module Stage3 {
// flow out of a callable
revFlowOut(_, node, _, _, ap, config) and
toReturn = true and
if fwdFlow(node, any(CcCall ccc), apSome(_), ap, config)
if returnNodeMayFlowThrough(node, ap, config)
then returnAp = apSome(ap)
else returnAp = apNone()
}
@@ -2036,9 +2059,8 @@ private module Stage3 {
) {
exists(NodeEx out, boolean allowsFieldFlow |
revFlow(out, toReturn, returnAp, ap, config) and
flowOutOfCall(call, ret, out, allowsFieldFlow, config)
|
ap instanceof ApNil or allowsFieldFlow = true
flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
)
}
@@ -2048,9 +2070,8 @@ private module Stage3 {
) {
exists(ParamNodeEx p, boolean allowsFieldFlow |
revFlow(p, false, returnAp, ap, config) and
flowIntoCall(_, arg, p, allowsFieldFlow, config)
|
ap instanceof ApNil or allowsFieldFlow = true
flowIntoCall(_, arg, p, allowsFieldFlow, config) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
)
}
@@ -2060,9 +2081,8 @@ private module Stage3 {
) {
exists(ParamNodeEx p, boolean allowsFieldFlow |
revFlow(p, true, apSome(returnAp), ap, config) and
flowThroughIntoCall(call, arg, p, allowsFieldFlow, config)
|
ap instanceof ApNil or allowsFieldFlow = true
flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
)
}
@@ -2473,12 +2493,14 @@ private module Stage4 {
pragma[nomagic]
private predicate flowThroughOutOfCall(
DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config
DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow,
Configuration config
) {
flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and
PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and
PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _,
pragma[only_bind_into](config))
pragma[only_bind_into](config)) and
ccc.matchesCall(call)
}
/**
@@ -2603,9 +2625,8 @@ private module Stage4 {
exists(ArgNodeEx arg, boolean allowsFieldFlow |
fwdFlow(arg, outercc, argAp, ap, config) and
flowIntoCall(call, arg, p, allowsFieldFlow, config) and
innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc)
|
ap instanceof ApNil or allowsFieldFlow = true
innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
)
}
@@ -2620,9 +2641,8 @@ private module Stage4 {
fwdFlow(ret, innercc, argAp, ap, config) and
flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
inner = ret.getEnclosingCallable() and
ccOut = getCallContextReturn(inner, call, innercc)
|
ap instanceof ApNil or allowsFieldFlow = true
ccOut = getCallContextReturn(inner, call, innercc) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
)
}
@@ -2632,10 +2652,8 @@ private module Stage4 {
) {
exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc |
fwdFlow(ret, ccc, apSome(argAp), ap, config) and
flowThroughOutOfCall(call, ret, out, allowsFieldFlow, config) and
ccc.matchesCall(call)
|
ap instanceof ApNil or allowsFieldFlow = true
flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
)
}
@@ -2691,6 +2709,11 @@ private module Stage4 {
callMayFlowThroughFwd(call, pragma[only_bind_into](config))
}
pragma[nomagic]
private predicate returnNodeMayFlowThrough(RetNodeEx ret, Ap ap, Configuration config) {
fwdFlow(ret, any(CcCall ccc), apSome(_), ap, config)
}
/**
* Holds if `node` with access path `ap` is part of a path from a source to a
* sink in the configuration `config`.
@@ -2767,7 +2790,7 @@ private module Stage4 {
// flow out of a callable
revFlowOut(_, node, _, _, ap, config) and
toReturn = true and
if fwdFlow(node, any(CcCall ccc), apSome(_), ap, config)
if returnNodeMayFlowThrough(node, ap, config)
then returnAp = apSome(ap)
else returnAp = apNone()
}
@@ -2802,9 +2825,8 @@ private module Stage4 {
) {
exists(NodeEx out, boolean allowsFieldFlow |
revFlow(out, toReturn, returnAp, ap, config) and
flowOutOfCall(call, ret, out, allowsFieldFlow, config)
|
ap instanceof ApNil or allowsFieldFlow = true
flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
)
}
@@ -2814,9 +2836,8 @@ private module Stage4 {
) {
exists(ParamNodeEx p, boolean allowsFieldFlow |
revFlow(p, false, returnAp, ap, config) and
flowIntoCall(_, arg, p, allowsFieldFlow, config)
|
ap instanceof ApNil or allowsFieldFlow = true
flowIntoCall(_, arg, p, allowsFieldFlow, config) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
)
}
@@ -2826,9 +2847,8 @@ private module Stage4 {
) {
exists(ParamNodeEx p, boolean allowsFieldFlow |
revFlow(p, true, apSome(returnAp), ap, config) and
flowThroughIntoCall(call, arg, p, allowsFieldFlow, config)
|
ap instanceof ApNil or allowsFieldFlow = true
flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
)
}

View File

@@ -9,6 +9,19 @@ private import tainttracking1.TaintTrackingParameter::Private
private import tainttracking1.TaintTrackingParameter::Public
module Consistency {
private newtype TConsistencyConfiguration = MkConsistencyConfiguration()
/** A class for configuring the consistency queries. */
class ConsistencyConfiguration extends TConsistencyConfiguration {
string toString() { none() }
/** Holds if `n` should be excluded from the consistency test `postWithInFlow`. */
predicate postWithInFlowExclude(Node n) { none() }
/** Holds if `n` should be excluded from the consistency test `argHasPostUpdate`. */
predicate argHasPostUpdateExclude(ArgumentNode n) { none() }
}
private class RelevantNode extends Node {
RelevantNode() {
this instanceof ArgumentNode or
@@ -164,7 +177,7 @@ module Consistency {
query predicate argHasPostUpdate(ArgumentNode n, string msg) {
not hasPost(n) and
not isImmutableOrUnobservable(n) and
not any(ConsistencyConfiguration c).argHasPostUpdateExclude(n) and
msg = "ArgumentNode is missing PostUpdateNode."
}
@@ -177,6 +190,7 @@ module Consistency {
isPostUpdateNode(n) and
not clearsContent(n, _) and
simpleLocalFlowStep(_, n) and
not any(ConsistencyConfiguration c).postWithInFlowExclude(n) and
msg = "PostUpdateNode should not be the target of local flow."
}
}

View File

@@ -64,37 +64,37 @@ module LocalFlow {
)
}
/**
* Holds if there is a local use-use flow step from `nodeFrom` to `nodeTo`
* involving SSA definition `def`.
*/
predicate localSsaFlowStepUseUse(Ssa::Definition def, Node nodeFrom, Node nodeTo) {
def.hasAdjacentReads(nodeFrom.asExpr(), nodeTo.asExpr())
}
/**
* Holds if there is a local flow step from `nodeFrom` to `nodeTo` involving
* SSA definition `def`.
*/
predicate localSsaFlowStep(Ssa::Definition def, Node nodeFrom, Node nodeTo) {
// Flow from assignment into SSA definition
def.(Ssa::WriteDefinition).assigns(nodeFrom.asExpr()) and
nodeTo.(SsaDefinitionNode).getDefinition() = def
or
// Flow from SSA definition to first read
def = nodeFrom.(SsaDefinitionNode).getDefinition() and
nodeTo.asExpr() = def.getAFirstRead()
or
// Flow from read to next read
exists(
CfgNodes::ExprNodes::VariableReadAccessCfgNode read1,
CfgNodes::ExprNodes::VariableReadAccessCfgNode read2
|
def.hasAdjacentReads(read1, read2) and
nodeTo.asExpr() = read2
|
nodeFrom.asExpr() = read1
private predicate localSsaFlowStep(Node nodeFrom, Node nodeTo) {
exists(Ssa::Definition def |
// Flow from assignment into SSA definition
def.(Ssa::WriteDefinition).assigns(nodeFrom.asExpr()) and
nodeTo.(SsaDefinitionNode).getDefinition() = def
or
read1 = nodeFrom.(PostUpdateNode).getPreUpdateNode().asExpr()
)
or
// Flow into phi node
exists(Ssa::PhiNode phi |
localFlowSsaInput(nodeFrom, def, phi) and
phi = nodeTo.(SsaDefinitionNode).getDefinition() and
def = phi.getAnInput()
// Flow from SSA definition to first read
def = nodeFrom.(SsaDefinitionNode).getDefinition() and
nodeTo.asExpr() = def.getAFirstRead()
or
// Flow from read to next read
localSsaFlowStepUseUse(def, nodeFrom.(PostUpdateNode).getPreUpdateNode(), nodeTo)
or
// Flow into phi node
exists(Ssa::PhiNode phi |
localFlowSsaInput(nodeFrom, def, phi) and
phi = nodeTo.(SsaDefinitionNode).getDefinition() and
def = phi.getAnInput()
)
)
// TODO
// or
@@ -105,6 +105,42 @@ module LocalFlow {
// def = uncertain.getPriorDefinition()
// )
}
predicate localFlowStepCommon(Node nodeFrom, Node nodeTo) {
localSsaFlowStep(nodeFrom, nodeTo)
or
nodeFrom.(SelfParameterNode).getMethod() = nodeTo.asExpr().getExpr().getEnclosingCallable() and
nodeTo.asExpr().getExpr() instanceof Self
or
nodeFrom.asExpr() = nodeTo.asExpr().(CfgNodes::ExprNodes::AssignExprCfgNode).getRhs()
or
nodeFrom.asExpr() = nodeTo.asExpr().(CfgNodes::ExprNodes::BlockArgumentCfgNode).getValue()
or
nodeFrom.asExpr() = nodeTo.asExpr().(CfgNodes::ExprNodes::StmtSequenceCfgNode).getLastStmt()
or
nodeFrom.asExpr() = nodeTo.asExpr().(CfgNodes::ExprNodes::ConditionalExprCfgNode).getBranch(_)
or
nodeFrom.asExpr() = nodeTo.asExpr().(CfgNodes::ExprNodes::CaseExprCfgNode).getBranch(_)
or
exists(CfgNodes::ExprCfgNode exprTo, ReturningStatementNode n |
nodeFrom = n and
exprTo = nodeTo.asExpr() and
n.getReturningNode().getNode() instanceof BreakStmt and
exprTo.getNode() instanceof Loop and
nodeTo.asExpr().getAPredecessor(any(SuccessorTypes::BreakSuccessor s)) = n.getReturningNode()
)
or
nodeFrom.asExpr() = nodeTo.(ReturningStatementNode).getReturningNode().getReturnedValueNode()
or
nodeTo.asExpr() =
any(CfgNodes::ExprNodes::ForExprCfgNode for |
exists(SuccessorType s |
not s instanceof SuccessorTypes::BreakSuccessor and
exists(for.getAPredecessor(s))
) and
nodeFrom.asExpr() = for.getValue()
)
}
}
/** An argument of a call (including qualifier arguments, excluding block arguments). */
@@ -160,49 +196,13 @@ private module Cached {
p.(KeywordParameter).getDefaultValue() = e.getExprNode().getExpr()
}
private predicate localFlowStepCommon(Node nodeFrom, Node nodeTo) {
LocalFlow::localSsaFlowStep(_, nodeFrom, nodeTo)
or
nodeFrom.(SelfParameterNode).getMethod() = nodeTo.asExpr().getExpr().getEnclosingCallable() and
nodeTo.asExpr().getExpr() instanceof Self
or
nodeFrom.asExpr() = nodeTo.asExpr().(CfgNodes::ExprNodes::AssignExprCfgNode).getRhs()
or
nodeFrom.asExpr() = nodeTo.asExpr().(CfgNodes::ExprNodes::BlockArgumentCfgNode).getValue()
or
nodeFrom.asExpr() = nodeTo.asExpr().(CfgNodes::ExprNodes::StmtSequenceCfgNode).getLastStmt()
or
nodeFrom.asExpr() = nodeTo.asExpr().(CfgNodes::ExprNodes::ConditionalExprCfgNode).getBranch(_)
or
nodeFrom.asExpr() = nodeTo.asExpr().(CfgNodes::ExprNodes::CaseExprCfgNode).getBranch(_)
or
exists(CfgNodes::ExprCfgNode exprTo, ReturningStatementNode n |
nodeFrom = n and
exprTo = nodeTo.asExpr() and
n.getReturningNode().getNode() instanceof BreakStmt and
exprTo.getNode() instanceof Loop and
nodeTo.asExpr().getAPredecessor(any(SuccessorTypes::BreakSuccessor s)) = n.getReturningNode()
)
or
nodeFrom.asExpr() = nodeTo.(ReturningStatementNode).getReturningNode().getReturnedValueNode()
or
nodeTo.asExpr() =
any(CfgNodes::ExprNodes::ForExprCfgNode for |
exists(SuccessorType s |
not s instanceof SuccessorTypes::BreakSuccessor and
exists(for.getAPredecessor(s))
) and
nodeFrom.asExpr() = for.getValue()
)
}
/**
* This is the local flow predicate that is used as a building block in global
* data flow.
*/
cached
predicate simpleLocalFlowStep(Node nodeFrom, Node nodeTo) {
localFlowStepCommon(nodeFrom, nodeTo)
LocalFlow::localFlowStepCommon(nodeFrom, nodeTo)
or
defaultValueFlow(nodeTo.(ParameterNode).getParameter(), nodeFrom)
or
@@ -210,18 +210,23 @@ private module Cached {
or
nodeTo.(SynthReturnNode).getAnInput() = nodeFrom
or
LocalFlow::localSsaFlowStepUseUse(_, nodeFrom, nodeTo) and
not FlowSummaryImpl::Private::Steps::summaryClearsContentArg(nodeFrom, _)
or
FlowSummaryImpl::Private::Steps::summaryLocalStep(nodeFrom, nodeTo, true)
}
/** This is the local flow predicate that is exposed. */
cached
predicate localFlowStepImpl(Node nodeFrom, Node nodeTo) {
localFlowStepCommon(nodeFrom, nodeTo)
LocalFlow::localFlowStepCommon(nodeFrom, nodeTo)
or
defaultValueFlow(nodeTo.(ParameterNode).getParameter(), nodeFrom)
or
nodeTo = LocalFlow::getParameterDefNode(nodeFrom.(ParameterNode).getParameter())
or
LocalFlow::localSsaFlowStepUseUse(_, nodeFrom, nodeTo)
or
// Simple flow through library code is included in the exposed local
// step relation, even though flow is technically inter-procedural
FlowSummaryImpl::Private::Steps::summaryThroughStep(nodeFrom, nodeTo, true)
@@ -230,12 +235,14 @@ private module Cached {
/** This is the local flow predicate that is used in type tracking. */
cached
predicate localFlowStepTypeTracker(Node nodeFrom, Node nodeTo) {
localFlowStepCommon(nodeFrom, nodeTo)
LocalFlow::localFlowStepCommon(nodeFrom, nodeTo)
or
exists(NamedParameter p |
defaultValueFlow(p, nodeFrom) and
nodeTo = LocalFlow::getParameterDefNode(p)
)
or
LocalFlow::localSsaFlowStepUseUse(_, nodeFrom, nodeTo)
}
cached
@@ -417,7 +424,7 @@ private module ParameterNodes {
import ParameterNodes
/** A data-flow node used to model flow summaries. */
private class SummaryNode extends NodeImpl, TSummaryNode {
class SummaryNode extends NodeImpl, TSummaryNode {
private FlowSummaryImpl::Public::SummarizedCallable c;
private FlowSummaryImpl::Private::SummaryNodeState state;
@@ -757,15 +764,6 @@ class Unit extends TUnit {
string toString() { result = "unit" }
}
/**
* Holds if `n` does not require a `PostUpdateNode` as it either cannot be
* modified or its modification cannot be observed, for example if it is a
* freshly created object that is not saved in a variable.
*
* This predicate is only used for consistency checks.
*/
predicate isImmutableOrUnobservable(Node n) { n instanceof BlockArgumentNode }
/**
* Holds if the node `n` is unreachable when the call context is `call`.
*/

View File

@@ -127,6 +127,38 @@ module Public {
SummaryComponentStack return(ReturnKind rk) { result = singleton(SummaryComponent::return(rk)) }
}
private predicate noComponentSpecificCsv(SummaryComponent sc) {
not exists(getComponentSpecificCsv(sc))
}
/** Gets a textual representation of this component used for flow summaries. */
private string getComponentCsv(SummaryComponent sc) {
result = getComponentSpecificCsv(sc)
or
noComponentSpecificCsv(sc) and
(
exists(int i | sc = TParameterSummaryComponent(i) and result = "Parameter[" + i + "]")
or
exists(int i | sc = TArgumentSummaryComponent(i) and result = "Argument[" + i + "]")
or
sc = TReturnSummaryComponent(getReturnValueKind()) and result = "ReturnValue"
)
}
/** Gets a textual representation of this stack used for flow summaries. */
string getComponentStackCsv(SummaryComponentStack stack) {
exists(SummaryComponent head, SummaryComponentStack tail |
head = stack.head() and
tail = stack.tail() and
result = getComponentCsv(head) + " of " + getComponentStackCsv(tail)
)
or
exists(SummaryComponent c |
stack = TSingletonSummaryComponentStack(c) and
result = getComponentCsv(c)
)
}
/**
* A class that exists for QL technical reasons only (the IPA type used
* to represent component stacks needs to be bounded).
@@ -970,18 +1002,38 @@ module Private {
module TestOutput {
/** A flow summary to include in the `summary/3` query predicate. */
abstract class RelevantSummarizedCallable extends SummarizedCallable {
/** Gets the string representation of this callable used by `summary/3`. */
string getFullString() { result = this.toString() }
/** Gets the string representation of this callable used by `summary/1`. */
abstract string getCallableCsv();
/** Holds if flow is progated between `input` and `output` */
predicate relevantSummary(
SummaryComponentStack input, SummaryComponentStack output, boolean preservesValue
) {
this.propagatesFlow(input, output, preservesValue)
}
}
/** A query predicate for outputting flow summaries in QL tests. */
query predicate summary(string callable, string flow, boolean preservesValue) {
/** Render the kind in the format used in flow summaries. */
private string renderKind(boolean preservesValue) {
preservesValue = true and result = "value"
or
preservesValue = false and result = "taint"
}
/**
* A query predicate for outputting flow summaries in semi-colon separated format in QL tests.
* The syntax is: "namespace;type;overrides;name;signature;ext;inputspec;outputspec;kind",
* ext is hardcoded to empty.
*/
query predicate summary(string csv) {
exists(
RelevantSummarizedCallable c, SummaryComponentStack input, SummaryComponentStack output
RelevantSummarizedCallable c, SummaryComponentStack input, SummaryComponentStack output,
boolean preservesValue
|
callable = c.getFullString() and
c.propagatesFlow(input, output, preservesValue) and
flow = input + " -> " + output
c.relevantSummary(input, output, preservesValue) and
csv =
c.getCallableCsv() + ";;" + getComponentStackCsv(input) + ";" +
getComponentStackCsv(output) + ";" + renderKind(preservesValue)
)
}
}

View File

@@ -69,6 +69,11 @@ SummaryComponent interpretComponentSpecific(string c) {
result = FlowSummary::SummaryComponent::argument(any(int i | i >= 0))
}
/** Gets the textual representation of a summary component in the format used for flow summaries. */
string getComponentSpecificCsv(SummaryComponent sc) {
sc = TArgumentSummaryComponent(-2) and result = "BlockArgument"
}
/** Gets the return kind corresponding to specification `"ReturnValue"`. */
NormalReturnKind getReturnValueKind() { any() }

View File

@@ -634,3 +634,29 @@ class UncertainWriteDefinition extends WriteDefinition {
)
}
}
/** Provides a set of consistency queries. */
module Consistency {
abstract class RelevantDefinition extends Definition {
abstract predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
);
}
query predicate nonUniqueDef(RelevantDefinition def, SourceVariable v, BasicBlock bb, int i) {
ssaDefReachesRead(v, def, bb, i) and
not exists(unique(Definition def0 | ssaDefReachesRead(v, def0, bb, i)))
}
query predicate readWithoutDef(SourceVariable v, BasicBlock bb, int i) {
variableRead(bb, i, v, _) and
not ssaDefReachesRead(v, _, bb, i)
}
query predicate deadDef(RelevantDefinition def, SourceVariable v) {
v = def.getSourceVariable() and
not ssaDefReachesRead(_, def, _, _) and
not phiHasInputFromBlock(_, def, _) and
not uncertainWriteDefinitionInput(_, def)
}
}

View File

@@ -257,3 +257,21 @@ predicate controllerTemplateFile(ActionControllerControllerClass cls, ErbFile te
)
)
}
/**
* A call to either `skip_forgery_protection` or
* `skip_before_action :verify_authenticity_token` to disable CSRF authenticity
* token protection.
*/
class ActionControllerSkipForgeryProtectionCall extends CSRFProtectionSetting::Range {
ActionControllerSkipForgeryProtectionCall() {
exists(MethodCall call | call = this.asExpr().getExpr() |
call.getMethodName() = "skip_forgery_protection"
or
call.getMethodName() = "skip_before_action" and
call.getAnArgument().(SymbolLiteral).getValueText() = "verify_authenticity_token"
)
}
override boolean getVerificationSetting() { result = false }
}

View File

@@ -4,7 +4,7 @@ private import codeql.ruby.Concepts
private import codeql.ruby.DataFlow
private import codeql.ruby.dataflow.FlowSummary
/** Defines calls to `ActiveStorage::Filename#sanitized` as path sanitizers. */
/** A call to `ActiveStorage::Filename#sanitized`, considered as a path sanitizer. */
class ActiveStorageFilenameSanitizedCall extends Path::PathSanitization::Range, DataFlow::CallNode {
ActiveStorageFilenameSanitizedCall() {
this.getReceiver() =
@@ -13,7 +13,7 @@ class ActiveStorageFilenameSanitizedCall extends Path::PathSanitization::Range,
}
}
/** Taint summary for `ActiveStorage::Filename.new`. */
/** The taint summary for `ActiveStorage::Filename.new`. */
class ActiveStorageFilenameNewSummary extends SummarizedCallable {
ActiveStorageFilenameNewSummary() { this = "ActiveStorage::Filename.new" }
@@ -33,7 +33,7 @@ class ActiveStorageFilenameNewSummary extends SummarizedCallable {
}
}
/** Taint summary for `ActiveStorage::Filename#sanitized`. */
/** The taint summary for `ActiveStorage::Filename#sanitized`. */
class ActiveStorageFilenameSanitizedSummary extends SummarizedCallable {
ActiveStorageFilenameSanitizedSummary() { this = "ActiveStorage::Filename#sanitized" }

View File

@@ -254,7 +254,7 @@ module File {
}
/**
* Flow summary for several methods on the `File` class that propagate taint
* A flow summary for several methods on the `File` class that propagate taint
* from their first argument to the return value.
*/
class FilePathConversionSummary extends SummarizedCallable {
@@ -277,7 +277,7 @@ module File {
}
/**
* Flow summary for `File.join`, which propagates taint from every argument to
* A flow summary for `File.join`, which propagates taint from every argument to
* its return value.
*/
class FileJoinSummary extends SummarizedCallable {

View File

@@ -0,0 +1,131 @@
/**
* Provides classes for working with Rails.
*/
private import codeql.files.FileSystem
private import codeql.ruby.AST
private import codeql.ruby.Concepts
private import codeql.ruby.DataFlow
private import codeql.ruby.frameworks.ActionController
private import codeql.ruby.frameworks.ActionView
private import codeql.ruby.frameworks.ActiveRecord
private import codeql.ruby.frameworks.ActiveStorage
private import codeql.ruby.ast.internal.Module
private import codeql.ruby.ApiGraphs
/**
* A reference to either `Rails::Railtie`, `Rails::Engine`, or `Rails::Application`.
* `Engine` and `Application` extend `Railtie`, but may not have definitions present in the database.
*/
private class RailtieClassAccess extends ConstantReadAccess {
RailtieClassAccess() {
this.getScopeExpr().(ConstantAccess).getName() = "Rails" and
this.getName() = ["Railtie", "Engine", "Application"]
}
}
// A `ClassDeclaration` that (transitively) extends `Rails::Railtie`
private class RailtieClass extends ClassDeclaration {
RailtieClass() {
this.getSuperclassExpr() instanceof RailtieClassAccess or
exists(RailtieClass other | other.getModule() = resolveScopeExpr(this.getSuperclassExpr()))
}
}
private DataFlow::CallNode getAConfigureCallNode() {
// `Rails.application.configure`
result = API::getTopLevelMember("Rails").getReturn("application").getAMethodCall("configure")
or
// `Rails::Application.configure`
exists(ConstantReadAccess read, RailtieClass cls |
read = result.getReceiver().asExpr().getExpr() and
resolveScopeExpr(read) = cls.getModule() and
result.asExpr().getExpr().(MethodCall).getMethodName() = "configure"
)
}
/**
* An access to a Rails config object.
*/
private class ConfigSourceNode extends DataFlow::LocalSourceNode {
ConfigSourceNode() {
// `Foo < Rails::Application ... config ...`
exists(MethodCall configCall | this.asExpr().getExpr() = configCall |
configCall.getMethodName() = "config" and
configCall.getEnclosingModule() instanceof RailtieClass
)
or
// `Rails.application.config`
this =
API::getTopLevelMember("Rails")
.getReturn("application")
.getReturn("config")
.getAnImmediateUse()
or
// `Rails.application.configure { ... config ... }`
// `Rails::Application.configure { ... config ... }`
exists(DataFlow::CallNode configureCallNode, Block block, MethodCall configCall |
configCall = this.asExpr().getExpr()
|
configureCallNode = getAConfigureCallNode() and
block = configureCallNode.asExpr().getExpr().(MethodCall).getBlock() and
configCall.getParent+() = block and
configCall.getMethodName() = "config"
)
}
}
private class ConfigNode extends DataFlow::Node {
ConfigNode() { exists(ConfigSourceNode src | src.flowsTo(this)) }
}
// A call where the Rails application config is the receiver
private class CallAgainstConfig extends DataFlow::CallNode {
CallAgainstConfig() { this.getReceiver() instanceof ConfigNode }
MethodCall getCall() { result = this.asExpr().getExpr() }
Block getBlock() { result = this.getCall().getBlock() }
}
private class ActionControllerConfigNode extends DataFlow::Node {
ActionControllerConfigNode() {
exists(CallAgainstConfig source | source.getCall().getMethodName() = "action_controller" |
source.flowsTo(this)
)
}
}
/** Holds if `node` can contain `value`. */
private predicate hasBooleanValue(DataFlow::Node node, boolean value) {
exists(DataFlow::LocalSourceNode literal |
literal.asExpr().getExpr().(BooleanLiteral).getValue() = value and
literal.flowsTo(node)
)
}
// `<actionControllerConfig>.allow_forgery_protection = <verificationSetting>`
private DataFlow::CallNode getAnAllowForgeryProtectionCall(boolean verificationSetting) {
// exclude some test configuration
not (
result.getLocation().getFile().getRelativePath().matches("%test/%") or
result.getLocation().getFile().getStem() = "test"
) and
result.getReceiver() instanceof ActionControllerConfigNode and
result.asExpr().getExpr().(MethodCall).getMethodName() = "allow_forgery_protection=" and
hasBooleanValue(result.getArgument(0), verificationSetting)
}
/**
* A `DataFlow::Node` that may enable or disable Rails CSRF protection in
* production code.
*/
private class AllowForgeryProtectionSetting extends CSRFProtectionSetting::Range {
private boolean verificationSetting;
AllowForgeryProtectionSetting() { this = getAnAllowForgeryProtectionCall(verificationSetting) }
override boolean getVerificationSetting() { result = verificationSetting }
}
// TODO: initialization hooks, e.g. before_configuration, after_initialize...
// TODO: initializers

View File

@@ -2,6 +2,7 @@ private import codeql.ruby.AST
private import codeql.ruby.Concepts
private import codeql.ruby.DataFlow
private import codeql.ruby.ApiGraphs
private import codeql.ruby.dataflow.FlowSummary
/**
* The `Kernel` module is included by the `Object` class, so its methods are available
@@ -71,7 +72,7 @@ string basicObjectInstanceMethodName() {
}
/**
* Instance methods on `BasicObject`, which are available to all classes.
* An instance method on `BasicObject`, which is available to all classes.
*/
class BasicObjectInstanceMethodCall extends UnknownMethodCall {
BasicObjectInstanceMethodCall() { this.getMethodName() = basicObjectInstanceMethodName() }
@@ -92,14 +93,14 @@ string objectInstanceMethodName() {
}
/**
* Instance methods on `Object`, which are available to all classes except `BasicObject`.
* An instance method on `Object`, which is available to all classes except `BasicObject`.
*/
class ObjectInstanceMethodCall extends UnknownMethodCall {
ObjectInstanceMethodCall() { this.getMethodName() = objectInstanceMethodName() }
}
/**
* Method calls which have no known target.
* A `Method` call that has no known target.
* These will typically be calls to methods inherited from a superclass.
*/
class UnknownMethodCall extends MethodCall {
@@ -333,3 +334,18 @@ class ModuleEvalCallCodeExecution extends CodeExecution::Range, DataFlow::CallNo
override DataFlow::Node getCode() { result = this.getArgument(0) }
}
/** Flow summary for `Regexp.escape` and its alias, `Regexp.quote`. */
class RegexpEscapeSummary extends SummarizedCallable {
RegexpEscapeSummary() { this = "Regexp.escape" }
override MethodCall getACall() {
result = API::getTopLevelMember("Regexp").getAMethodCall(["escape", "quote"]).asExpr().getExpr()
}
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
input = "Argument[0]" and
output = "ReturnValue" and
preservesValue = false
}
}

View File

@@ -18,12 +18,14 @@ private import codeql.ruby.ApiGraphs
* https://github.com/excon/excon/blob/master/README.md
*/
class ExconHttpRequest extends HTTP::Client::Request::Range {
DataFlow::Node requestUse;
DataFlow::CallNode requestUse;
API::Node requestNode;
API::Node connectionNode;
DataFlow::Node connectionUse;
ExconHttpRequest() {
requestUse = requestNode.getAnImmediateUse() and
connectionUse = connectionNode.getAnImmediateUse() and
connectionNode =
[
// one-off requests
@@ -44,6 +46,17 @@ class ExconHttpRequest extends HTTP::Client::Request::Range {
override DataFlow::Node getResponseBody() { result = requestNode.getAMethodCall("body") }
override DataFlow::Node getURL() {
// For one-off requests, the URL is in the first argument of the request method call.
// For connection re-use, the URL is split between the first argument of the `new` call
// and the `path` keyword argument of the request method call.
result = requestUse.getArgument(0) and not result.asExpr().getExpr() instanceof Pair
or
result = requestUse.getKeywordArgument("path")
or
result = connectionUse.(DataFlow::CallNode).getArgument(0)
}
override predicate disablesCertificateValidation(DataFlow::Node disablingNode) {
// Check for `ssl_verify_peer: false` in the options hash.
exists(DataFlow::Node arg, int i |

View File

@@ -11,12 +11,16 @@ private import codeql.ruby.ApiGraphs
* # connection re-use
* connection = Faraday.new("http://example.com")
* connection.get("/").body
*
* connection = Faraday.new(url: "http://example.com")
* connection.get("/").body
* ```
*/
class FaradayHttpRequest extends HTTP::Client::Request::Range {
DataFlow::Node requestUse;
API::Node requestNode;
API::Node connectionNode;
DataFlow::Node connectionUse;
DataFlow::CallNode requestUse;
FaradayHttpRequest() {
connectionNode =
@@ -29,11 +33,18 @@ class FaradayHttpRequest extends HTTP::Client::Request::Range {
requestNode =
connectionNode.getReturn(["get", "head", "delete", "post", "put", "patch", "trace"]) and
requestUse = requestNode.getAnImmediateUse() and
connectionUse = connectionNode.getAnImmediateUse() and
this = requestUse.asExpr().getExpr()
}
override DataFlow::Node getResponseBody() { result = requestNode.getAMethodCall("body") }
override DataFlow::Node getURL() {
result = requestUse.getArgument(0) or
result = connectionUse.(DataFlow::CallNode).getArgument(0) or
result = connectionUse.(DataFlow::CallNode).getKeywordArgument("url")
}
override predicate disablesCertificateValidation(DataFlow::Node disablingNode) {
// `Faraday::new` takes an options hash as its second argument, and we're
// looking for

View File

@@ -12,7 +12,7 @@ private import codeql.ruby.ApiGraphs
class HttpClientRequest extends HTTP::Client::Request::Range {
API::Node requestNode;
API::Node connectionNode;
DataFlow::Node requestUse;
DataFlow::CallNode requestUse;
string method;
HttpClientRequest() {
@@ -31,6 +31,8 @@ class HttpClientRequest extends HTTP::Client::Request::Range {
this = requestUse.asExpr().getExpr()
}
override DataFlow::Node getURL() { result = requestUse.getArgument(0) }
override DataFlow::Node getResponseBody() {
// The `get_content` and `post_content` methods return the response body as
// a string. The other methods return a `HTTPClient::Message` object which

View File

@@ -11,6 +11,7 @@ private import codeql.ruby.ApiGraphs
* # TODO: module inclusion
* class MyClass
* include HTTParty
* base_uri "http://example.com"
* end
*
* MyClass.new("http://example.com")
@@ -18,7 +19,7 @@ private import codeql.ruby.ApiGraphs
*/
class HttpartyRequest extends HTTP::Client::Request::Range {
API::Node requestNode;
DataFlow::Node requestUse;
DataFlow::CallNode requestUse;
HttpartyRequest() {
requestUse = requestNode.getAnImmediateUse() and
@@ -28,6 +29,8 @@ class HttpartyRequest extends HTTP::Client::Request::Range {
this = requestUse.asExpr().getExpr()
}
override DataFlow::Node getURL() { result = requestUse.getArgument(0) }
override DataFlow::Node getResponseBody() {
// If HTTParty can recognise the response type, it will parse and return it
// directly from the request call. Otherwise, it will return a `HTTParty::Response`

View File

@@ -46,7 +46,7 @@ class NetHttpRequest extends HTTP::Client::Request::Range {
* Gets the node representing the URL of the request.
* Currently unused, but may be useful in future, e.g. to filter out certain requests.
*/
DataFlow::Node getURLArgument() { result = request.getArgument(0) }
override DataFlow::Node getURL() { result = request.getArgument(0) }
override DataFlow::Node getResponseBody() { result = responseBody }

View File

@@ -14,7 +14,7 @@ private import codeql.ruby.frameworks.StandardLibrary
*/
class OpenUriRequest extends HTTP::Client::Request::Range {
API::Node requestNode;
DataFlow::Node requestUse;
DataFlow::CallNode requestUse;
OpenUriRequest() {
requestNode =
@@ -24,6 +24,8 @@ class OpenUriRequest extends HTTP::Client::Request::Range {
this = requestUse.asExpr().getExpr()
}
override DataFlow::Node getURL() { result = requestUse.getArgument(0) }
override DataFlow::Node getResponseBody() {
result = requestNode.getAMethodCall(["read", "readlines"])
}
@@ -48,7 +50,7 @@ class OpenUriRequest extends HTTP::Client::Request::Range {
* ```
*/
class OpenUriKernelOpenRequest extends HTTP::Client::Request::Range {
DataFlow::Node requestUse;
DataFlow::CallNode requestUse;
OpenUriKernelOpenRequest() {
requestUse instanceof KernelMethodCall and
@@ -56,6 +58,8 @@ class OpenUriKernelOpenRequest extends HTTP::Client::Request::Range {
this = requestUse.asExpr().getExpr()
}
override DataFlow::Node getURL() { result = requestUse.getArgument(0) }
override DataFlow::CallNode getResponseBody() {
result.asExpr().getExpr().(MethodCall).getMethodName() in ["read", "readlines"] and
requestUse.(DataFlow::LocalSourceNode).flowsTo(result.getReceiver())

View File

@@ -6,23 +6,38 @@ private import codeql.ruby.ApiGraphs
* A call that makes an HTTP request using `RestClient`.
* ```ruby
* RestClient.get("http://example.com").body
* RestClient::Resource.new("http://example.com").get.body
* RestClient::Request.execute(url: "http://example.com").body
* ```
*/
class RestClientHttpRequest extends HTTP::Client::Request::Range {
DataFlow::Node requestUse;
DataFlow::CallNode requestUse;
API::Node requestNode;
API::Node connectionNode;
RestClientHttpRequest() {
connectionNode =
[
API::getTopLevelMember("RestClient"),
API::getTopLevelMember("RestClient").getMember("Resource").getInstance()
] and
requestNode =
connectionNode.getReturn(["get", "head", "delete", "options", "post", "put", "patch"]) and
requestUse = requestNode.getAnImmediateUse() and
this = requestUse.asExpr().getExpr()
this = requestUse.asExpr().getExpr() and
(
connectionNode =
[
API::getTopLevelMember("RestClient"),
API::getTopLevelMember("RestClient").getMember("Resource").getInstance()
] and
requestNode =
connectionNode.getReturn(["get", "head", "delete", "options", "post", "put", "patch"])
or
connectionNode = API::getTopLevelMember("RestClient").getMember("Request") and
requestNode = connectionNode.getReturn("execute")
)
}
override DataFlow::Node getURL() {
result = requestUse.getKeywordArgument("url")
or
result = requestUse.getArgument(0) and
// this rules out the alternative above
not result.asExpr().getExpr() instanceof Pair
}
override DataFlow::Node getResponseBody() { result = requestNode.getAMethodCall("body") }

View File

@@ -9,7 +9,7 @@ private import codeql.ruby.ApiGraphs
* ```
*/
class TyphoeusHttpRequest extends HTTP::Client::Request::Range {
DataFlow::Node requestUse;
DataFlow::CallNode requestUse;
API::Node requestNode;
TyphoeusHttpRequest() {
@@ -20,6 +20,8 @@ class TyphoeusHttpRequest extends HTTP::Client::Request::Range {
this = requestUse.asExpr().getExpr()
}
override DataFlow::Node getURL() { result = requestUse.getArgument(0) }
override DataFlow::Node getResponseBody() { result = requestNode.getAMethodCall("body") }
override predicate disablesCertificateValidation(DataFlow::Node disablingNode) {

View File

@@ -0,0 +1,51 @@
/**
* Provides default sources, sinks and sanitizers for reasoning about
* server side request forgery, as well as extension points for adding your own.
*/
private import ruby
private import codeql.ruby.ApiGraphs
private import codeql.ruby.CFG
private import codeql.ruby.DataFlow
private import codeql.ruby.dataflow.RemoteFlowSources
private import codeql.ruby.Concepts
private import codeql.ruby.dataflow.Sanitizers
/**
* Provides default sources, sinks and sanitizers for reasoning about
* server side request forgery, as well as extension points for adding your own.
*/
module ServerSideRequestForgery {
/**
* A data flow source for server side request forgery vulnerabilities.
*/
abstract class Source extends DataFlow::Node { }
/**
* A data flow sink for server side request forgery vulnerabilities.
*/
abstract class Sink extends DataFlow::Node { }
/**
* A sanitizer for server side request forgery vulnerabilities.
*/
abstract class Sanitizer extends DataFlow::Node { }
/**
* A sanitizer guard for "URL redirection" vulnerabilities.
*/
abstract class SanitizerGuard extends DataFlow::BarrierGuard { }
/** A source of remote user input, considered as a flow source for server side request forgery. */
class RemoteFlowSourceAsSource extends Source {
RemoteFlowSourceAsSource() { this instanceof RemoteFlowSource }
}
/** The URL of an HTTP request, considered as a sink. */
class HttpRequestAsSink extends Sink {
HttpRequestAsSink() { exists(HTTP::Client::Request req | req.getURL() = this) }
}
/** String interpolation with a fixed prefix, considered as a flow sanitizer. */
class StringInterpolationAsSanitizer extends PrefixedStringInterpolation, Sanitizer { }
}

View File

@@ -0,0 +1,33 @@
/**
* Provides a taint-tracking configuration for detecting
* "Server side request forgery" vulnerabilities.
*
* Note, for performance reasons: only import this file if `Configuration` is needed,
* otherwise `ServerSideRequestForgeryCustomizations` should be imported instead.
*/
import codeql.ruby.DataFlow::DataFlow::PathGraph
import codeql.ruby.DataFlow
import codeql.ruby.TaintTracking
import ServerSideRequestForgeryCustomizations::ServerSideRequestForgery
import codeql.ruby.dataflow.BarrierGuards
/**
* A taint-tracking configuration for detecting
* "Server side request forgery" vulnerabilities.
*/
class Configuration extends TaintTracking::Configuration {
Configuration() { this = "ServerSideRequestForgery" }
override predicate isSource(DataFlow::Node source) { source instanceof Source }
override predicate isSink(DataFlow::Node sink) { sink instanceof Sink }
override predicate isSanitizer(DataFlow::Node node) { node instanceof Sanitizer }
override predicate isSanitizerGuard(DataFlow::BarrierGuard guard) {
guard instanceof SanitizerGuard or
guard instanceof StringConstCompare or
guard instanceof StringConstArrayInclusionCall
}
}

View File

@@ -9,6 +9,7 @@ private import codeql.ruby.DataFlow
private import codeql.ruby.Concepts
private import codeql.ruby.dataflow.RemoteFlowSources
private import codeql.ruby.dataflow.BarrierGuards
private import codeql.ruby.dataflow.Sanitizers
/**
* Provides default sources, sinks and sanitizers for detecting
@@ -85,6 +86,8 @@ module UrlRedirect {
}
/**
* A string interpolation, seen as a sanitizer for "URL redirection" vulnerabilities.
*
* String interpolation is considered safe, provided the string is prefixed by a non-tainted value.
* In most cases this will prevent the tainted value from controlling e.g. the host of the URL.
*
@@ -103,11 +106,7 @@ module UrlRedirect {
*
* We currently don't catch these cases.
*/
class StringInterpolationAsSanitizer extends Sanitizer {
StringInterpolationAsSanitizer() {
exists(StringlikeLiteral str, int n | str.getComponent(n) = this.asExpr().getExpr() and n > 0)
}
}
class StringInterpolationAsSanitizer extends PrefixedStringInterpolation, Sanitizer { }
/**
* These methods return a new `ActionController::Parameters` or a `Hash` containing a subset of

View File

@@ -13,7 +13,6 @@
*/
import RegExpTreeView
private import codeql.Locations
/**
* A configuration for which parts of a regular expression should be considered relevant for
@@ -219,9 +218,7 @@ private newtype TInputSymbol =
recc instanceof RegExpCharacterClass and
not recc.(RegExpCharacterClass).isUniversalClass()
or
recc instanceof RegExpCharacterClassEscape
or
recc instanceof RegExpNamedCharacterProperty
isEscapeClass(recc, _)
)
} or
/** An input symbol representing all characters matched by `.`. */
@@ -343,22 +340,13 @@ private module CharacterClasses {
char <= hi
)
or
exists(RegExpCharacterClassEscape escape | escape = child |
escape.getValue() = escape.getValue().toLowerCase() and
classEscapeMatches(escape.getValue(), char)
exists(string charClass | isEscapeClass(child, charClass) |
charClass.toLowerCase() = charClass and
classEscapeMatches(charClass, char)
or
char = getARelevantChar() and
escape.getValue() = escape.getValue().toUpperCase() and
not classEscapeMatches(escape.getValue().toLowerCase(), char)
)
or
exists(RegExpNamedCharacterProperty charProp | charProp = child |
not charProp.isInverted() and
namedCharacterPropertyMatches(charProp.getName(), char)
or
char = getARelevantChar() and
charProp.isInverted() and
not namedCharacterPropertyMatches(charProp.getName(), char)
charClass.toUpperCase() = charClass and
not classEscapeMatches(charClass, char)
)
)
}
@@ -421,16 +409,10 @@ private module CharacterClasses {
or
child.(RegExpCharacterRange).isRange(_, result)
or
exists(RegExpCharacterClassEscape escape | child = escape |
result = min(string s | classEscapeMatches(escape.getValue().toLowerCase(), s))
exists(string charClass | isEscapeClass(child, charClass) |
result = min(string s | classEscapeMatches(charClass.toLowerCase(), s))
or
result = max(string s | classEscapeMatches(escape.getValue().toLowerCase(), s))
)
or
exists(RegExpNamedCharacterProperty charProp | child = charProp |
result = min(string s | namedCharacterPropertyMatches(charProp.getName(), s))
or
result = max(string s | namedCharacterPropertyMatches(charProp.getName(), s))
result = max(string s | classEscapeMatches(charClass.toLowerCase(), s))
)
)
}
@@ -480,60 +462,40 @@ private module CharacterClasses {
char = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_".charAt(_)
}
/**
* Holds if the named character property (e.g. from a POSIX bracket
* expression) `propName` matches `char`. For example, it holds when `name` is
* `"word"` and `char` is `"a"`.
*
* TODO: expand to cover more properties.
*/
private predicate namedCharacterPropertyMatches(string propName, string char) {
propName = ["digit", "Digit"] and
char = "0123456789".charAt(_)
or
propName = ["space", "Space"] and
(
char = [" ", "\t", "\r", "\n"]
or
char = getARelevantChar() and
char.regexpMatch("\\u000b|\\u000c") // \v|\f (vertical tab | form feed)
)
or
propName = ["word", "Word"] and
char = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_".charAt(_)
}
/**
* An implementation of `CharacterClass` for \d, \s, and \w.
*/
private class PositiveCharacterClassEscape extends CharacterClass {
RegExpCharacterClassEscape cc;
RegExpTerm cc;
string charClass;
PositiveCharacterClassEscape() {
this = getCanonicalCharClass(cc) and cc.getValue() = ["d", "s", "w"]
isEscapeClass(cc, charClass) and
this = getCanonicalCharClass(cc) and
charClass = ["d", "s", "w"]
}
override string getARelevantChar() {
cc.getValue() = "d" and
charClass = "d" and
result = ["0", "9"]
or
cc.getValue() = "s" and
charClass = "s" and
result = " "
or
cc.getValue() = "w" and
charClass = "w" and
result = ["a", "Z", "_", "0", "9"]
}
override predicate matches(string char) { classEscapeMatches(cc.getValue(), char) }
override predicate matches(string char) { classEscapeMatches(charClass, char) }
override string choose() {
cc.getValue() = "d" and
charClass = "d" and
result = "9"
or
cc.getValue() = "s" and
charClass = "s" and
result = " "
or
cc.getValue() = "w" and
charClass = "w" and
result = "a"
}
}
@@ -542,88 +504,29 @@ private module CharacterClasses {
* An implementation of `CharacterClass` for \D, \S, and \W.
*/
private class NegativeCharacterClassEscape extends CharacterClass {
RegExpCharacterClassEscape cc;
RegExpTerm cc;
string charClass;
NegativeCharacterClassEscape() {
this = getCanonicalCharClass(cc) and cc.getValue() = ["D", "S", "W"]
isEscapeClass(cc, charClass) and
this = getCanonicalCharClass(cc) and
charClass = ["D", "S", "W"]
}
override string getARelevantChar() {
cc.getValue() = "D" and
charClass = "D" and
result = ["a", "Z", "!"]
or
cc.getValue() = "S" and
charClass = "S" and
result = ["a", "9", "!"]
or
cc.getValue() = "W" and
charClass = "W" and
result = [" ", "!"]
}
bindingset[char]
override predicate matches(string char) {
not classEscapeMatches(cc.getValue().toLowerCase(), char)
}
}
/**
* An implementation of `NamedCharacterProperty` for positive (non-inverted)
* character properties.
*/
private class PositiveNamedCharacterProperty extends CharacterClass {
RegExpNamedCharacterProperty cp;
PositiveNamedCharacterProperty() { this = getCanonicalCharClass(cp) and not cp.isInverted() }
override string getARelevantChar() {
exists(string lowerName | lowerName = cp.getName().toLowerCase() |
lowerName = "digit" and
result = ["0", "9"]
or
lowerName = "space" and
result = [" "]
or
lowerName = "word" and
result = ["a", "Z", "_", "0", "9"]
)
}
override predicate matches(string char) { namedCharacterPropertyMatches(cp.getName(), char) }
override string choose() {
exists(string lowerName | lowerName = cp.getName().toLowerCase() |
lowerName = "digit" and
result = "9"
or
lowerName = "space" and
result = " "
or
lowerName = "word" and
result = "a"
)
}
}
private class InvertedNamedCharacterProperty extends CharacterClass {
RegExpNamedCharacterProperty cp;
InvertedNamedCharacterProperty() { this = getCanonicalCharClass(cp) and cp.isInverted() }
override string getARelevantChar() {
exists(string lowerName | lowerName = cp.getName().toLowerCase() |
lowerName = "digit" and
result = ["a", "Z", "!"]
or
lowerName = "space" and
result = ["a", "9", "!"]
or
lowerName = "word" and
result = [" ", "!"]
)
}
bindingset[char]
override predicate matches(string char) {
not namedCharacterPropertyMatches(cp.getName(), char)
not classEscapeMatches(charClass.toLowerCase(), char)
}
}
}
@@ -702,18 +605,12 @@ predicate delta(State q1, EdgeLabel lbl, State q2) {
q2 = after(cc)
)
or
exists(RegExpCharacterClassEscape cc |
exists(RegExpTerm cc | isEscapeClass(cc, _) |
q1 = before(cc) and
lbl = CharClass(cc.getRawValue() + "|" + getCanonicalizationFlags(cc.getRootTerm())) and
q2 = after(cc)
)
or
exists(RegExpNamedCharacterProperty cp |
q1 = before(cp) and
lbl = CharClass(cp.getRawValue()) and
q2 = after(cp)
)
or
exists(RegExpAlt alt | lbl = Epsilon() | q1 = before(alt) and q2 = before(alt.getAChild()))
or
exists(RegExpSequence seq | lbl = Epsilon() | q1 = before(seq) and q2 = before(seq.getChild(0)))

View File

@@ -0,0 +1,85 @@
/**
* Provides default sources, sinks and sanitizers for reasoning about regexp
* injection vulnerabilities, as well as extension points for adding your own.
*/
private import ruby
private import codeql.ruby.DataFlow
private import codeql.ruby.Concepts
private import codeql.ruby.Frameworks
private import codeql.ruby.dataflow.RemoteFlowSources
private import codeql.ruby.dataflow.BarrierGuards
private import codeql.ruby.ApiGraphs
/**
* Provides default sources, sinks and sanitizers for detecting
* regexp injection vulnerabilities, as well as extension points for
* adding your own.
*/
module RegExpInjection {
/**
* A data flow source for regexp injection vulnerabilities.
*/
abstract class Source extends DataFlow::Node { }
/**
* A data flow sink for regexp injection vulnerabilities.
*/
abstract class Sink extends DataFlow::Node { }
/**
* A sanitizer guard for regexp injection vulnerabilities.
*/
abstract class SanitizerGuard extends DataFlow::BarrierGuard { }
/**
* A data flow sanitized for regexp injection vulnerabilities.
*/
abstract class Sanitizer extends DataFlow::Node { }
/**
* A source of remote user input, considered as a flow source.
*/
class RemoteFlowSourceAsSource extends Source, RemoteFlowSource { }
/** A regexp literal, considered as a flow sink. */
class RegExpLiteralAsSink extends Sink {
RegExpLiteralAsSink() { this.asExpr().getExpr() instanceof RegExpLiteral }
}
/**
* The first argument of a call to `Regexp.new` or `Regexp.compile`,
* considered as a flow sink.
*/
class ConstructedRegExpAsSink extends Sink {
ConstructedRegExpAsSink() {
exists(API::Node regexp, DataFlow::CallNode callNode |
regexp = API::getTopLevelMember("Regexp") and
(callNode = regexp.getAnInstantiation() or callNode = regexp.getAMethodCall("compile")) and
this = callNode.getArgument(0)
)
}
}
/**
* A comparison with a constant string, considered as a sanitizer-guard.
*/
class StringConstCompareAsSanitizerGuard extends SanitizerGuard, StringConstCompare { }
/**
* An inclusion check against an array of constant strings, considered as a
* sanitizer-guard.
*/
class StringConstArrayInclusionCallAsSanitizerGuard extends SanitizerGuard,
StringConstArrayInclusionCall { }
/**
* A call to `Regexp.escape` (or its alias, `Regexp.quote`), considered as a
* sanitizer.
*/
class RegexpEscapeSanitization extends Sanitizer {
RegexpEscapeSanitization() {
this = API::getTopLevelMember("Regexp").getAMethodCall(["escape", "quote"])
}
}
}

View File

@@ -0,0 +1,29 @@
/**
* Provides a taint-tracking configuration for detecting regexp injection vulnerabilities.
*
* Note, for performance reasons: only import this file if `Configuration` is needed,
* otherwise `RegExpInjectionCustomizations` should be imported instead.
*/
import codeql.ruby.DataFlow::DataFlow::PathGraph
import codeql.ruby.DataFlow
import codeql.ruby.TaintTracking
import RegExpInjectionCustomizations
import codeql.ruby.dataflow.BarrierGuards
/**
* A taint-tracking configuration for detecting regexp injection vulnerabilities.
*/
class Configuration extends TaintTracking::Configuration {
Configuration() { this = "RegExpInjection" }
override predicate isSource(DataFlow::Node source) { source instanceof RegExpInjection::Source }
override predicate isSink(DataFlow::Node sink) { sink instanceof RegExpInjection::Sink }
override predicate isSanitizerGuard(DataFlow::BarrierGuard guard) {
guard instanceof RegExpInjection::SanitizerGuard
}
override predicate isSanitizer(DataFlow::Node node) { node instanceof RegExpInjection::Sanitizer }
}

View File

@@ -1,6 +1,27 @@
private import codeql.ruby.ast.Literal as AST
private import codeql.Locations
private import ParseRegExp
import codeql.Locations
/**
* Holds if `term` is an ecape class representing e.g. `\d`.
* `clazz` is which character class it represents, e.g. "d" for `\d`.
*/
predicate isEscapeClass(RegExpTerm term, string clazz) {
exists(RegExpCharacterClassEscape escape | term = escape | escape.getValue() = clazz)
or
// TODO: expand to cover more properties
exists(RegExpNamedCharacterProperty escape | term = escape |
escape.getName().toLowerCase() = "digit" and
if escape.isInverted() then clazz = "D" else clazz = "d"
or
escape.getName().toLowerCase() = "space" and
if escape.isInverted() then clazz = "S" else clazz = "s"
or
escape.getName().toLowerCase() = "word" and
if escape.isInverted() then clazz = "W" else clazz = "w"
)
}
/**
* Holds if the regular expression should not be considered.

View File

@@ -52,13 +52,27 @@ case @diagnostic.severity of
@ruby_underscore_arg = @ruby_assignment | @ruby_binary | @ruby_conditional | @ruby_operator_assignment | @ruby_range | @ruby_unary | @ruby_underscore_primary
@ruby_underscore_expression = @ruby_assignment | @ruby_binary | @ruby_break | @ruby_call | @ruby_next | @ruby_operator_assignment | @ruby_return | @ruby_unary | @ruby_underscore_arg | @ruby_yield
@ruby_underscore_lhs = @ruby_call | @ruby_element_reference | @ruby_scope_resolution | @ruby_token_false | @ruby_token_nil | @ruby_token_true | @ruby_underscore_variable
@ruby_underscore_method_name = @ruby_delimited_symbol | @ruby_setter | @ruby_token_class_variable | @ruby_token_constant | @ruby_token_global_variable | @ruby_token_identifier | @ruby_token_instance_variable | @ruby_token_operator | @ruby_token_simple_symbol
@ruby_underscore_primary = @ruby_array | @ruby_begin | @ruby_break | @ruby_case__ | @ruby_chained_string | @ruby_class | @ruby_delimited_symbol | @ruby_for | @ruby_hash | @ruby_if | @ruby_lambda | @ruby_method | @ruby_module | @ruby_next | @ruby_parenthesized_statements | @ruby_rational | @ruby_redo | @ruby_regex | @ruby_retry | @ruby_return | @ruby_singleton_class | @ruby_singleton_method | @ruby_string__ | @ruby_string_array | @ruby_subshell | @ruby_symbol_array | @ruby_token_character | @ruby_token_complex | @ruby_token_float | @ruby_token_heredoc_beginning | @ruby_token_integer | @ruby_token_simple_symbol | @ruby_unary | @ruby_underscore_lhs | @ruby_unless | @ruby_until | @ruby_while | @ruby_yield
@ruby_underscore_pattern_constant = @ruby_scope_resolution | @ruby_token_constant
@ruby_underscore_statement = @ruby_alias | @ruby_assignment | @ruby_begin_block | @ruby_binary | @ruby_break | @ruby_call | @ruby_end_block | @ruby_if_modifier | @ruby_next | @ruby_operator_assignment | @ruby_rescue_modifier | @ruby_return | @ruby_unary | @ruby_undef | @ruby_underscore_arg | @ruby_unless_modifier | @ruby_until_modifier | @ruby_while_modifier | @ruby_yield
@ruby_underscore_pattern_expr = @ruby_alternative_pattern | @ruby_as_pattern | @ruby_underscore_pattern_expr_basic
@ruby_underscore_pattern_expr_basic = @ruby_array_pattern | @ruby_find_pattern | @ruby_hash_pattern | @ruby_range | @ruby_token_identifier | @ruby_underscore_pattern_constant | @ruby_underscore_pattern_primitive | @ruby_variable_reference_pattern
@ruby_underscore_pattern_primitive = @ruby_delimited_symbol | @ruby_lambda | @ruby_regex | @ruby_string__ | @ruby_string_array | @ruby_symbol_array | @ruby_token_encoding | @ruby_token_false | @ruby_token_file | @ruby_token_line | @ruby_token_nil | @ruby_token_self | @ruby_token_simple_symbol | @ruby_token_true | @ruby_unary | @ruby_underscore_simple_numeric
@ruby_underscore_pattern_top_expr_body = @ruby_array_pattern | @ruby_find_pattern | @ruby_hash_pattern | @ruby_underscore_pattern_expr
@ruby_underscore_primary = @ruby_array | @ruby_begin | @ruby_break | @ruby_case__ | @ruby_case_match | @ruby_chained_string | @ruby_class | @ruby_delimited_symbol | @ruby_for | @ruby_hash | @ruby_if | @ruby_lambda | @ruby_method | @ruby_module | @ruby_next | @ruby_parenthesized_statements | @ruby_redo | @ruby_regex | @ruby_retry | @ruby_return | @ruby_singleton_class | @ruby_singleton_method | @ruby_string__ | @ruby_string_array | @ruby_subshell | @ruby_symbol_array | @ruby_token_character | @ruby_token_heredoc_beginning | @ruby_token_simple_symbol | @ruby_unary | @ruby_underscore_lhs | @ruby_underscore_simple_numeric | @ruby_unless | @ruby_until | @ruby_while | @ruby_yield
@ruby_underscore_simple_numeric = @ruby_rational | @ruby_token_complex | @ruby_token_float | @ruby_token_integer
@ruby_underscore_statement = @ruby_alias | @ruby_begin_block | @ruby_end_block | @ruby_if_modifier | @ruby_rescue_modifier | @ruby_undef | @ruby_underscore_expression | @ruby_unless_modifier | @ruby_until_modifier | @ruby_while_modifier
@ruby_underscore_variable = @ruby_token_class_variable | @ruby_token_constant | @ruby_token_global_variable | @ruby_token_identifier | @ruby_token_instance_variable | @ruby_token_self | @ruby_token_super
@@ -69,7 +83,19 @@ ruby_alias_def(
int loc: @location ref
);
@ruby_argument_list_child_type = @ruby_block_argument | @ruby_break | @ruby_call | @ruby_hash_splat_argument | @ruby_next | @ruby_pair | @ruby_return | @ruby_splat_argument | @ruby_token_forward_argument | @ruby_underscore_arg | @ruby_yield
#keyset[ruby_alternative_pattern, index]
ruby_alternative_pattern_alternatives(
int ruby_alternative_pattern: @ruby_alternative_pattern ref,
int index: int ref,
unique int alternatives: @ruby_underscore_pattern_expr_basic ref
);
ruby_alternative_pattern_def(
unique int id: @ruby_alternative_pattern,
int loc: @location ref
);
@ruby_argument_list_child_type = @ruby_block_argument | @ruby_hash_splat_argument | @ruby_pair | @ruby_splat_argument | @ruby_token_forward_argument | @ruby_underscore_expression
#keyset[ruby_argument_list, index]
ruby_argument_list_child(
@@ -83,7 +109,7 @@ ruby_argument_list_def(
int loc: @location ref
);
@ruby_array_child_type = @ruby_block_argument | @ruby_break | @ruby_call | @ruby_hash_splat_argument | @ruby_next | @ruby_pair | @ruby_return | @ruby_splat_argument | @ruby_token_forward_argument | @ruby_underscore_arg | @ruby_yield
@ruby_array_child_type = @ruby_block_argument | @ruby_hash_splat_argument | @ruby_pair | @ruby_splat_argument | @ruby_token_forward_argument | @ruby_underscore_expression
#keyset[ruby_array, index]
ruby_array_child(
@@ -97,9 +123,35 @@ ruby_array_def(
int loc: @location ref
);
ruby_array_pattern_class(
unique int ruby_array_pattern: @ruby_array_pattern ref,
unique int class: @ruby_underscore_pattern_constant ref
);
@ruby_array_pattern_child_type = @ruby_splat_parameter | @ruby_underscore_pattern_expr
#keyset[ruby_array_pattern, index]
ruby_array_pattern_child(
int ruby_array_pattern: @ruby_array_pattern ref,
int index: int ref,
unique int child: @ruby_array_pattern_child_type ref
);
ruby_array_pattern_def(
unique int id: @ruby_array_pattern,
int loc: @location ref
);
ruby_as_pattern_def(
unique int id: @ruby_as_pattern,
int name: @ruby_token_identifier ref,
int value: @ruby_underscore_pattern_expr ref,
int loc: @location ref
);
@ruby_assignment_left_type = @ruby_left_assignment_list | @ruby_underscore_lhs
@ruby_assignment_right_type = @ruby_break | @ruby_call | @ruby_next | @ruby_return | @ruby_right_assignment_list | @ruby_splat_argument | @ruby_underscore_arg | @ruby_yield
@ruby_assignment_right_type = @ruby_right_assignment_list | @ruby_splat_argument | @ruby_underscore_expression
ruby_assignment_def(
unique int id: @ruby_assignment,
@@ -164,8 +216,6 @@ ruby_begin_block_def(
int loc: @location ref
);
@ruby_binary_left_type = @ruby_break | @ruby_call | @ruby_next | @ruby_return | @ruby_underscore_arg | @ruby_yield
case @ruby_binary.operator of
0 = @ruby_binary_bangequal
| 1 = @ruby_binary_bangtilde
@@ -195,13 +245,11 @@ case @ruby_binary.operator of
;
@ruby_binary_right_type = @ruby_break | @ruby_call | @ruby_next | @ruby_return | @ruby_underscore_arg | @ruby_yield
ruby_binary_def(
unique int id: @ruby_binary,
int left: @ruby_binary_left_type ref,
int left: @ruby_underscore_expression ref,
int operator: int ref,
int right: @ruby_binary_right_type ref,
int right: @ruby_underscore_expression ref,
int loc: @location ref
);
@@ -236,7 +284,7 @@ ruby_block_parameter_def(
int loc: @location ref
);
@ruby_block_parameters_child_type = @ruby_block_parameter | @ruby_destructured_parameter | @ruby_hash_splat_parameter | @ruby_keyword_parameter | @ruby_optional_parameter | @ruby_splat_parameter | @ruby_token_forward_parameter | @ruby_token_identifier
@ruby_block_parameters_child_type = @ruby_block_parameter | @ruby_destructured_parameter | @ruby_hash_splat_parameter | @ruby_keyword_parameter | @ruby_optional_parameter | @ruby_splat_parameter | @ruby_token_forward_parameter | @ruby_token_hash_splat_nil | @ruby_token_identifier
#keyset[ruby_block_parameters, index]
ruby_block_parameters_child(
@@ -306,6 +354,24 @@ ruby_case_def(
int loc: @location ref
);
#keyset[ruby_case_match, index]
ruby_case_match_clauses(
int ruby_case_match: @ruby_case_match ref,
int index: int ref,
unique int clauses: @ruby_in_clause ref
);
ruby_case_match_else(
unique int ruby_case_match: @ruby_case_match ref,
unique int else: @ruby_else ref
);
ruby_case_match_def(
unique int id: @ruby_case_match,
int value: @ruby_underscore_statement ref,
int loc: @location ref
);
#keyset[ruby_chained_string, index]
ruby_chained_string_child(
int ruby_chained_string: @ruby_chained_string ref,
@@ -376,7 +442,7 @@ ruby_destructured_left_assignment_def(
int loc: @location ref
);
@ruby_destructured_parameter_child_type = @ruby_block_parameter | @ruby_destructured_parameter | @ruby_hash_splat_parameter | @ruby_keyword_parameter | @ruby_optional_parameter | @ruby_splat_parameter | @ruby_token_forward_parameter | @ruby_token_identifier
@ruby_destructured_parameter_child_type = @ruby_block_parameter | @ruby_destructured_parameter | @ruby_hash_splat_parameter | @ruby_keyword_parameter | @ruby_optional_parameter | @ruby_splat_parameter | @ruby_token_forward_parameter | @ruby_token_hash_splat_nil | @ruby_token_identifier
#keyset[ruby_destructured_parameter, index]
ruby_destructured_parameter_child(
@@ -423,7 +489,7 @@ ruby_do_block_def(
int loc: @location ref
);
@ruby_element_reference_child_type = @ruby_block_argument | @ruby_break | @ruby_call | @ruby_hash_splat_argument | @ruby_next | @ruby_pair | @ruby_return | @ruby_splat_argument | @ruby_token_forward_argument | @ruby_underscore_arg | @ruby_yield
@ruby_element_reference_child_type = @ruby_block_argument | @ruby_hash_splat_argument | @ruby_pair | @ruby_splat_argument | @ruby_token_forward_argument | @ruby_underscore_expression
#keyset[ruby_element_reference, index]
ruby_element_reference_child(
@@ -518,6 +584,25 @@ ruby_exceptions_def(
int loc: @location ref
);
ruby_find_pattern_class(
unique int ruby_find_pattern: @ruby_find_pattern ref,
unique int class: @ruby_underscore_pattern_constant ref
);
@ruby_find_pattern_child_type = @ruby_splat_parameter | @ruby_underscore_pattern_expr
#keyset[ruby_find_pattern, index]
ruby_find_pattern_child(
int ruby_find_pattern: @ruby_find_pattern ref,
int index: int ref,
unique int child: @ruby_find_pattern_child_type ref
);
ruby_find_pattern_def(
unique int id: @ruby_find_pattern,
int loc: @location ref
);
@ruby_for_pattern_type = @ruby_left_assignment_list | @ruby_underscore_lhs
ruby_for_def(
@@ -542,6 +627,25 @@ ruby_hash_def(
int loc: @location ref
);
ruby_hash_pattern_class(
unique int ruby_hash_pattern: @ruby_hash_pattern ref,
unique int class: @ruby_underscore_pattern_constant ref
);
@ruby_hash_pattern_child_type = @ruby_hash_splat_parameter | @ruby_keyword_pattern | @ruby_token_hash_splat_nil
#keyset[ruby_hash_pattern, index]
ruby_hash_pattern_child(
int ruby_hash_pattern: @ruby_hash_pattern ref,
int index: int ref,
unique int child: @ruby_hash_pattern_child_type ref
);
ruby_hash_pattern_def(
unique int id: @ruby_hash_pattern,
int loc: @location ref
);
ruby_hash_splat_argument_def(
unique int id: @ruby_hash_splat_argument,
int child: @ruby_underscore_arg ref,
@@ -590,12 +694,16 @@ ruby_if_def(
int loc: @location ref
);
@ruby_if_modifier_condition_type = @ruby_break | @ruby_call | @ruby_next | @ruby_return | @ruby_underscore_arg | @ruby_yield
ruby_if_guard_def(
unique int id: @ruby_if_guard,
int condition: @ruby_underscore_expression ref,
int loc: @location ref
);
ruby_if_modifier_def(
unique int id: @ruby_if_modifier,
int body: @ruby_underscore_statement ref,
int condition: @ruby_if_modifier_condition_type ref,
int condition: @ruby_underscore_expression ref,
int loc: @location ref
);
@@ -605,6 +713,24 @@ ruby_in_def(
int loc: @location ref
);
ruby_in_clause_body(
unique int ruby_in_clause: @ruby_in_clause ref,
unique int body: @ruby_then ref
);
@ruby_in_clause_guard_type = @ruby_if_guard | @ruby_unless_guard
ruby_in_clause_guard(
unique int ruby_in_clause: @ruby_in_clause ref,
unique int guard: @ruby_in_clause_guard_type ref
);
ruby_in_clause_def(
unique int id: @ruby_in_clause,
int pattern: @ruby_underscore_pattern_top_expr_body ref,
int loc: @location ref
);
@ruby_interpolation_child_type = @ruby_token_empty_statement | @ruby_underscore_statement
#keyset[ruby_interpolation, index]
@@ -630,6 +756,19 @@ ruby_keyword_parameter_def(
int loc: @location ref
);
@ruby_keyword_pattern_key_type = @ruby_string__ | @ruby_token_hash_key_symbol
ruby_keyword_pattern_value(
unique int ruby_keyword_pattern: @ruby_keyword_pattern ref,
unique int value: @ruby_underscore_pattern_expr ref
);
ruby_keyword_pattern_def(
unique int id: @ruby_keyword_pattern,
int key__: @ruby_keyword_pattern_key_type ref,
int loc: @location ref
);
@ruby_lambda_body_type = @ruby_block | @ruby_do_block
ruby_lambda_parameters(
@@ -643,7 +782,7 @@ ruby_lambda_def(
int loc: @location ref
);
@ruby_lambda_parameters_child_type = @ruby_block_parameter | @ruby_destructured_parameter | @ruby_hash_splat_parameter | @ruby_keyword_parameter | @ruby_optional_parameter | @ruby_splat_parameter | @ruby_token_forward_parameter | @ruby_token_identifier
@ruby_lambda_parameters_child_type = @ruby_block_parameter | @ruby_destructured_parameter | @ruby_hash_splat_parameter | @ruby_keyword_parameter | @ruby_optional_parameter | @ruby_splat_parameter | @ruby_token_forward_parameter | @ruby_token_hash_splat_nil | @ruby_token_identifier
#keyset[ruby_lambda_parameters, index]
ruby_lambda_parameters_child(
@@ -676,7 +815,7 @@ ruby_method_parameters(
unique int parameters: @ruby_method_parameters ref
);
@ruby_method_child_type = @ruby_else | @ruby_ensure | @ruby_rescue | @ruby_token_empty_statement | @ruby_underscore_statement
@ruby_method_child_type = @ruby_else | @ruby_ensure | @ruby_rescue | @ruby_token_empty_statement | @ruby_underscore_arg | @ruby_underscore_statement
#keyset[ruby_method, index]
ruby_method_child(
@@ -691,7 +830,7 @@ ruby_method_def(
int loc: @location ref
);
@ruby_method_parameters_child_type = @ruby_block_parameter | @ruby_destructured_parameter | @ruby_hash_splat_parameter | @ruby_keyword_parameter | @ruby_optional_parameter | @ruby_splat_parameter | @ruby_token_forward_parameter | @ruby_token_identifier
@ruby_method_parameters_child_type = @ruby_block_parameter | @ruby_destructured_parameter | @ruby_hash_splat_parameter | @ruby_keyword_parameter | @ruby_optional_parameter | @ruby_splat_parameter | @ruby_token_forward_parameter | @ruby_token_hash_splat_nil | @ruby_token_identifier
#keyset[ruby_method_parameters, index]
ruby_method_parameters_child(
@@ -749,13 +888,11 @@ case @ruby_operator_assignment.operator of
;
@ruby_operator_assignment_right_type = @ruby_break | @ruby_call | @ruby_next | @ruby_return | @ruby_underscore_arg | @ruby_yield
ruby_operator_assignment_def(
unique int id: @ruby_operator_assignment,
int left: @ruby_underscore_lhs ref,
int operator: int ref,
int right: @ruby_operator_assignment_right_type ref,
int right: @ruby_underscore_expression ref,
int loc: @location ref
);
@@ -811,14 +948,18 @@ ruby_program_def(
int loc: @location ref
);
@ruby_range_begin_type = @ruby_underscore_arg | @ruby_underscore_pattern_primitive
ruby_range_begin(
unique int ruby_range: @ruby_range ref,
unique int begin: @ruby_underscore_arg ref
unique int begin: @ruby_range_begin_type ref
);
@ruby_range_end_type = @ruby_underscore_arg | @ruby_underscore_pattern_primitive
ruby_range_end(
unique int ruby_range: @ruby_range ref,
unique int end: @ruby_underscore_arg ref
unique int end: @ruby_range_end_type ref
);
case @ruby_range.operator of
@@ -885,12 +1026,12 @@ ruby_rescue_def(
int loc: @location ref
);
@ruby_rescue_modifier_handler_type = @ruby_break | @ruby_call | @ruby_next | @ruby_return | @ruby_underscore_arg | @ruby_yield
@ruby_rescue_modifier_body_type = @ruby_underscore_arg | @ruby_underscore_statement
ruby_rescue_modifier_def(
unique int id: @ruby_rescue_modifier,
int body: @ruby_underscore_statement ref,
int handler: @ruby_rescue_modifier_handler_type ref,
int body: @ruby_rescue_modifier_body_type ref,
int handler: @ruby_underscore_expression ref,
int loc: @location ref
);
@@ -940,9 +1081,11 @@ ruby_right_assignment_list_def(
@ruby_scope_resolution_name_type = @ruby_token_constant | @ruby_token_identifier
@ruby_scope_resolution_scope_type = @ruby_underscore_pattern_constant | @ruby_underscore_primary
ruby_scope_resolution_scope(
unique int ruby_scope_resolution: @ruby_scope_resolution ref,
unique int scope: @ruby_underscore_primary ref
unique int scope: @ruby_scope_resolution_scope_type ref
);
ruby_scope_resolution_def(
@@ -979,7 +1122,7 @@ ruby_singleton_method_parameters(
unique int parameters: @ruby_method_parameters ref
);
@ruby_singleton_method_child_type = @ruby_else | @ruby_ensure | @ruby_rescue | @ruby_token_empty_statement | @ruby_underscore_statement
@ruby_singleton_method_child_type = @ruby_else | @ruby_ensure | @ruby_rescue | @ruby_token_empty_statement | @ruby_underscore_arg | @ruby_underscore_statement
#keyset[ruby_singleton_method, index]
ruby_singleton_method_child(
@@ -1051,11 +1194,9 @@ ruby_subshell_def(
int loc: @location ref
);
@ruby_superclass_child_type = @ruby_break | @ruby_call | @ruby_next | @ruby_return | @ruby_underscore_arg | @ruby_yield
ruby_superclass_def(
unique int id: @ruby_superclass,
int child: @ruby_superclass_child_type ref,
int child: @ruby_underscore_expression ref,
int loc: @location ref
);
@@ -1085,7 +1226,7 @@ ruby_then_def(
int loc: @location ref
);
@ruby_unary_operand_type = @ruby_break | @ruby_call | @ruby_next | @ruby_parenthesized_statements | @ruby_return | @ruby_token_float | @ruby_token_integer | @ruby_underscore_arg | @ruby_yield
@ruby_unary_operand_type = @ruby_parenthesized_statements | @ruby_underscore_expression | @ruby_underscore_simple_numeric
case @ruby_unary.operator of
0 = @ruby_unary_bang
@@ -1134,12 +1275,16 @@ ruby_unless_def(
int loc: @location ref
);
@ruby_unless_modifier_condition_type = @ruby_break | @ruby_call | @ruby_next | @ruby_return | @ruby_underscore_arg | @ruby_yield
ruby_unless_guard_def(
unique int id: @ruby_unless_guard,
int condition: @ruby_underscore_expression ref,
int loc: @location ref
);
ruby_unless_modifier_def(
unique int id: @ruby_unless_modifier,
int body: @ruby_underscore_statement ref,
int condition: @ruby_unless_modifier_condition_type ref,
int condition: @ruby_underscore_expression ref,
int loc: @location ref
);
@@ -1150,12 +1295,16 @@ ruby_until_def(
int loc: @location ref
);
@ruby_until_modifier_condition_type = @ruby_break | @ruby_call | @ruby_next | @ruby_return | @ruby_underscore_arg | @ruby_yield
ruby_until_modifier_def(
unique int id: @ruby_until_modifier,
int body: @ruby_underscore_statement ref,
int condition: @ruby_until_modifier_condition_type ref,
int condition: @ruby_underscore_expression ref,
int loc: @location ref
);
ruby_variable_reference_pattern_def(
unique int id: @ruby_variable_reference_pattern,
int name: @ruby_token_identifier ref,
int loc: @location ref
);
@@ -1183,12 +1332,10 @@ ruby_while_def(
int loc: @location ref
);
@ruby_while_modifier_condition_type = @ruby_break | @ruby_call | @ruby_next | @ruby_return | @ruby_underscore_arg | @ruby_yield
ruby_while_modifier_def(
unique int id: @ruby_while_modifier,
int body: @ruby_underscore_statement ref,
int condition: @ruby_while_modifier_condition_type ref,
int condition: @ruby_underscore_expression ref,
int loc: @location ref
);
@@ -1217,31 +1364,35 @@ case @ruby_token.kind of
| 4 = @ruby_token_complex
| 5 = @ruby_token_constant
| 6 = @ruby_token_empty_statement
| 7 = @ruby_token_escape_sequence
| 8 = @ruby_token_false
| 9 = @ruby_token_float
| 10 = @ruby_token_forward_argument
| 11 = @ruby_token_forward_parameter
| 12 = @ruby_token_global_variable
| 13 = @ruby_token_hash_key_symbol
| 14 = @ruby_token_heredoc_beginning
| 15 = @ruby_token_heredoc_content
| 16 = @ruby_token_heredoc_end
| 17 = @ruby_token_identifier
| 18 = @ruby_token_instance_variable
| 19 = @ruby_token_integer
| 20 = @ruby_token_nil
| 21 = @ruby_token_operator
| 22 = @ruby_token_self
| 23 = @ruby_token_simple_symbol
| 24 = @ruby_token_string_content
| 25 = @ruby_token_super
| 26 = @ruby_token_true
| 27 = @ruby_token_uninterpreted
| 7 = @ruby_token_encoding
| 8 = @ruby_token_escape_sequence
| 9 = @ruby_token_false
| 10 = @ruby_token_file
| 11 = @ruby_token_float
| 12 = @ruby_token_forward_argument
| 13 = @ruby_token_forward_parameter
| 14 = @ruby_token_global_variable
| 15 = @ruby_token_hash_key_symbol
| 16 = @ruby_token_hash_splat_nil
| 17 = @ruby_token_heredoc_beginning
| 18 = @ruby_token_heredoc_content
| 19 = @ruby_token_heredoc_end
| 20 = @ruby_token_identifier
| 21 = @ruby_token_instance_variable
| 22 = @ruby_token_integer
| 23 = @ruby_token_line
| 24 = @ruby_token_nil
| 25 = @ruby_token_operator
| 26 = @ruby_token_self
| 27 = @ruby_token_simple_symbol
| 28 = @ruby_token_string_content
| 29 = @ruby_token_super
| 30 = @ruby_token_true
| 31 = @ruby_token_uninterpreted
;
@ruby_ast_node = @ruby_alias | @ruby_argument_list | @ruby_array | @ruby_assignment | @ruby_bare_string | @ruby_bare_symbol | @ruby_begin | @ruby_begin_block | @ruby_binary | @ruby_block | @ruby_block_argument | @ruby_block_parameter | @ruby_block_parameters | @ruby_break | @ruby_call | @ruby_case__ | @ruby_chained_string | @ruby_class | @ruby_conditional | @ruby_delimited_symbol | @ruby_destructured_left_assignment | @ruby_destructured_parameter | @ruby_do | @ruby_do_block | @ruby_element_reference | @ruby_else | @ruby_elsif | @ruby_end_block | @ruby_ensure | @ruby_exception_variable | @ruby_exceptions | @ruby_for | @ruby_hash | @ruby_hash_splat_argument | @ruby_hash_splat_parameter | @ruby_heredoc_body | @ruby_if | @ruby_if_modifier | @ruby_in | @ruby_interpolation | @ruby_keyword_parameter | @ruby_lambda | @ruby_lambda_parameters | @ruby_left_assignment_list | @ruby_method | @ruby_method_parameters | @ruby_module | @ruby_next | @ruby_operator_assignment | @ruby_optional_parameter | @ruby_pair | @ruby_parenthesized_statements | @ruby_pattern | @ruby_program | @ruby_range | @ruby_rational | @ruby_redo | @ruby_regex | @ruby_rescue | @ruby_rescue_modifier | @ruby_rest_assignment | @ruby_retry | @ruby_return | @ruby_right_assignment_list | @ruby_scope_resolution | @ruby_setter | @ruby_singleton_class | @ruby_singleton_method | @ruby_splat_argument | @ruby_splat_parameter | @ruby_string__ | @ruby_string_array | @ruby_subshell | @ruby_superclass | @ruby_symbol_array | @ruby_then | @ruby_token | @ruby_unary | @ruby_undef | @ruby_unless | @ruby_unless_modifier | @ruby_until | @ruby_until_modifier | @ruby_when | @ruby_while | @ruby_while_modifier | @ruby_yield
@ruby_ast_node = @ruby_alias | @ruby_alternative_pattern | @ruby_argument_list | @ruby_array | @ruby_array_pattern | @ruby_as_pattern | @ruby_assignment | @ruby_bare_string | @ruby_bare_symbol | @ruby_begin | @ruby_begin_block | @ruby_binary | @ruby_block | @ruby_block_argument | @ruby_block_parameter | @ruby_block_parameters | @ruby_break | @ruby_call | @ruby_case__ | @ruby_case_match | @ruby_chained_string | @ruby_class | @ruby_conditional | @ruby_delimited_symbol | @ruby_destructured_left_assignment | @ruby_destructured_parameter | @ruby_do | @ruby_do_block | @ruby_element_reference | @ruby_else | @ruby_elsif | @ruby_end_block | @ruby_ensure | @ruby_exception_variable | @ruby_exceptions | @ruby_find_pattern | @ruby_for | @ruby_hash | @ruby_hash_pattern | @ruby_hash_splat_argument | @ruby_hash_splat_parameter | @ruby_heredoc_body | @ruby_if | @ruby_if_guard | @ruby_if_modifier | @ruby_in | @ruby_in_clause | @ruby_interpolation | @ruby_keyword_parameter | @ruby_keyword_pattern | @ruby_lambda | @ruby_lambda_parameters | @ruby_left_assignment_list | @ruby_method | @ruby_method_parameters | @ruby_module | @ruby_next | @ruby_operator_assignment | @ruby_optional_parameter | @ruby_pair | @ruby_parenthesized_statements | @ruby_pattern | @ruby_program | @ruby_range | @ruby_rational | @ruby_redo | @ruby_regex | @ruby_rescue | @ruby_rescue_modifier | @ruby_rest_assignment | @ruby_retry | @ruby_return | @ruby_right_assignment_list | @ruby_scope_resolution | @ruby_setter | @ruby_singleton_class | @ruby_singleton_method | @ruby_splat_argument | @ruby_splat_parameter | @ruby_string__ | @ruby_string_array | @ruby_subshell | @ruby_superclass | @ruby_symbol_array | @ruby_then | @ruby_token | @ruby_unary | @ruby_undef | @ruby_unless | @ruby_unless_guard | @ruby_unless_modifier | @ruby_until | @ruby_until_modifier | @ruby_variable_reference_pattern | @ruby_when | @ruby_while | @ruby_while_modifier | @ruby_yield
@ruby_ast_node_parent = @file | @ruby_ast_node

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,79 @@
private class RubyToken extends @ruby_token {
string toString() { none() }
}
private class Location extends @location {
string toString() { none() }
}
bindingset[old]
private int newKind(int old) {
old in [0 .. 6] and result = old
or
// @ruby_token_escape_sequence
old = 7 and result = 8
or
// @ruby_token_false
old = 8 and result = 9
or
// @ruby_token_float
old = 9 and result = 11
or
// @ruby_token_forward_argument
old = 10 and result = 12
or
// @ruby_token_forward_parameter
old = 11 and result = 13
or
// @ruby_token_global_variable
old = 12 and result = 14
or
// @ruby_token_hash_key_symbol
old = 13 and result = 15
or
// @ruby_token_heredoc_beginning
old = 14 and result = 17
or
// @ruby_token_heredoc_content
old = 15 and result = 18
or
// @ruby_token_heredoc_end
old = 16 and result = 19
or
// @ruby_token_identifier
old = 17 and result = 20
or
// @ruby_token_instance_variable
old = 18 and result = 21
or
// @ruby_token_integer
old = 19 and result = 22
or
// @ruby_token_nil
old = 20 and result = 24
or
// @ruby_token_operator
old = 21 and result = 25
or
// @ruby_token_self
old = 22 and result = 26
or
// @ruby_token_simple_symbol
old = 23 and result = 27
or
// @ruby_token_string_content
old = 24 and result = 28
or
// @ruby_token_super
old = 25 and result = 29
or
// @ruby_token_true
old = 26 and result = 30
or
// @ruby_token_uninterpreted
old = 27 and result = 31
}
from RubyToken id, int kind, string value, Location loc
where ruby_tokeninfo(id, kind, value, loc)
select id, newKind(kind), value, loc

View File

@@ -0,0 +1,3 @@
description: Re-number @ruby_token.kind
compatibility: full
ruby_tokeninfo.rel: run ruby_tokeninfo.qlo

View File

@@ -17,7 +17,7 @@ private import codeql.ruby.printAst
external string selectedSourceFile();
/**
* Overrides the configuration to print only nodes in the selected source file.
* A configuration that only prints nodes in the selected source file.
*/
class Cfg extends PrintAstConfiguration {
override predicate shouldPrintNode(AstNode n) {

View File

@@ -23,7 +23,7 @@ import codeql.ruby.dataflow.RemoteFlowSources
import DataFlow::PathGraph
/**
* Method calls that have a suggested replacement.
* A method call that has a suggested replacement.
*/
abstract class Replacement extends DataFlow::CallNode {
abstract string getFrom();

View File

@@ -9,7 +9,6 @@
* @id rb/sql-injection
* @tags security
* external/cwe/cwe-089
* external/owasp/owasp-a1
*/
import ruby

View File

@@ -9,7 +9,6 @@
* @precision high
* @id rb/code-injection
* @tags security
* external/owasp/owasp-a1
* external/cwe/cwe-094
* external/cwe/cwe-095
* external/cwe/cwe-116

View File

@@ -0,0 +1,49 @@
<!DOCTYPE qhelp PUBLIC
"-//Semmle//qhelp//EN"
"qhelp.dtd">
<qhelp>
<overview>
<p>
Constructing a regular expression with unsanitized user input is dangerous,
since a malicious user may be able to modify the meaning of the expression. In
particular, such a user may be able to provide a regular expression fragment
that takes exponential time in the worst case, and use that to perform a Denial
of Service attack.
</p>
</overview>
<recommendation>
<p>
Before embedding user input into a regular expression, use a sanitization
function such as <code>Regexp.escape</code> to escape meta-characters that have
special meaning.
</p>
</recommendation>
<example>
<p>
The following examples construct regular expressions from an HTTP request
parameter without sanitizing it first:
</p>
<sample src="examples/regexp_injection_bad.rb" />
<p>
Instead, the request parameter should be sanitized first. This ensures that the
user cannot insert characters that have special meanings in regular expressions.
</p>
<sample src="examples/regexp_injection_good.rb" />
</example>
<references>
<li>
OWASP:
<a href="https://www.owasp.org/index.php/Regular_expression_Denial_of_Service_-_ReDoS">Regular expression Denial of Service - ReDoS</a>.
</li>
<li>
Wikipedia: <a href="https://en.wikipedia.org/wiki/ReDoS">ReDoS</a>.
</li>
<li>
Ruby: <a href="https://ruby-doc.org/core-3.0.2/Regexp.html#method-c-escape">Regexp.escape</a>.
</li>
</references>
</qhelp>

View File

@@ -0,0 +1,26 @@
/**
* @name Regular expression injection
* @description User input should not be used in regular expressions without
* first being escaped. Otherwise, a malicious user may be able to
* inject an expression that could require exponential time on
* certain inputs.
* @kind path-problem
* @problem.severity error
* @security-severity 7.5
* @precision high
* @id rb/regexp-injection
* @tags security
* external/cwe/cwe-1333
* external/cwe/cwe-730
* external/cwe/cwe-400
*/
import ruby
import DataFlow::PathGraph
import codeql.ruby.DataFlow
import codeql.ruby.security.performance.RegExpInjectionQuery
from Configuration cfg, DataFlow::PathNode source, DataFlow::PathNode sink
where cfg.hasFlowPath(source, sink)
select sink.getNode(), source, sink, "This regular expression is constructed from a $@.",
source.getNode(), "user-provided value"

View File

@@ -0,0 +1,11 @@
class UsersController < ActionController::Base
def first_example
# BAD: Unsanitized user input is used to construct a regular expression
regex = /#{ params[:key] }/
end
def second_example
# BAD: Unsanitized user input is used to construct a regular expression
regex = Regexp.new(params[:key])
end
end

View File

@@ -0,0 +1,6 @@
class UsersController < ActionController::Base
def example
# GOOD: User input is sanitized before constructing the regular expression
regex = Regexp.new(Regex.escape(params[:key]))
end
end

View File

@@ -0,0 +1,61 @@
<!DOCTYPE qhelp PUBLIC
"-//Semmle//qhelp//EN"
"qhelp.dtd">
<qhelp>
<overview>
<p>
Cross-site request forgery (CSRF) is a type of vulnerability in which an
attacker is able to force a user carry out an action that the user did
not intend.
</p>
<p>
The attacker tricks an authenticated user into submitting a request to the
web application. Typically this request will result in a state change on
the server, such as changing the user's password. The request can be
initiated when the user visits a site controlled by the attacker. If the
web application relies only on cookies for authentication, or on other
credentials that are automatically included in the request, then this
request will appear as legitimate to the server.
</p>
<p>
A common countermeasure for CSRF is to generate a unique token to be
included in the HTML sent from the server to a user. This token can be
used as a hidden field to be sent back with requests to the server, where
the server can then check that the token is valid and associated with the
relevant user session.
</p>
</overview>
<recommendation>
<p>
In many web frameworks, CSRF protection is enabled by default. In these
cases, using the default configuration is sufficient to guard against most
CSRF attacks.
</p>
</recommendation>
<example>
<p>
The following example shows a case where CSRF protection is disabled by
skipping token verification.
</p>
<sample src="examples/UsersController.rb"/>
<p>
Verification can be re-enabled by removing the call to
<code>skip_before_action</code>.
</p>
</example>
<references>
<li>Wikipedia: <a href="https://en.wikipedia.org/wiki/Cross-site_request_forgery">Cross-site request forgery</a></li>
<li>OWASP: <a href="https://owasp.org/www-community/attacks/csrf">Cross-site request forgery</a></li>
<li>Securing Rails Applications: <a href="https://guides.rubyonrails.org/security.html#cross-site-request-forgery-csrf">Cross-Site Request Forgery (CSRF)</a></li>
</references>
</qhelp>

View File

@@ -0,0 +1,19 @@
/**
* @name CSRF protection disabled
* @description Disabling CSRF protection makes the application vulnerable to
* a Cross-Site Request Forgery (CSRF) attack.
* @kind problem
* @problem.severity warning
* @security-severity 8.8
* @precision high
* @id rb/csrf-protection-disabled
* @tags security
* external/cwe/cwe-352
*/
import ruby
import codeql.ruby.Concepts
from CSRFProtectionSetting s
where s.getVerificationSetting() = false
select s, "Potential CSRF vulnerability due to forgery protection being disabled."

View File

@@ -0,0 +1,3 @@
class UsersController < ApplicationController
skip_before_action :verify_authenticity_token
end

View File

@@ -0,0 +1,41 @@
<!DOCTYPE qhelp PUBLIC
"-//Semmle//qhelp//EN"
"qhelp.dtd">
<qhelp>
<overview>
<p>Directly incorporating user input into an HTTP request without validating the input
can facilitate server-side request forgery (SSRF) attacks. In these attacks, the
request may be changed, directed at a different server, or via a different
protocol. This can allow the attacker to obtain sensitive information or perform
actions with escalated privilege.
</p>
</overview>
<recommendation>
<p>To guard against SSRF attacks you should avoid putting user-provided input
directly into a request URL. Instead, maintain a list of authorized
URLs on the server; then choose from that list based on the input provided.
Alternatively, ensure requests constructed from user input are limited to
a particular host or more restrictive URL prefix.</p>
</recommendation>
<example>
<p>The following example shows an HTTP request parameter being used directly to form a
new request without validating the input, which facilitates SSRF attacks.
It also shows how to remedy the problem by validating the user input against a known fixed string.
</p>
<sample src="ServerSideRequestForgery.rb" />
</example>
<references>
<li>
<a href="https://owasp.org/www-community/attacks/Server_Side_Request_Forgery">OWASP SSRF</a>
</li>
</references>
</qhelp>

View File

@@ -0,0 +1,20 @@
/**
* @name Uncontrolled data used in network request
* @description Making a network request with user-controlled data allows for request forgery attacks.
* @kind path-problem
* @problem.severity error
* @security-severity 9.1
* @precision high
* @id rb/request-forgery
* @tags security
* external/cwe/cwe-918
*/
import ruby
import codeql.ruby.DataFlow
import codeql.ruby.security.ServerSideRequestForgeryQuery
from Configuration config, DataFlow::PathNode source, DataFlow::PathNode sink
where config.hasFlowPath(source, sink)
select sink.getNode(), source, sink, "The URL of this request depends on $@.", source.getNode(),
"a user-provided value"

View File

@@ -0,0 +1,25 @@
require "excon"
require "json"
class PostsController < ActionController::Base
def create
user = params[:user_id]
# BAD - user can control the entire URL of the request
users_service_domain = params[:users_service_domain]
response = Excon.post("#{users_service_domain}/logins", body: {user_id: user}).body
token = JSON.parse(response)["token"]
# GOOD - path is validated against a known fixed string
path = if params[:users_service_path] == "v1/users"
"v1/users"
else
"v2/users"
end
response = Excon.post("users-service/#{path}", body: {user_id: user}).body
token = JSON.parse(response)["token"]
@post = Post.create(params[:post].merge(user_token: token))
render @post
end
end

View File

@@ -365,7 +365,6 @@ calls/calls.rb:
# 223| getReceiver: [ConstantReadAccess] X
# 226| getStmt: [ForExpr] for ... in ...
# 226| getPattern: [LocalVariableAccess] x
# 226| <in>: [???] In
# 226| getValue: [MethodCall] call to bar
# 226| getReceiver: [Self, SelfVariableAccess] self
# 226| getBody: [StmtSequence] do ...
@@ -373,7 +372,6 @@ calls/calls.rb:
# 227| getReceiver: [Self, SelfVariableAccess] self
# 229| getStmt: [ForExpr] for ... in ...
# 229| getPattern: [LocalVariableAccess] x
# 229| <in>: [???] In
# 229| getValue: [MethodCall] call to bar
# 229| getReceiver: [ConstantReadAccess] X
# 229| getBody: [StmtSequence] do ...
@@ -655,6 +653,26 @@ calls/calls.rb:
# 336| getReceiver: [Self, SelfVariableAccess] self
# 336| getArgument: [LocalVariableAccess] b
# 336| getArgument: [ForwardedArguments] ...
# 340| getStmt: [ForExpr] for ... in ...
# 340| getPattern: [TuplePattern] (..., ...)
# 340| getElement: [LocalVariableAccess] x
# 340| getElement: [LocalVariableAccess] y
# 340| getElement: [LocalVariableAccess] z
# 340| getValue: [ArrayLiteral] [...]
# 340| getElement: [ArrayLiteral] [...]
# 340| getElement: [IntegerLiteral] 1
# 340| getElement: [IntegerLiteral] 2
# 340| getElement: [IntegerLiteral] 3
# 340| getElement: [ArrayLiteral] [...]
# 340| getElement: [IntegerLiteral] 4
# 340| getElement: [IntegerLiteral] 5
# 340| getElement: [IntegerLiteral] 6
# 340| getBody: [StmtSequence] do ...
# 341| getStmt: [MethodCall] call to foo
# 341| getReceiver: [Self, SelfVariableAccess] self
# 341| getArgument: [LocalVariableAccess] x
# 341| getArgument: [LocalVariableAccess] y
# 341| getArgument: [LocalVariableAccess] z
control/cases.rb:
# 1| [Toplevel] cases.rb
# 2| getStmt: [AssignExpr] ... = ...
@@ -680,7 +698,7 @@ control/cases.rb:
# 11| getPattern: [LocalVariableAccess] d
# 11| getBody: [StmtSequence] then ...
# 12| getStmt: [IntegerLiteral] 200
# 13| getBranch: [StmtSequence] else ...
# 13| getBranch/getElseBranch: [StmtSequence] else ...
# 14| getStmt: [IntegerLiteral] 300
# 18| getStmt: [CaseExpr] case ...
# 19| getBranch: [WhenExpr] when ...
@@ -701,6 +719,376 @@ control/cases.rb:
# 21| getAnOperand/getArgument/getGreaterOperand/getRightOperand: [LocalVariableAccess] b
# 21| getBody: [StmtSequence] then ...
# 21| getStmt: [IntegerLiteral] 30
# 26| getStmt: [CaseExpr] case ...
# 26| getValue: [MethodCall] call to expr
# 26| getReceiver: [Self, SelfVariableAccess] self
# 27| getBranch: [InClause] in ... then ...
# 27| getPattern: [IntegerLiteral] 5
# 27| getBody: [StmtSequence] then ...
# 27| getStmt: [BooleanLiteral] true
# 28| getBranch/getElseBranch: [StmtSequence] else ...
# 28| getStmt: [BooleanLiteral] false
# 31| getStmt: [CaseExpr] case ...
# 31| getValue: [MethodCall] call to expr
# 31| getReceiver: [Self, SelfVariableAccess] self
# 32| getBranch: [InClause] in ... then ...
# 32| getPattern: [LocalVariableAccess] x
# 32| getCondition: [LTExpr] ... < ...
# 32| getAnOperand/getLeftOperand/getLesserOperand/getReceiver: [LocalVariableAccess] x
# 32| getAnOperand/getArgument/getGreaterOperand/getRightOperand: [IntegerLiteral] 0
# 32| getBody: [StmtSequence] then ...
# 33| getStmt: [BooleanLiteral] true
# 34| getBranch: [InClause] in ... then ...
# 34| getPattern: [LocalVariableAccess] x
# 34| getCondition: [LTExpr] ... < ...
# 34| getAnOperand/getLeftOperand/getLesserOperand/getReceiver: [LocalVariableAccess] x
# 34| getAnOperand/getArgument/getGreaterOperand/getRightOperand: [IntegerLiteral] 0
# 34| getBody: [StmtSequence] then ...
# 35| getStmt: [BooleanLiteral] true
# 36| getBranch/getElseBranch: [StmtSequence] else ...
# 36| getStmt: [BooleanLiteral] false
# 39| getStmt: [CaseExpr] case ...
# 39| getValue: [MethodCall] call to expr
# 39| getReceiver: [Self, SelfVariableAccess] self
# 40| getBranch: [InClause] in ... then ...
# 40| getPattern: [IntegerLiteral] 5
# 41| getBranch: [InClause] in ... then ...
# 41| getPattern: [ArrayPattern] [ ..., * ]
# 41| getPrefixElement/getSuffixElement: [IntegerLiteral] 5
# 42| getBranch: [InClause] in ... then ...
# 42| getPattern: [ArrayPattern] [ ..., * ]
# 42| getPrefixElement: [IntegerLiteral] 1
# 42| getPrefixElement: [IntegerLiteral] 2
# 43| getBranch: [InClause] in ... then ...
# 43| getPattern: [ArrayPattern] [ ..., * ]
# 43| getPrefixElement/getSuffixElement: [IntegerLiteral] 1
# 43| getPrefixElement/getSuffixElement: [IntegerLiteral] 2
# 44| getBranch: [InClause] in ... then ...
# 44| getPattern: [ArrayPattern] [ ..., * ]
# 44| getPrefixElement: [IntegerLiteral] 1
# 44| getPrefixElement: [IntegerLiteral] 2
# 44| getPrefixElement: [IntegerLiteral] 3
# 45| getBranch: [InClause] in ... then ...
# 45| getPattern: [ArrayPattern] [ ..., * ]
# 45| getPrefixElement/getSuffixElement: [IntegerLiteral] 1
# 45| getPrefixElement/getSuffixElement: [IntegerLiteral] 2
# 45| getPrefixElement/getSuffixElement: [IntegerLiteral] 3
# 46| getBranch: [InClause] in ... then ...
# 46| getPattern: [ArrayPattern] [ ..., * ]
# 46| getPrefixElement/getSuffixElement: [IntegerLiteral] 1
# 46| getPrefixElement/getSuffixElement: [IntegerLiteral] 2
# 46| getPrefixElement/getSuffixElement: [IntegerLiteral] 3
# 47| getBranch: [InClause] in ... then ...
# 47| getPattern: [ArrayPattern] [ ..., * ]
# 47| getPrefixElement/getSuffixElement: [IntegerLiteral] 1
# 47| getRestVariableAccess: [LocalVariableAccess] x
# 47| getSuffixElement: [IntegerLiteral] 3
# 48| getBranch: [InClause] in ... then ...
# 48| getPattern: [ArrayPattern] [ ..., * ]
# 49| getBranch: [InClause] in ... then ...
# 49| getPattern: [ArrayPattern] [ ..., * ]
# 49| getSuffixElement: [IntegerLiteral] 3
# 49| getSuffixElement: [IntegerLiteral] 4
# 50| getBranch: [InClause] in ... then ...
# 50| getPattern: [FindPattern] [ *,...,* ]
# 50| getElement: [IntegerLiteral] 3
# 51| getBranch: [InClause] in ... then ...
# 51| getPattern: [FindPattern] [ *,...,* ]
# 51| getPrefixVariableAccess: [LocalVariableAccess] a
# 51| getElement: [IntegerLiteral] 3
# 51| getSuffixVariableAccess: [LocalVariableAccess] b
# 52| getBranch: [InClause] in ... then ...
# 52| getPattern: [HashPattern] { ..., ** }
# 52| getKey: [SymbolLiteral] :a
# 53| getBranch: [InClause] in ... then ...
# 53| getPattern: [HashPattern] { ..., ** }
# 53| getKey: [SymbolLiteral] :a
# 53| getValue: [IntegerLiteral] 5
# 54| getBranch: [InClause] in ... then ...
# 54| getPattern: [HashPattern] { ..., ** }
# 54| getKey: [SymbolLiteral] :a
# 54| getValue: [IntegerLiteral] 5
# 55| getBranch: [InClause] in ... then ...
# 55| getPattern: [HashPattern] { ..., ** }
# 55| getKey: [SymbolLiteral] :a
# 55| getValue: [IntegerLiteral] 5
# 55| getKey: [SymbolLiteral] :b
# 56| getBranch: [InClause] in ... then ...
# 56| getPattern: [HashPattern] { ..., ** }
# 56| getKey: [SymbolLiteral] :a
# 56| getValue: [IntegerLiteral] 5
# 56| getKey: [SymbolLiteral] :b
# 56| getRestVariableAccess: [LocalVariableAccess] map
# 57| getBranch: [InClause] in ... then ...
# 57| getPattern: [HashPattern] { ..., ** }
# 57| getKey: [SymbolLiteral] :a
# 57| getValue: [IntegerLiteral] 5
# 57| getKey: [SymbolLiteral] :b
# 58| getBranch: [InClause] in ... then ...
# 58| getPattern: [HashPattern] { ..., ** }
# 59| getBranch: [InClause] in ... then ...
# 59| getPattern: [ArrayPattern] [ ..., * ]
# 59| getPrefixElement: [IntegerLiteral] 5
# 60| getBranch: [InClause] in ... then ...
# 60| getPattern: [ArrayPattern] [ ..., * ]
# 60| getPrefixElement/getSuffixElement: [IntegerLiteral] 5
# 61| getBranch: [InClause] in ... then ...
# 61| getPattern: [ArrayPattern] [ ..., * ]
# 61| getPrefixElement: [IntegerLiteral] 1
# 61| getPrefixElement: [IntegerLiteral] 2
# 62| getBranch: [InClause] in ... then ...
# 62| getPattern: [ArrayPattern] [ ..., * ]
# 62| getPrefixElement/getSuffixElement: [IntegerLiteral] 1
# 62| getPrefixElement/getSuffixElement: [IntegerLiteral] 2
# 63| getBranch: [InClause] in ... then ...
# 63| getPattern: [ArrayPattern] [ ..., * ]
# 63| getPrefixElement: [IntegerLiteral] 1
# 63| getPrefixElement: [IntegerLiteral] 2
# 63| getPrefixElement: [IntegerLiteral] 3
# 64| getBranch: [InClause] in ... then ...
# 64| getPattern: [ArrayPattern] [ ..., * ]
# 64| getPrefixElement/getSuffixElement: [IntegerLiteral] 1
# 64| getPrefixElement/getSuffixElement: [IntegerLiteral] 2
# 64| getPrefixElement/getSuffixElement: [IntegerLiteral] 3
# 65| getBranch: [InClause] in ... then ...
# 65| getPattern: [ArrayPattern] [ ..., * ]
# 65| getPrefixElement/getSuffixElement: [IntegerLiteral] 1
# 65| getPrefixElement/getSuffixElement: [IntegerLiteral] 2
# 65| getPrefixElement/getSuffixElement: [IntegerLiteral] 3
# 66| getBranch: [InClause] in ... then ...
# 66| getPattern: [ArrayPattern] [ ..., * ]
# 66| getPrefixElement/getSuffixElement: [IntegerLiteral] 1
# 66| getRestVariableAccess: [LocalVariableAccess] x
# 66| getSuffixElement: [IntegerLiteral] 3
# 67| getBranch: [InClause] in ... then ...
# 67| getPattern: [ArrayPattern] [ ..., * ]
# 68| getBranch: [InClause] in ... then ...
# 68| getPattern: [ArrayPattern] [ ..., * ]
# 68| getSuffixElement: [IntegerLiteral] 3
# 68| getSuffixElement: [IntegerLiteral] 4
# 69| getBranch: [InClause] in ... then ...
# 69| getPattern: [FindPattern] [ *,...,* ]
# 69| getElement: [IntegerLiteral] 3
# 70| getBranch: [InClause] in ... then ...
# 70| getPattern: [FindPattern] [ *,...,* ]
# 70| getPrefixVariableAccess: [LocalVariableAccess] a
# 70| getElement: [IntegerLiteral] 3
# 70| getSuffixVariableAccess: [LocalVariableAccess] b
# 71| getBranch: [InClause] in ... then ...
# 71| getPattern: [HashPattern] { ..., ** }
# 71| getKey: [SymbolLiteral] :a
# 72| getBranch: [InClause] in ... then ...
# 72| getPattern: [HashPattern] { ..., ** }
# 72| getKey: [SymbolLiteral] :a
# 72| getValue: [IntegerLiteral] 5
# 73| getBranch: [InClause] in ... then ...
# 73| getPattern: [HashPattern] { ..., ** }
# 73| getKey: [SymbolLiteral] :a
# 73| getValue: [IntegerLiteral] 5
# 74| getBranch: [InClause] in ... then ...
# 74| getPattern: [HashPattern] { ..., ** }
# 74| getKey: [SymbolLiteral] :a
# 74| getValue: [IntegerLiteral] 5
# 74| getKey: [SymbolLiteral] :b
# 75| getBranch: [InClause] in ... then ...
# 75| getPattern: [HashPattern] { ..., ** }
# 75| getKey: [SymbolLiteral] :a
# 75| getValue: [IntegerLiteral] 5
# 75| getKey: [SymbolLiteral] :b
# 75| getRestVariableAccess: [LocalVariableAccess] map
# 76| getBranch: [InClause] in ... then ...
# 76| getPattern: [HashPattern] { ..., ** }
# 76| getKey: [SymbolLiteral] :a
# 76| getValue: [IntegerLiteral] 5
# 76| getKey: [SymbolLiteral] :b
# 77| getBranch: [InClause] in ... then ...
# 77| getPattern: [HashPattern] { ..., ** }
# 78| getBranch: [InClause] in ... then ...
# 78| getPattern: [HashPattern] { ..., ** }
# 79| getBranch: [InClause] in ... then ...
# 79| getPattern: [ArrayPattern] [ ..., * ]
# 84| getStmt: [AssignExpr] ... = ...
# 84| getAnOperand/getLeftOperand: [LocalVariableAccess] foo
# 84| getAnOperand/getRightOperand: [IntegerLiteral] 42
# 86| getStmt: [CaseExpr] case ...
# 86| getValue: [MethodCall] call to expr
# 86| getReceiver: [Self, SelfVariableAccess] self
# 87| getBranch: [InClause] in ... then ...
# 87| getPattern: [IntegerLiteral] 5
# 88| getBranch: [InClause] in ... then ...
# 88| getPattern: [VariableReferencePattern] ^...
# 88| getVariableAccess: [LocalVariableAccess] foo
# 89| getBranch: [InClause] in ... then ...
# 89| getPattern: [LocalVariableAccess] var
# 90| getBranch: [InClause] in ... then ...
# 90| getPattern: [StringLiteral] "string"
# 90| getComponent: [StringTextComponent] string
# 91| getBranch: [InClause] in ... then ...
# 91| getPattern: [ArrayLiteral] %w(...)
# 91| getElement: [StringLiteral] "foo"
# 91| getComponent: [StringTextComponent] foo
# 91| getElement: [StringLiteral] "bar"
# 91| getComponent: [StringTextComponent] bar
# 92| getBranch: [InClause] in ... then ...
# 92| getPattern: [ArrayLiteral] %i(...)
# 92| getElement: [SymbolLiteral] :"foo"
# 92| getComponent: [StringTextComponent] foo
# 92| getElement: [SymbolLiteral] :"bar"
# 92| getComponent: [StringTextComponent] bar
# 93| getBranch: [InClause] in ... then ...
# 93| getPattern: [RegExpLiteral] /.*abc[0-9]/
# 93| getParsed: [RegExpSequence] .*abc[0-9]
# 93| 0: [RegExpStar] .*
# 93| 0: [RegExpDot] .
# 93| 1: [RegExpConstant, RegExpNormalChar] a
# 93| 2: [RegExpConstant, RegExpNormalChar] b
# 93| 3: [RegExpConstant, RegExpNormalChar] c
# 93| 4: [RegExpCharacterClass] [0-9]
# 93| 0: [RegExpCharacterRange] 0-9
# 93| 0: [RegExpConstant, RegExpNormalChar] 0
# 93| 1: [RegExpConstant, RegExpNormalChar] 9
# 93| getComponent: [StringTextComponent] .*abc[0-9]
# 94| getBranch: [InClause] in ... then ...
# 94| getPattern: [RangeLiteral] _ .. _
# 94| getBegin: [IntegerLiteral] 5
# 94| getEnd: [IntegerLiteral] 10
# 95| getBranch: [InClause] in ... then ...
# 95| getPattern: [RangeLiteral] _ .. _
# 95| getEnd: [IntegerLiteral] 10
# 96| getBranch: [InClause] in ... then ...
# 96| getPattern: [RangeLiteral] _ .. _
# 96| getBegin: [IntegerLiteral] 5
# 97| getBranch: [InClause] in ... then ...
# 97| getPattern: [AsPattern] ... => ...
# 97| getPattern: [IntegerLiteral] 5
# 97| getVariableAccess: [LocalVariableAccess] x
# 98| getBranch: [InClause] in ... then ...
# 98| getPattern: [AlternativePattern] ... | ...
# 98| getAlternative: [IntegerLiteral] 5
# 98| getAlternative: [VariableReferencePattern] ^...
# 98| getVariableAccess: [LocalVariableAccess] foo
# 98| getAlternative: [LocalVariableAccess] var
# 98| getAlternative: [StringLiteral] "string"
# 98| getComponent: [StringTextComponent] string
# 99| getBranch: [InClause] in ... then ...
# 99| getPattern: [ConstantReadAccess] Foo
# 100| getBranch: [InClause] in ... then ...
# 100| getPattern: [ConstantReadAccess] Bar
# 100| getScopeExpr: [ConstantReadAccess] Foo
# 101| getBranch: [InClause] in ... then ...
# 101| getPattern: [ConstantReadAccess] Bar
# 101| getScopeExpr: [ConstantReadAccess] Foo
# 102| getBranch: [InClause] in ... then ...
# 102| getPattern: [AlternativePattern] ... | ...
# 102| getAlternative: [NilLiteral] nil
# 102| getAlternative: [Self, SelfVariableAccess] self
# 102| getAlternative: [BooleanLiteral] true
# 102| getAlternative: [BooleanLiteral] false
# 102| getAlternative: [LineLiteral] __LINE__
# 102| getAlternative: [FileLiteral] __FILE__
# 102| getAlternative: [EncodingLiteral] __ENCODING__
# 103| getBranch: [InClause] in ... then ...
# 103| getPattern: [Lambda] -> { ... }
# 103| getParameter: [SimpleParameter] x
# 103| getDefiningAccess: [LocalVariableAccess] x
# 103| getStmt: [EqExpr] ... == ...
# 103| getAnOperand/getLeftOperand/getReceiver: [LocalVariableAccess] x
# 103| getAnOperand/getArgument/getRightOperand: [IntegerLiteral] 10
# 104| getBranch: [InClause] in ... then ...
# 104| getPattern: [SymbolLiteral] :foo
# 105| getBranch: [InClause] in ... then ...
# 105| getPattern: [SymbolLiteral] :"foo bar"
# 105| getComponent: [StringTextComponent] foo bar
# 106| getBranch: [InClause] in ... then ...
# 106| getPattern: [AlternativePattern] ... | ...
# 106| getAlternative: [UnaryMinusExpr] - ...
# 106| getAnOperand/getOperand/getReceiver: [IntegerLiteral] 5
# 106| getAlternative: [UnaryPlusExpr] + ...
# 106| getAnOperand/getOperand/getReceiver: [IntegerLiteral] 10
# 111| getStmt: [CaseExpr] case ...
# 111| getValue: [MethodCall] call to expr
# 111| getReceiver: [Self, SelfVariableAccess] self
# 112| getBranch: [InClause] in ... then ...
# 112| getPattern: [ArrayPattern] [ ..., * ]
# 113| getBranch: [InClause] in ... then ...
# 113| getPattern: [ArrayPattern] [ ..., * ]
# 113| getPrefixElement: [LocalVariableAccess] x
# 114| getBranch: [InClause] in ... then ...
# 114| getPattern: [ArrayPattern] [ ..., * ]
# 114| getPrefixElement/getSuffixElement: [LocalVariableAccess] x
# 115| getBranch: [InClause] in ... then ...
# 115| getPattern: [ArrayPattern] [ ..., * ]
# 115| getClass: [ConstantReadAccess] Bar
# 115| getScopeExpr: [ConstantReadAccess] Foo
# 116| getBranch: [InClause] in ... then ...
# 116| getPattern: [ArrayPattern] [ ..., * ]
# 116| getClass: [ConstantReadAccess] Foo
# 117| getBranch: [InClause] in ... then ...
# 117| getPattern: [ArrayPattern] [ ..., * ]
# 117| getClass: [ConstantReadAccess] Bar
# 118| getBranch: [InClause] in ... then ...
# 118| getPattern: [ArrayPattern] [ ..., * ]
# 118| getClass: [ConstantReadAccess] Bar
# 118| getPrefixElement/getSuffixElement: [LocalVariableAccess] a
# 118| getPrefixElement/getSuffixElement: [LocalVariableAccess] b
# 118| getRestVariableAccess: [LocalVariableAccess] c
# 118| getSuffixElement: [LocalVariableAccess] d
# 118| getSuffixElement: [LocalVariableAccess] e
# 123| getStmt: [CaseExpr] case ...
# 123| getValue: [MethodCall] call to expr
# 123| getReceiver: [Self, SelfVariableAccess] self
# 124| getBranch: [InClause] in ... then ...
# 124| getPattern: [FindPattern] [ *,...,* ]
# 124| getElement: [LocalVariableAccess] x
# 125| getBranch: [InClause] in ... then ...
# 125| getPattern: [FindPattern] [ *,...,* ]
# 125| getPrefixVariableAccess: [LocalVariableAccess] x
# 125| getElement: [IntegerLiteral] 1
# 125| getElement: [IntegerLiteral] 2
# 125| getSuffixVariableAccess: [LocalVariableAccess] y
# 126| getBranch: [InClause] in ... then ...
# 126| getPattern: [FindPattern] [ *,...,* ]
# 126| getClass: [ConstantReadAccess] Bar
# 126| getScopeExpr: [ConstantReadAccess] Foo
# 126| getElement: [IntegerLiteral] 1
# 127| getBranch: [InClause] in ... then ...
# 127| getPattern: [FindPattern] [ *,...,* ]
# 127| getClass: [ConstantReadAccess] Foo
# 127| getElement: [ConstantReadAccess] Bar
# 132| getStmt: [CaseExpr] case ...
# 132| getValue: [MethodCall] call to expr
# 132| getReceiver: [Self, SelfVariableAccess] self
# 133| getBranch: [InClause] in ... then ...
# 133| getPattern: [HashPattern] { ..., ** }
# 134| getBranch: [InClause] in ... then ...
# 134| getPattern: [HashPattern] { ..., ** }
# 134| getKey: [SymbolLiteral] :x
# 135| getBranch: [InClause] in ... then ...
# 135| getPattern: [HashPattern] { ..., ** }
# 135| getClass: [ConstantReadAccess] Bar
# 135| getScopeExpr: [ConstantReadAccess] Foo
# 135| getKey: [SymbolLiteral] :x
# 135| getValue: [IntegerLiteral] 1
# 136| getBranch: [InClause] in ... then ...
# 136| getPattern: [HashPattern] { ..., ** }
# 136| getClass: [ConstantReadAccess] Bar
# 136| getScopeExpr: [ConstantReadAccess] Foo
# 136| getKey: [SymbolLiteral] :x
# 136| getValue: [IntegerLiteral] 1
# 136| getKey: [SymbolLiteral] :a
# 136| getRestVariableAccess: [LocalVariableAccess] rest
# 137| getBranch: [InClause] in ... then ...
# 137| getPattern: [HashPattern] { ..., ** }
# 137| getClass: [ConstantReadAccess] Foo
# 137| getKey: [SymbolLiteral] :y
# 138| getBranch: [InClause] in ... then ...
# 138| getPattern: [HashPattern] { ..., ** }
# 138| getClass: [ConstantReadAccess] Bar
# 139| getBranch: [InClause] in ... then ...
# 139| getPattern: [HashPattern] { ..., ** }
# 139| getClass: [ConstantReadAccess] Bar
# 139| getKey: [SymbolLiteral] :a
# 139| getValue: [IntegerLiteral] 1
modules/classes.rb:
# 2| [Toplevel] classes.rb
# 3| getStmt: [ClassDeclaration] Foo
@@ -1422,7 +1810,6 @@ control/loops.rb:
# 6| getAnOperand/getRightOperand: [IntegerLiteral] 0
# 9| getStmt: [ForExpr] for ... in ...
# 9| getPattern: [LocalVariableAccess] n
# 9| <in>: [???] In
# 9| getValue: [RangeLiteral] _ .. _
# 9| getBegin: [IntegerLiteral] 1
# 9| getEnd: [IntegerLiteral] 10
@@ -1435,7 +1822,6 @@ control/loops.rb:
# 11| getAnOperand/getRightOperand: [LocalVariableAccess] n
# 16| getStmt: [ForExpr] for ... in ...
# 16| getPattern: [LocalVariableAccess] n
# 16| <in>: [???] In
# 16| getValue: [RangeLiteral] _ .. _
# 16| getBegin: [IntegerLiteral] 1
# 16| getEnd: [IntegerLiteral] 10
@@ -1450,7 +1836,6 @@ control/loops.rb:
# 22| getPattern: [TuplePattern] (..., ...)
# 22| getElement: [LocalVariableAccess] key
# 22| getElement: [LocalVariableAccess] value
# 22| <in>: [???] In
# 22| getValue: [HashLiteral] {...}
# 22| getElement: [Pair] Pair
# 22| getKey: [SymbolLiteral] :foo
@@ -1469,7 +1854,6 @@ control/loops.rb:
# 28| getPattern: [TuplePattern] (..., ...)
# 28| getElement: [LocalVariableAccess] key
# 28| getElement: [LocalVariableAccess] value
# 28| <in>: [???] In
# 28| getValue: [HashLiteral] {...}
# 28| getElement: [Pair] Pair
# 28| getKey: [SymbolLiteral] :foo
@@ -2131,6 +2515,16 @@ params/params.rb:
# 70| getAnOperand/getLeftOperand/getReceiver: [LocalVariableAccess] a
# 70| getAnOperand/getArgument/getRightOperand: [LocalVariableAccess] b
# 70| getAnOperand/getArgument/getRightOperand: [LocalVariableAccess] c
# 73| getStmt: [Method] method_with_nil_splat
# 73| getParameter: [SimpleParameter] wibble
# 73| getDefiningAccess: [LocalVariableAccess] wibble
# 73| getParameter: [HashSplatNilParameter] **nil
# 77| getStmt: [MethodCall] call to each
# 77| getReceiver: [LocalVariableAccess] array
# 77| getBlock: [DoBlock] do ... end
# 77| getParameter: [SimpleParameter] val
# 77| getDefiningAccess: [LocalVariableAccess] val
# 77| getParameter: [HashSplatNilParameter] **nil
erb/template.html.erb:
# 19| [Toplevel] template.html.erb
# 19| getStmt: [StringLiteral] "hello world"
@@ -2140,7 +2534,6 @@ erb/template.html.erb:
# 25| getAnOperand/getRightOperand: [StringLiteral] ""
# 27| getStmt: [ForExpr] for ... in ...
# 27| getPattern: [LocalVariableAccess] x
# 27| <in>: [???] In
# 27| getValue: [ArrayLiteral] [...]
# 27| getElement: [StringLiteral] "foo"
# 27| getComponent: [StringTextComponent] foo

View File

@@ -23,6 +23,30 @@ calls/calls.rb:
# 67| getAnOperand/getLeftOperand/getReceiver: [LocalVariableAccess] var1
# 67| getAnOperand/getArgument/getRightOperand: [MethodCall] call to bar
# 67| getReceiver: [ConstantReadAccess] X
# 226| [ForExpr] for ... in ...
# 226| getDesugared: [MethodCall] call to each
# 226| getBlock: [BraceBlock] { ... }
# 226| getParameter: [SimpleParameter] __synth__0__1
# 226| getDefiningAccess: [LocalVariableAccess] __synth__0__1
# 226| getStmt: [AssignExpr] ... = ...
# 226| getAnOperand/getRightOperand: [LocalVariableAccess] __synth__0__1
# 226| getAnOperand/getLeftOperand: [LocalVariableAccess] x
# 227| getStmt: [MethodCall] call to baz
# 227| getReceiver: [Self, SelfVariableAccess] self
# 226| getReceiver: [MethodCall] call to bar
# 226| getReceiver: [Self, SelfVariableAccess] self
# 229| [ForExpr] for ... in ...
# 229| getDesugared: [MethodCall] call to each
# 229| getBlock: [BraceBlock] { ... }
# 229| getParameter: [SimpleParameter] __synth__0__1
# 229| getDefiningAccess: [LocalVariableAccess] __synth__0__1
# 229| getStmt: [AssignExpr] ... = ...
# 229| getAnOperand/getRightOperand: [LocalVariableAccess] __synth__0__1
# 229| getAnOperand/getLeftOperand: [LocalVariableAccess] x
# 230| getStmt: [MethodCall] call to baz
# 230| getReceiver: [ConstantReadAccess] X
# 229| getReceiver: [MethodCall] call to bar
# 229| getReceiver: [ConstantReadAccess] X
# 314| [AssignExpr] ... = ...
# 314| getDesugared: [StmtSequence] ...
# 314| getStmt: [SetterMethodCall] call to foo=
@@ -195,6 +219,68 @@ calls/calls.rb:
# 320| getAnOperand/getArgument/getRightOperand: [IntegerLiteral] 2
# 320| getAnOperand/getLeftOperand: [LocalVariableAccess] __synth__4
# 320| getStmt: [LocalVariableAccess] __synth__4
# 340| [ForExpr] for ... in ...
# 340| getDesugared: [MethodCall] call to each
# 340| getBlock: [BraceBlock] { ... }
# 340| getParameter: [SimpleParameter] __synth__0__1
# 340| getDefiningAccess: [LocalVariableAccess] __synth__0__1
# 340| getStmt: [AssignExpr] ... = ...
# 340| getDesugared: [StmtSequence] ...
# 340| getStmt: [AssignExpr] ... = ...
# 340| getAnOperand/getRightOperand: [SplatExpr] * ...
# 340| getAnOperand/getOperand/getReceiver: [LocalVariableAccess] __synth__0__1
# 340| getAnOperand/getLeftOperand: [LocalVariableAccess] __synth__0__1
# 340| getStmt: [AssignExpr] ... = ...
# 340| getAnOperand/getLeftOperand: [LocalVariableAccess] x
# 340| getAnOperand/getRightOperand: [MethodCall] call to []
# 340| getArgument: [IntegerLiteral] 0
# 340| getReceiver: [LocalVariableAccess] __synth__0__1
# 340| getStmt: [AssignExpr] ... = ...
# 340| getAnOperand/getLeftOperand: [LocalVariableAccess] y
# 340| getAnOperand/getRightOperand: [MethodCall] call to []
# 340| getArgument: [IntegerLiteral] 1
# 340| getReceiver: [LocalVariableAccess] __synth__0__1
# 340| getStmt: [AssignExpr] ... = ...
# 340| getAnOperand/getLeftOperand: [LocalVariableAccess] z
# 340| getAnOperand/getRightOperand: [MethodCall] call to []
# 340| getArgument: [IntegerLiteral] 2
# 340| getReceiver: [LocalVariableAccess] __synth__0__1
# 340| getLeftOperand: [TuplePattern] (..., ...)
# 341| getStmt: [MethodCall] call to foo
# 341| getReceiver: [Self, SelfVariableAccess] self
# 341| getArgument: [LocalVariableAccess] x
# 341| getArgument: [LocalVariableAccess] y
# 341| getArgument: [LocalVariableAccess] z
# 340| getReceiver: [ArrayLiteral] [...]
# 340| getDesugared: [MethodCall] call to []
# 340| getReceiver: [ConstantReadAccess] Array
# 340| getArgument: [ArrayLiteral] [...]
# 340| getDesugared: [MethodCall] call to []
# 340| getReceiver: [ConstantReadAccess] Array
# 340| getArgument: [IntegerLiteral] 1
# 340| getArgument: [IntegerLiteral] 2
# 340| getArgument: [IntegerLiteral] 3
# 340| getArgument: [ArrayLiteral] [...]
# 340| getDesugared: [MethodCall] call to []
# 340| getReceiver: [ConstantReadAccess] Array
# 340| getArgument: [IntegerLiteral] 4
# 340| getArgument: [IntegerLiteral] 5
# 340| getArgument: [IntegerLiteral] 6
control/cases.rb:
# 91| [ArrayLiteral] %w(...)
# 91| getDesugared: [MethodCall] call to []
# 91| getReceiver: [ConstantReadAccess] Array
# 91| getArgument: [StringLiteral] "foo"
# 91| getComponent: [StringTextComponent] foo
# 91| getArgument: [StringLiteral] "bar"
# 91| getComponent: [StringTextComponent] bar
# 92| [ArrayLiteral] %i(...)
# 92| getDesugared: [MethodCall] call to []
# 92| getReceiver: [ConstantReadAccess] Array
# 92| getArgument: [SymbolLiteral] :"foo"
# 92| getComponent: [StringTextComponent] foo
# 92| getArgument: [SymbolLiteral] :"bar"
# 92| getComponent: [StringTextComponent] bar
constants/constants.rb:
# 20| [ArrayLiteral] [...]
# 20| getDesugared: [MethodCall] call to []
@@ -327,48 +413,132 @@ literals/literals.rb:
# 109| getArgument: [SymbolLiteral] :"baz"
# 109| getComponent: [StringTextComponent] baz
control/loops.rb:
# 10| [AssignAddExpr] ... += ...
# 10| getDesugared: [AssignExpr] ... = ...
# 10| getAnOperand/getLeftOperand: [LocalVariableAccess] sum
# 10| getAnOperand/getRightOperand: [AddExpr] ... + ...
# 10| getAnOperand/getLeftOperand/getReceiver: [LocalVariableAccess] sum
# 10| getAnOperand/getArgument/getRightOperand: [LocalVariableAccess] n
# 17| [AssignAddExpr] ... += ...
# 17| getDesugared: [AssignExpr] ... = ...
# 17| getAnOperand/getLeftOperand: [LocalVariableAccess] sum
# 17| getAnOperand/getRightOperand: [AddExpr] ... + ...
# 17| getAnOperand/getLeftOperand/getReceiver: [LocalVariableAccess] sum
# 17| getAnOperand/getArgument/getRightOperand: [LocalVariableAccess] n
# 18| [AssignSubExpr] ... -= ...
# 18| getDesugared: [AssignExpr] ... = ...
# 18| getAnOperand/getLeftOperand: [LocalVariableAccess] foo
# 18| getAnOperand/getRightOperand: [SubExpr] ... - ...
# 18| getAnOperand/getLeftOperand/getReceiver: [LocalVariableAccess] foo
# 18| getAnOperand/getArgument/getRightOperand: [LocalVariableAccess] n
# 23| [AssignAddExpr] ... += ...
# 23| getDesugared: [AssignExpr] ... = ...
# 23| getAnOperand/getLeftOperand: [LocalVariableAccess] sum
# 23| getAnOperand/getRightOperand: [AddExpr] ... + ...
# 23| getAnOperand/getLeftOperand/getReceiver: [LocalVariableAccess] sum
# 23| getAnOperand/getArgument/getRightOperand: [LocalVariableAccess] value
# 24| [AssignMulExpr] ... *= ...
# 24| getDesugared: [AssignExpr] ... = ...
# 24| getAnOperand/getLeftOperand: [LocalVariableAccess] foo
# 24| getAnOperand/getRightOperand: [MulExpr] ... * ...
# 24| getAnOperand/getLeftOperand/getReceiver: [LocalVariableAccess] foo
# 24| getAnOperand/getArgument/getRightOperand: [LocalVariableAccess] value
# 29| [AssignAddExpr] ... += ...
# 29| getDesugared: [AssignExpr] ... = ...
# 29| getAnOperand/getLeftOperand: [LocalVariableAccess] sum
# 29| getAnOperand/getRightOperand: [AddExpr] ... + ...
# 29| getAnOperand/getLeftOperand/getReceiver: [LocalVariableAccess] sum
# 29| getAnOperand/getArgument/getRightOperand: [LocalVariableAccess] value
# 30| [AssignDivExpr] ... /= ...
# 30| getDesugared: [AssignExpr] ... = ...
# 30| getAnOperand/getLeftOperand: [LocalVariableAccess] foo
# 30| getAnOperand/getRightOperand: [DivExpr] ... / ...
# 30| getAnOperand/getLeftOperand/getReceiver: [LocalVariableAccess] foo
# 30| getAnOperand/getArgument/getRightOperand: [LocalVariableAccess] value
# 9| [ForExpr] for ... in ...
# 9| getDesugared: [MethodCall] call to each
# 9| getBlock: [BraceBlock] { ... }
# 9| getParameter: [SimpleParameter] __synth__0__1
# 9| getDefiningAccess: [LocalVariableAccess] __synth__0__1
# 9| getStmt: [AssignExpr] ... = ...
# 9| getAnOperand/getRightOperand: [LocalVariableAccess] __synth__0__1
# 9| getAnOperand/getLeftOperand: [LocalVariableAccess] n
# 10| getStmt: [AssignAddExpr] ... += ...
# 10| getDesugared: [AssignExpr] ... = ...
# 10| getAnOperand/getLeftOperand: [LocalVariableAccess] sum
# 10| getAnOperand/getRightOperand: [AddExpr] ... + ...
# 10| getAnOperand/getLeftOperand/getReceiver: [LocalVariableAccess] sum
# 10| getAnOperand/getArgument/getRightOperand: [LocalVariableAccess] n
# 11| getStmt: [AssignExpr] ... = ...
# 11| getAnOperand/getLeftOperand: [LocalVariableAccess] foo
# 11| getAnOperand/getRightOperand: [LocalVariableAccess] n
# 9| getReceiver: [RangeLiteral] _ .. _
# 9| getBegin: [IntegerLiteral] 1
# 9| getEnd: [IntegerLiteral] 10
# 16| [ForExpr] for ... in ...
# 16| getDesugared: [MethodCall] call to each
# 16| getBlock: [BraceBlock] { ... }
# 16| getParameter: [SimpleParameter] __synth__0__1
# 16| getDefiningAccess: [LocalVariableAccess] __synth__0__1
# 16| getStmt: [AssignExpr] ... = ...
# 16| getAnOperand/getRightOperand: [LocalVariableAccess] __synth__0__1
# 16| getAnOperand/getLeftOperand: [LocalVariableAccess] n
# 17| getStmt: [AssignAddExpr] ... += ...
# 17| getDesugared: [AssignExpr] ... = ...
# 17| getAnOperand/getLeftOperand: [LocalVariableAccess] sum
# 17| getAnOperand/getRightOperand: [AddExpr] ... + ...
# 17| getAnOperand/getLeftOperand/getReceiver: [LocalVariableAccess] sum
# 17| getAnOperand/getArgument/getRightOperand: [LocalVariableAccess] n
# 18| getStmt: [AssignSubExpr] ... -= ...
# 18| getDesugared: [AssignExpr] ... = ...
# 18| getAnOperand/getLeftOperand: [LocalVariableAccess] foo
# 18| getAnOperand/getRightOperand: [SubExpr] ... - ...
# 18| getAnOperand/getLeftOperand/getReceiver: [LocalVariableAccess] foo
# 18| getAnOperand/getArgument/getRightOperand: [LocalVariableAccess] n
# 16| getReceiver: [RangeLiteral] _ .. _
# 16| getBegin: [IntegerLiteral] 1
# 16| getEnd: [IntegerLiteral] 10
# 22| [ForExpr] for ... in ...
# 22| getDesugared: [MethodCall] call to each
# 22| getBlock: [BraceBlock] { ... }
# 22| getParameter: [SimpleParameter] __synth__0__1
# 22| getDefiningAccess: [LocalVariableAccess] __synth__0__1
# 22| getStmt: [AssignExpr] ... = ...
# 22| getDesugared: [StmtSequence] ...
# 22| getStmt: [AssignExpr] ... = ...
# 22| getAnOperand/getRightOperand: [SplatExpr] * ...
# 22| getAnOperand/getOperand/getReceiver: [LocalVariableAccess] __synth__0__1
# 22| getAnOperand/getLeftOperand: [LocalVariableAccess] __synth__0__1
# 22| getStmt: [AssignExpr] ... = ...
# 22| getAnOperand/getLeftOperand: [LocalVariableAccess] key
# 22| getAnOperand/getRightOperand: [MethodCall] call to []
# 22| getArgument: [IntegerLiteral] 0
# 22| getReceiver: [LocalVariableAccess] __synth__0__1
# 22| getStmt: [AssignExpr] ... = ...
# 22| getAnOperand/getLeftOperand: [LocalVariableAccess] value
# 22| getAnOperand/getRightOperand: [MethodCall] call to []
# 22| getArgument: [IntegerLiteral] 1
# 22| getReceiver: [LocalVariableAccess] __synth__0__1
# 22| getLeftOperand: [TuplePattern] (..., ...)
# 23| getStmt: [AssignAddExpr] ... += ...
# 23| getDesugared: [AssignExpr] ... = ...
# 23| getAnOperand/getLeftOperand: [LocalVariableAccess] sum
# 23| getAnOperand/getRightOperand: [AddExpr] ... + ...
# 23| getAnOperand/getLeftOperand/getReceiver: [LocalVariableAccess] sum
# 23| getAnOperand/getArgument/getRightOperand: [LocalVariableAccess] value
# 24| getStmt: [AssignMulExpr] ... *= ...
# 24| getDesugared: [AssignExpr] ... = ...
# 24| getAnOperand/getLeftOperand: [LocalVariableAccess] foo
# 24| getAnOperand/getRightOperand: [MulExpr] ... * ...
# 24| getAnOperand/getLeftOperand/getReceiver: [LocalVariableAccess] foo
# 24| getAnOperand/getArgument/getRightOperand: [LocalVariableAccess] value
# 22| getReceiver: [HashLiteral] {...}
# 22| getElement: [Pair] Pair
# 22| getKey: [SymbolLiteral] :foo
# 22| getValue: [IntegerLiteral] 0
# 22| getElement: [Pair] Pair
# 22| getKey: [SymbolLiteral] :bar
# 22| getValue: [IntegerLiteral] 1
# 28| [ForExpr] for ... in ...
# 28| getDesugared: [MethodCall] call to each
# 28| getBlock: [BraceBlock] { ... }
# 28| getParameter: [SimpleParameter] __synth__0__1
# 28| getDefiningAccess: [LocalVariableAccess] __synth__0__1
# 28| getStmt: [AssignExpr] ... = ...
# 28| getDesugared: [StmtSequence] ...
# 28| getStmt: [AssignExpr] ... = ...
# 28| getAnOperand/getRightOperand: [SplatExpr] * ...
# 28| getAnOperand/getOperand/getReceiver: [LocalVariableAccess] __synth__0__1
# 28| getAnOperand/getLeftOperand: [LocalVariableAccess] __synth__0__1
# 28| getStmt: [AssignExpr] ... = ...
# 28| getAnOperand/getLeftOperand: [LocalVariableAccess] key
# 28| getAnOperand/getRightOperand: [MethodCall] call to []
# 28| getArgument: [IntegerLiteral] 0
# 28| getReceiver: [LocalVariableAccess] __synth__0__1
# 28| getStmt: [AssignExpr] ... = ...
# 28| getAnOperand/getLeftOperand: [LocalVariableAccess] value
# 28| getAnOperand/getRightOperand: [MethodCall] call to []
# 28| getArgument: [IntegerLiteral] 1
# 28| getReceiver: [LocalVariableAccess] __synth__0__1
# 28| getLeftOperand: [TuplePattern] (..., ...)
# 29| getStmt: [AssignAddExpr] ... += ...
# 29| getDesugared: [AssignExpr] ... = ...
# 29| getAnOperand/getLeftOperand: [LocalVariableAccess] sum
# 29| getAnOperand/getRightOperand: [AddExpr] ... + ...
# 29| getAnOperand/getLeftOperand/getReceiver: [LocalVariableAccess] sum
# 29| getAnOperand/getArgument/getRightOperand: [LocalVariableAccess] value
# 30| getStmt: [AssignDivExpr] ... /= ...
# 30| getDesugared: [AssignExpr] ... = ...
# 30| getAnOperand/getLeftOperand: [LocalVariableAccess] foo
# 30| getAnOperand/getRightOperand: [DivExpr] ... / ...
# 30| getAnOperand/getLeftOperand/getReceiver: [LocalVariableAccess] foo
# 30| getAnOperand/getArgument/getRightOperand: [LocalVariableAccess] value
# 31| getStmt: [BreakStmt] break
# 28| getReceiver: [HashLiteral] {...}
# 28| getElement: [Pair] Pair
# 28| getKey: [SymbolLiteral] :foo
# 28| getValue: [IntegerLiteral] 0
# 28| getElement: [Pair] Pair
# 28| getKey: [SymbolLiteral] :bar
# 28| getValue: [IntegerLiteral] 1
# 36| [AssignAddExpr] ... += ...
# 36| getDesugared: [AssignExpr] ... = ...
# 36| getAnOperand/getLeftOperand: [LocalVariableAccess] x
@@ -535,21 +705,30 @@ params/params.rb:
# 21| getDesugared: [MethodCall] call to []
# 21| getReceiver: [ConstantReadAccess] Array
erb/template.html.erb:
# 27| [ArrayLiteral] [...]
# 27| getDesugared: [MethodCall] call to []
# 27| getReceiver: [ConstantReadAccess] Array
# 27| getArgument: [StringLiteral] "foo"
# 27| getComponent: [StringTextComponent] foo
# 27| getArgument: [StringLiteral] "bar"
# 27| getComponent: [StringTextComponent] bar
# 27| getArgument: [StringLiteral] "baz"
# 27| getComponent: [StringTextComponent] baz
# 28| [AssignAddExpr] ... += ...
# 28| getDesugared: [AssignExpr] ... = ...
# 28| getAnOperand/getLeftOperand: [LocalVariableAccess] xs
# 28| getAnOperand/getRightOperand: [AddExpr] ... + ...
# 28| getAnOperand/getLeftOperand/getReceiver: [LocalVariableAccess] xs
# 28| getAnOperand/getArgument/getRightOperand: [LocalVariableAccess] x
# 27| [ForExpr] for ... in ...
# 27| getDesugared: [MethodCall] call to each
# 27| getBlock: [BraceBlock] { ... }
# 27| getParameter: [SimpleParameter] __synth__0__1
# 27| getDefiningAccess: [LocalVariableAccess] __synth__0__1
# 27| getStmt: [AssignExpr] ... = ...
# 27| getAnOperand/getRightOperand: [LocalVariableAccess] __synth__0__1
# 27| getAnOperand/getLeftOperand: [LocalVariableAccess] x
# 28| getStmt: [AssignAddExpr] ... += ...
# 28| getDesugared: [AssignExpr] ... = ...
# 28| getAnOperand/getLeftOperand: [LocalVariableAccess] xs
# 28| getAnOperand/getRightOperand: [AddExpr] ... + ...
# 28| getAnOperand/getLeftOperand/getReceiver: [LocalVariableAccess] xs
# 28| getAnOperand/getArgument/getRightOperand: [LocalVariableAccess] x
# 29| getStmt: [LocalVariableAccess] xs
# 27| getReceiver: [ArrayLiteral] [...]
# 27| getDesugared: [MethodCall] call to []
# 27| getReceiver: [ConstantReadAccess] Array
# 27| getArgument: [StringLiteral] "foo"
# 27| getComponent: [StringTextComponent] foo
# 27| getArgument: [StringLiteral] "bar"
# 27| getComponent: [StringTextComponent] bar
# 27| getArgument: [StringLiteral] "baz"
# 27| getComponent: [StringTextComponent] baz
gems/test.gemspec:
# 2| [AssignExpr] ... = ...
# 2| getDesugared: [StmtSequence] ...

View File

@@ -0,0 +1,4 @@
deadEnd
| control/cases.rb:19:13:19:19 | then ... |
| control/cases.rb:20:13:20:19 | then ... |
| control/cases.rb:21:13:21:19 | then ... |

View File

@@ -56,6 +56,15 @@
| calls/calls.rb:320:31:320:31 | 1 | 1 |
| calls/calls.rb:320:37:320:37 | 2 | 2 |
| calls/calls.rb:328:31:328:37 | "error" | error |
| calls/calls.rb:340:5:340:5 | 0 | 0 |
| calls/calls.rb:340:8:340:8 | 1 | 1 |
| calls/calls.rb:340:11:340:11 | 2 | 2 |
| calls/calls.rb:340:18:340:18 | 1 | 1 |
| calls/calls.rb:340:20:340:20 | 2 | 2 |
| calls/calls.rb:340:22:340:22 | 3 | 3 |
| calls/calls.rb:340:27:340:27 | 4 | 4 |
| calls/calls.rb:340:29:340:29 | 5 | 5 |
| calls/calls.rb:340:31:340:31 | 6 | 6 |
| constants/constants.rb:3:19:3:27 | "const_a" | const_a |
| constants/constants.rb:6:15:6:23 | "const_b" | const_b |
| constants/constants.rb:17:12:17:18 | "Hello" | Hello |
@@ -87,6 +96,122 @@
| control/cases.rb:21:6:21:6 | a | 0 |
| control/cases.rb:21:10:21:10 | b | 0 |
| control/cases.rb:21:18:21:19 | 30 | 30 |
| control/cases.rb:27:6:27:6 | 5 | 5 |
| control/cases.rb:27:13:27:16 | true | true |
| control/cases.rb:28:8:28:12 | false | false |
| control/cases.rb:32:19:32:19 | 0 | 0 |
| control/cases.rb:33:8:33:11 | true | true |
| control/cases.rb:34:15:34:15 | 0 | 0 |
| control/cases.rb:35:8:35:11 | true | true |
| control/cases.rb:36:8:36:12 | false | false |
| control/cases.rb:40:6:40:6 | 5 | 5 |
| control/cases.rb:41:6:41:6 | 5 | 5 |
| control/cases.rb:42:6:42:6 | 1 | 1 |
| control/cases.rb:42:9:42:9 | 2 | 2 |
| control/cases.rb:43:6:43:6 | 1 | 1 |
| control/cases.rb:43:9:43:9 | 2 | 2 |
| control/cases.rb:44:6:44:6 | 1 | 1 |
| control/cases.rb:44:9:44:9 | 2 | 2 |
| control/cases.rb:44:12:44:12 | 3 | 3 |
| control/cases.rb:45:6:45:6 | 1 | 1 |
| control/cases.rb:45:9:45:9 | 2 | 2 |
| control/cases.rb:45:12:45:12 | 3 | 3 |
| control/cases.rb:46:6:46:6 | 1 | 1 |
| control/cases.rb:46:9:46:9 | 2 | 2 |
| control/cases.rb:46:12:46:12 | 3 | 3 |
| control/cases.rb:47:6:47:6 | 1 | 1 |
| control/cases.rb:47:13:47:13 | 3 | 3 |
| control/cases.rb:49:9:49:9 | 3 | 3 |
| control/cases.rb:49:12:49:12 | 4 | 4 |
| control/cases.rb:50:9:50:9 | 3 | 3 |
| control/cases.rb:51:10:51:10 | 3 | 3 |
| control/cases.rb:52:6:52:6 | :a | a |
| control/cases.rb:53:6:53:6 | :a | a |
| control/cases.rb:53:9:53:9 | 5 | 5 |
| control/cases.rb:54:6:54:6 | :a | a |
| control/cases.rb:54:9:54:9 | 5 | 5 |
| control/cases.rb:55:6:55:6 | :a | a |
| control/cases.rb:55:9:55:9 | 5 | 5 |
| control/cases.rb:55:12:55:12 | :b | b |
| control/cases.rb:56:6:56:6 | :a | a |
| control/cases.rb:56:9:56:9 | 5 | 5 |
| control/cases.rb:56:12:56:12 | :b | b |
| control/cases.rb:57:6:57:6 | :a | a |
| control/cases.rb:57:9:57:9 | 5 | 5 |
| control/cases.rb:57:12:57:12 | :b | b |
| control/cases.rb:59:7:59:7 | 5 | 5 |
| control/cases.rb:60:7:60:7 | 5 | 5 |
| control/cases.rb:61:7:61:7 | 1 | 1 |
| control/cases.rb:61:10:61:10 | 2 | 2 |
| control/cases.rb:62:7:62:7 | 1 | 1 |
| control/cases.rb:62:10:62:10 | 2 | 2 |
| control/cases.rb:63:7:63:7 | 1 | 1 |
| control/cases.rb:63:10:63:10 | 2 | 2 |
| control/cases.rb:63:13:63:13 | 3 | 3 |
| control/cases.rb:64:7:64:7 | 1 | 1 |
| control/cases.rb:64:10:64:10 | 2 | 2 |
| control/cases.rb:64:13:64:13 | 3 | 3 |
| control/cases.rb:65:7:65:7 | 1 | 1 |
| control/cases.rb:65:10:65:10 | 2 | 2 |
| control/cases.rb:65:13:65:13 | 3 | 3 |
| control/cases.rb:66:7:66:7 | 1 | 1 |
| control/cases.rb:66:14:66:14 | 3 | 3 |
| control/cases.rb:68:10:68:10 | 3 | 3 |
| control/cases.rb:68:13:68:13 | 4 | 4 |
| control/cases.rb:69:10:69:10 | 3 | 3 |
| control/cases.rb:70:11:70:11 | 3 | 3 |
| control/cases.rb:71:7:71:7 | :a | a |
| control/cases.rb:72:7:72:7 | :a | a |
| control/cases.rb:72:10:72:10 | 5 | 5 |
| control/cases.rb:73:7:73:7 | :a | a |
| control/cases.rb:73:10:73:10 | 5 | 5 |
| control/cases.rb:74:7:74:7 | :a | a |
| control/cases.rb:74:10:74:10 | 5 | 5 |
| control/cases.rb:74:13:74:13 | :b | b |
| control/cases.rb:75:7:75:7 | :a | a |
| control/cases.rb:75:10:75:10 | 5 | 5 |
| control/cases.rb:75:13:75:13 | :b | b |
| control/cases.rb:76:7:76:7 | :a | a |
| control/cases.rb:76:10:76:10 | 5 | 5 |
| control/cases.rb:76:13:76:13 | :b | b |
| control/cases.rb:84:7:84:8 | 42 | 42 |
| control/cases.rb:87:6:87:6 | 5 | 5 |
| control/cases.rb:90:6:90:13 | "string" | string |
| control/cases.rb:91:9:91:11 | "foo" | foo |
| control/cases.rb:91:13:91:15 | "bar" | bar |
| control/cases.rb:92:9:92:11 | :"foo" | foo |
| control/cases.rb:92:13:92:15 | :"bar" | bar |
| control/cases.rb:93:6:93:17 | /.*abc[0-9]/ | .*abc[0-9] |
| control/cases.rb:94:6:94:6 | 5 | 5 |
| control/cases.rb:94:11:94:12 | 10 | 10 |
| control/cases.rb:95:9:95:10 | 10 | 10 |
| control/cases.rb:96:6:96:6 | 5 | 5 |
| control/cases.rb:97:6:97:6 | 5 | 5 |
| control/cases.rb:98:6:98:6 | 5 | 5 |
| control/cases.rb:98:23:98:30 | "string" | string |
| control/cases.rb:102:6:102:8 | nil | nil |
| control/cases.rb:102:19:102:22 | true | true |
| control/cases.rb:102:26:102:30 | false | false |
| control/cases.rb:102:34:102:41 | __LINE__ | 102 |
| control/cases.rb:102:45:102:52 | __FILE__ | control/cases.rb |
| control/cases.rb:102:56:102:67 | __ENCODING__ | UTF-8 |
| control/cases.rb:103:18:103:19 | 10 | 10 |
| control/cases.rb:104:6:104:9 | :foo | foo |
| control/cases.rb:105:6:105:15 | :"foo bar" | foo bar |
| control/cases.rb:106:7:106:7 | 5 | 5 |
| control/cases.rb:106:12:106:13 | 10 | 10 |
| control/cases.rb:125:11:125:11 | 1 | 1 |
| control/cases.rb:125:14:125:14 | 2 | 2 |
| control/cases.rb:126:18:126:18 | 1 | 1 |
| control/cases.rb:134:7:134:7 | :x | x |
| control/cases.rb:135:16:135:16 | :x | x |
| control/cases.rb:135:18:135:18 | 1 | 1 |
| control/cases.rb:136:16:136:16 | :x | x |
| control/cases.rb:136:18:136:18 | 1 | 1 |
| control/cases.rb:136:21:136:21 | :a | a |
| control/cases.rb:137:11:137:11 | :y | y |
| control/cases.rb:139:11:139:11 | :a | a |
| control/cases.rb:139:14:139:14 | 1 | 1 |
| control/conditionals.rb:2:5:2:5 | 0 | 0 |
| control/conditionals.rb:3:5:3:5 | 0 | 0 |
| control/conditionals.rb:4:5:4:5 | 0 | 0 |
@@ -150,10 +275,14 @@
| control/loops.rb:9:13:9:14 | 10 | 10 |
| control/loops.rb:16:10:16:10 | 1 | 1 |
| control/loops.rb:16:13:16:14 | 10 | 10 |
| control/loops.rb:22:5:22:7 | 0 | 0 |
| control/loops.rb:22:10:22:14 | 1 | 1 |
| control/loops.rb:22:20:22:22 | :foo | foo |
| control/loops.rb:22:25:22:25 | 0 | 0 |
| control/loops.rb:22:28:22:30 | :bar | bar |
| control/loops.rb:22:33:22:33 | 1 | 1 |
| control/loops.rb:28:6:28:8 | 0 | 0 |
| control/loops.rb:28:11:28:15 | 1 | 1 |
| control/loops.rb:28:22:28:24 | :foo | foo |
| control/loops.rb:28:27:28:27 | 0 | 0 |
| control/loops.rb:28:30:28:32 | :bar | bar |

View File

@@ -6,6 +6,7 @@ splatExpr
| calls.rb:271:5:271:11 | * ... | calls.rb:271:6:271:11 | call to bar |
| calls.rb:316:31:316:42 | * ... | calls.rb:316:31:316:42 | [...] |
| calls.rb:317:14:317:22 | * ... | calls.rb:317:14:317:22 | [...] |
| calls.rb:340:1:342:3 | * ... | calls.rb:340:1:342:3 | __synth__0__1 |
hashSplatExpr
| calls.rb:274:5:274:9 | ** ... | calls.rb:274:7:274:9 | call to bar |
| calls.rb:275:5:275:12 | ** ... | calls.rb:275:7:275:12 | call to bar |

View File

@@ -90,6 +90,20 @@ callsWithArguments
| calls.rb:332:3:332:12 | call to super | super | 0 | calls.rb:332:9:332:11 | ... |
| calls.rb:336:3:336:13 | call to bar | bar | 0 | calls.rb:336:7:336:7 | b |
| calls.rb:336:3:336:13 | call to bar | bar | 1 | calls.rb:336:10:336:12 | ... |
| calls.rb:340:5:340:5 | call to [] | [] | 0 | calls.rb:340:5:340:5 | 0 |
| calls.rb:340:8:340:8 | call to [] | [] | 0 | calls.rb:340:8:340:8 | 1 |
| calls.rb:340:11:340:11 | call to [] | [] | 0 | calls.rb:340:11:340:11 | 2 |
| calls.rb:340:16:340:33 | call to [] | [] | 0 | calls.rb:340:17:340:23 | [...] |
| calls.rb:340:16:340:33 | call to [] | [] | 1 | calls.rb:340:26:340:32 | [...] |
| calls.rb:340:17:340:23 | call to [] | [] | 0 | calls.rb:340:18:340:18 | 1 |
| calls.rb:340:17:340:23 | call to [] | [] | 1 | calls.rb:340:20:340:20 | 2 |
| calls.rb:340:17:340:23 | call to [] | [] | 2 | calls.rb:340:22:340:22 | 3 |
| calls.rb:340:26:340:32 | call to [] | [] | 0 | calls.rb:340:27:340:27 | 4 |
| calls.rb:340:26:340:32 | call to [] | [] | 1 | calls.rb:340:29:340:29 | 5 |
| calls.rb:340:26:340:32 | call to [] | [] | 2 | calls.rb:340:31:340:31 | 6 |
| calls.rb:341:3:341:13 | call to foo | foo | 0 | calls.rb:341:7:341:7 | x |
| calls.rb:341:3:341:13 | call to foo | foo | 1 | calls.rb:341:10:341:10 | y |
| calls.rb:341:3:341:13 | call to foo | foo | 2 | calls.rb:341:13:341:13 | z |
callsWithReceiver
| calls.rb:2:1:2:5 | call to foo | calls.rb:2:1:2:5 | self |
| calls.rb:5:1:5:10 | call to bar | calls.rb:5:1:5:3 | Foo |
@@ -214,8 +228,10 @@ callsWithReceiver
| calls.rb:222:11:222:13 | call to foo | calls.rb:222:11:222:13 | self |
| calls.rb:223:1:223:6 | call to bar | calls.rb:223:1:223:1 | X |
| calls.rb:223:14:223:19 | call to foo | calls.rb:223:14:223:14 | X |
| calls.rb:226:1:228:3 | call to each | calls.rb:226:10:226:12 | call to bar |
| calls.rb:226:10:226:12 | call to bar | calls.rb:226:10:226:12 | self |
| calls.rb:227:3:227:5 | call to baz | calls.rb:227:3:227:5 | self |
| calls.rb:229:1:231:3 | call to each | calls.rb:229:10:229:15 | call to bar |
| calls.rb:229:10:229:15 | call to bar | calls.rb:229:10:229:10 | X |
| calls.rb:230:3:230:8 | call to baz | calls.rb:230:3:230:3 | X |
| calls.rb:234:1:234:3 | call to foo | calls.rb:234:1:234:3 | self |
@@ -328,16 +344,28 @@ callsWithReceiver
| calls.rb:328:13:328:15 | call to bar | calls.rb:328:13:328:15 | self |
| calls.rb:328:25:328:37 | call to print | calls.rb:328:25:328:37 | self |
| calls.rb:336:3:336:13 | call to bar | calls.rb:336:3:336:13 | self |
| calls.rb:340:1:342:3 | * ... | calls.rb:340:1:342:3 | __synth__0__1 |
| calls.rb:340:1:342:3 | call to each | calls.rb:340:16:340:33 | [...] |
| calls.rb:340:5:340:5 | call to [] | calls.rb:340:5:340:5 | __synth__0__1 |
| calls.rb:340:8:340:8 | call to [] | calls.rb:340:8:340:8 | __synth__0__1 |
| calls.rb:340:11:340:11 | call to [] | calls.rb:340:11:340:11 | __synth__0__1 |
| calls.rb:340:16:340:33 | call to [] | calls.rb:340:16:340:33 | Array |
| calls.rb:340:17:340:23 | call to [] | calls.rb:340:17:340:23 | Array |
| calls.rb:340:26:340:32 | call to [] | calls.rb:340:26:340:32 | Array |
| calls.rb:341:3:341:13 | call to foo | calls.rb:341:3:341:13 | self |
callsWithBlock
| calls.rb:17:1:17:17 | call to foo | calls.rb:17:5:17:17 | { ... } |
| calls.rb:20:1:22:3 | call to foo | calls.rb:20:5:22:3 | do ... end |
| calls.rb:25:1:27:3 | call to bar | calls.rb:25:16:27:3 | do ... end |
| calls.rb:92:1:92:21 | call to foo | calls.rb:92:7:92:21 | { ... } |
| calls.rb:95:1:98:3 | call to foo | calls.rb:95:7:98:3 | do ... end |
| calls.rb:226:1:228:3 | call to each | calls.rb:226:1:228:3 | { ... } |
| calls.rb:229:1:231:3 | call to each | calls.rb:229:1:231:3 | { ... } |
| calls.rb:290:5:290:23 | call to super | calls.rb:290:11:290:23 | { ... } |
| calls.rb:291:5:291:26 | call to super | calls.rb:291:11:291:26 | do ... end |
| calls.rb:292:5:292:30 | call to super | calls.rb:292:16:292:30 | { ... } |
| calls.rb:293:5:293:33 | call to super | calls.rb:293:16:293:33 | do ... end |
| calls.rb:340:1:342:3 | call to each | calls.rb:340:1:342:3 | { ... } |
yieldCalls
| calls.rb:31:3:31:7 | yield ... |
| calls.rb:36:3:36:16 | yield ... |

View File

@@ -335,3 +335,8 @@ end
def foo(a, b, ...)
bar(b, ...)
end
# for loop over nested array
for x, y, z in [[1,2,3], [4,5,6]]
foo x, y, z
end

View File

@@ -0,0 +1,4 @@
deadEnd
| cases.rb:19:13:19:19 | then ... |
| cases.rb:20:13:20:19 | then ... |
| cases.rb:21:13:21:19 | then ... |

View File

@@ -1,11 +1,25 @@
caseValues
| cases.rb:8:1:15:3 | case ... | cases.rb:8:6:8:6 | a |
| cases.rb:26:1:29:3 | case ... | cases.rb:26:6:26:9 | call to expr |
| cases.rb:31:1:37:3 | case ... | cases.rb:31:6:31:9 | call to expr |
| cases.rb:39:1:80:3 | case ... | cases.rb:39:6:39:9 | call to expr |
| cases.rb:86:1:107:3 | case ... | cases.rb:86:6:86:9 | call to expr |
| cases.rb:111:1:119:3 | case ... | cases.rb:111:6:111:9 | call to expr |
| cases.rb:123:1:128:3 | case ... | cases.rb:123:6:123:9 | call to expr |
| cases.rb:132:1:140:3 | case ... | cases.rb:132:6:132:9 | call to expr |
caseNoValues
| cases.rb:18:1:22:3 | case ... |
caseElseBranches
| cases.rb:8:1:15:3 | case ... | cases.rb:13:1:14:7 | else ... |
| cases.rb:26:1:29:3 | case ... | cases.rb:28:3:28:12 | else ... |
| cases.rb:31:1:37:3 | case ... | cases.rb:36:3:36:12 | else ... |
caseNoElseBranches
| cases.rb:18:1:22:3 | case ... |
| cases.rb:39:1:80:3 | case ... |
| cases.rb:86:1:107:3 | case ... |
| cases.rb:111:1:119:3 | case ... |
| cases.rb:123:1:128:3 | case ... |
| cases.rb:132:1:140:3 | case ... |
caseWhenBranches
| cases.rb:8:1:15:3 | case ... | cases.rb:9:1:10:7 | when ... | 0 | cases.rb:9:6:9:6 | b | cases.rb:9:7:10:7 | then ... |
| cases.rb:8:1:15:3 | case ... | cases.rb:11:1:12:7 | when ... | 0 | cases.rb:11:6:11:6 | c | cases.rb:11:10:12:7 | then ... |
@@ -20,3 +34,86 @@ caseAllBranches
| cases.rb:18:1:22:3 | case ... | 0 | cases.rb:19:1:19:19 | when ... |
| cases.rb:18:1:22:3 | case ... | 1 | cases.rb:20:1:20:19 | when ... |
| cases.rb:18:1:22:3 | case ... | 2 | cases.rb:21:1:21:19 | when ... |
| cases.rb:26:1:29:3 | case ... | 0 | cases.rb:27:3:27:16 | in ... then ... |
| cases.rb:26:1:29:3 | case ... | 1 | cases.rb:28:3:28:12 | else ... |
| cases.rb:31:1:37:3 | case ... | 0 | cases.rb:32:3:33:11 | in ... then ... |
| cases.rb:31:1:37:3 | case ... | 1 | cases.rb:34:3:35:11 | in ... then ... |
| cases.rb:31:1:37:3 | case ... | 2 | cases.rb:36:3:36:12 | else ... |
| cases.rb:39:1:80:3 | case ... | 0 | cases.rb:40:3:40:6 | in ... then ... |
| cases.rb:39:1:80:3 | case ... | 1 | cases.rb:41:3:41:8 | in ... then ... |
| cases.rb:39:1:80:3 | case ... | 2 | cases.rb:42:3:42:10 | in ... then ... |
| cases.rb:39:1:80:3 | case ... | 3 | cases.rb:43:3:43:11 | in ... then ... |
| cases.rb:39:1:80:3 | case ... | 4 | cases.rb:44:3:44:13 | in ... then ... |
| cases.rb:39:1:80:3 | case ... | 5 | cases.rb:45:3:45:14 | in ... then ... |
| cases.rb:39:1:80:3 | case ... | 6 | cases.rb:46:3:46:16 | in ... then ... |
| cases.rb:39:1:80:3 | case ... | 7 | cases.rb:47:3:47:14 | in ... then ... |
| cases.rb:39:1:80:3 | case ... | 8 | cases.rb:48:3:48:7 | in ... then ... |
| cases.rb:39:1:80:3 | case ... | 9 | cases.rb:49:3:49:13 | in ... then ... |
| cases.rb:39:1:80:3 | case ... | 10 | cases.rb:50:3:50:13 | in ... then ... |
| cases.rb:39:1:80:3 | case ... | 11 | cases.rb:51:3:51:15 | in ... then ... |
| cases.rb:39:1:80:3 | case ... | 12 | cases.rb:52:3:52:8 | in ... then ... |
| cases.rb:39:1:80:3 | case ... | 13 | cases.rb:53:3:53:10 | in ... then ... |
| cases.rb:39:1:80:3 | case ... | 14 | cases.rb:54:3:54:11 | in ... then ... |
| cases.rb:39:1:80:3 | case ... | 15 | cases.rb:55:3:55:18 | in ... then ... |
| cases.rb:39:1:80:3 | case ... | 16 | cases.rb:56:3:56:21 | in ... then ... |
| cases.rb:39:1:80:3 | case ... | 17 | cases.rb:57:3:57:21 | in ... then ... |
| cases.rb:39:1:80:3 | case ... | 18 | cases.rb:58:3:58:11 | in ... then ... |
| cases.rb:39:1:80:3 | case ... | 19 | cases.rb:59:3:59:9 | in ... then ... |
| cases.rb:39:1:80:3 | case ... | 20 | cases.rb:60:3:60:10 | in ... then ... |
| cases.rb:39:1:80:3 | case ... | 21 | cases.rb:61:3:61:12 | in ... then ... |
| cases.rb:39:1:80:3 | case ... | 22 | cases.rb:62:3:62:13 | in ... then ... |
| cases.rb:39:1:80:3 | case ... | 23 | cases.rb:63:3:63:15 | in ... then ... |
| cases.rb:39:1:80:3 | case ... | 24 | cases.rb:64:3:64:16 | in ... then ... |
| cases.rb:39:1:80:3 | case ... | 25 | cases.rb:65:3:65:18 | in ... then ... |
| cases.rb:39:1:80:3 | case ... | 26 | cases.rb:66:3:66:16 | in ... then ... |
| cases.rb:39:1:80:3 | case ... | 27 | cases.rb:67:3:67:9 | in ... then ... |
| cases.rb:39:1:80:3 | case ... | 28 | cases.rb:68:3:68:15 | in ... then ... |
| cases.rb:39:1:80:3 | case ... | 29 | cases.rb:69:3:69:15 | in ... then ... |
| cases.rb:39:1:80:3 | case ... | 30 | cases.rb:70:3:70:17 | in ... then ... |
| cases.rb:39:1:80:3 | case ... | 31 | cases.rb:71:3:71:10 | in ... then ... |
| cases.rb:39:1:80:3 | case ... | 32 | cases.rb:72:3:72:12 | in ... then ... |
| cases.rb:39:1:80:3 | case ... | 33 | cases.rb:73:3:73:13 | in ... then ... |
| cases.rb:39:1:80:3 | case ... | 34 | cases.rb:74:3:74:20 | in ... then ... |
| cases.rb:39:1:80:3 | case ... | 35 | cases.rb:75:3:75:23 | in ... then ... |
| cases.rb:39:1:80:3 | case ... | 36 | cases.rb:76:3:76:23 | in ... then ... |
| cases.rb:39:1:80:3 | case ... | 37 | cases.rb:77:3:77:13 | in ... then ... |
| cases.rb:39:1:80:3 | case ... | 38 | cases.rb:78:3:78:8 | in ... then ... |
| cases.rb:39:1:80:3 | case ... | 39 | cases.rb:79:3:79:8 | in ... then ... |
| cases.rb:86:1:107:3 | case ... | 0 | cases.rb:87:3:87:6 | in ... then ... |
| cases.rb:86:1:107:3 | case ... | 1 | cases.rb:88:3:88:9 | in ... then ... |
| cases.rb:86:1:107:3 | case ... | 2 | cases.rb:89:3:89:8 | in ... then ... |
| cases.rb:86:1:107:3 | case ... | 3 | cases.rb:90:3:90:13 | in ... then ... |
| cases.rb:86:1:107:3 | case ... | 4 | cases.rb:91:3:91:16 | in ... then ... |
| cases.rb:86:1:107:3 | case ... | 5 | cases.rb:92:3:92:16 | in ... then ... |
| cases.rb:86:1:107:3 | case ... | 6 | cases.rb:93:3:93:17 | in ... then ... |
| cases.rb:86:1:107:3 | case ... | 7 | cases.rb:94:3:94:12 | in ... then ... |
| cases.rb:86:1:107:3 | case ... | 8 | cases.rb:95:3:95:10 | in ... then ... |
| cases.rb:86:1:107:3 | case ... | 9 | cases.rb:96:3:96:9 | in ... then ... |
| cases.rb:86:1:107:3 | case ... | 10 | cases.rb:97:3:97:11 | in ... then ... |
| cases.rb:86:1:107:3 | case ... | 11 | cases.rb:98:3:98:30 | in ... then ... |
| cases.rb:86:1:107:3 | case ... | 12 | cases.rb:99:3:99:8 | in ... then ... |
| cases.rb:86:1:107:3 | case ... | 13 | cases.rb:100:3:100:13 | in ... then ... |
| cases.rb:86:1:107:3 | case ... | 14 | cases.rb:101:3:101:15 | in ... then ... |
| cases.rb:86:1:107:3 | case ... | 15 | cases.rb:102:3:102:67 | in ... then ... |
| cases.rb:86:1:107:3 | case ... | 16 | cases.rb:103:3:103:21 | in ... then ... |
| cases.rb:86:1:107:3 | case ... | 17 | cases.rb:104:3:104:9 | in ... then ... |
| cases.rb:86:1:107:3 | case ... | 18 | cases.rb:105:3:105:15 | in ... then ... |
| cases.rb:86:1:107:3 | case ... | 19 | cases.rb:106:3:106:13 | in ... then ... |
| cases.rb:111:1:119:3 | case ... | 0 | cases.rb:112:3:112:8 | in ... then ... |
| cases.rb:111:1:119:3 | case ... | 1 | cases.rb:113:3:113:9 | in ... then ... |
| cases.rb:111:1:119:3 | case ... | 2 | cases.rb:114:3:114:11 | in ... then ... |
| cases.rb:111:1:119:3 | case ... | 3 | cases.rb:115:3:115:16 | in ... then ... |
| cases.rb:111:1:119:3 | case ... | 4 | cases.rb:116:3:116:11 | in ... then ... |
| cases.rb:111:1:119:3 | case ... | 5 | cases.rb:117:3:117:12 | in ... then ... |
| cases.rb:111:1:119:3 | case ... | 6 | cases.rb:118:3:118:25 | in ... then ... |
| cases.rb:123:1:128:3 | case ... | 0 | cases.rb:124:3:124:15 | in ... then ... |
| cases.rb:123:1:128:3 | case ... | 1 | cases.rb:125:3:125:20 | in ... then ... |
| cases.rb:123:1:128:3 | case ... | 2 | cases.rb:126:3:126:23 | in ... then ... |
| cases.rb:123:1:128:3 | case ... | 3 | cases.rb:127:3:127:20 | in ... then ... |
| cases.rb:132:1:140:3 | case ... | 0 | cases.rb:133:3:133:8 | in ... then ... |
| cases.rb:132:1:140:3 | case ... | 1 | cases.rb:134:3:134:10 | in ... then ... |
| cases.rb:132:1:140:3 | case ... | 2 | cases.rb:135:3:135:21 | in ... then ... |
| cases.rb:132:1:140:3 | case ... | 3 | cases.rb:136:3:136:33 | in ... then ... |
| cases.rb:132:1:140:3 | case ... | 4 | cases.rb:137:3:137:14 | in ... then ... |
| cases.rb:132:1:140:3 | case ... | 5 | cases.rb:138:3:138:15 | in ... then ... |
| cases.rb:132:1:140:3 | case ... | 6 | cases.rb:139:3:139:23 | in ... then ... |

View File

@@ -11,7 +11,7 @@ query predicate caseElseBranches(CaseExpr c, StmtSequence elseBranch) {
query predicate caseNoElseBranches(CaseExpr c) { not exists(c.getElseBranch()) }
query predicate caseWhenBranches(CaseExpr c, WhenExpr when, int pIndex, Expr p, StmtSequence body) {
when = c.getAWhenBranch() and
when = c.getABranch() and
p = when.getPattern(pIndex) and
body = when.getBody()
}

View File

@@ -1,5 +1,12 @@
| cases.rb:8:1:15:3 | case ... | CaseExpr |
| cases.rb:18:1:22:3 | case ... | CaseExpr |
| cases.rb:26:1:29:3 | case ... | CaseExpr |
| cases.rb:31:1:37:3 | case ... | CaseExpr |
| cases.rb:39:1:80:3 | case ... | CaseExpr |
| cases.rb:86:1:107:3 | case ... | CaseExpr |
| cases.rb:111:1:119:3 | case ... | CaseExpr |
| cases.rb:123:1:128:3 | case ... | CaseExpr |
| cases.rb:132:1:140:3 | case ... | CaseExpr |
| conditionals.rb:10:1:12:3 | if ... | IfExpr |
| conditionals.rb:15:1:19:3 | if ... | IfExpr |
| conditionals.rb:22:1:30:3 | if ... | IfExpr |

View File

@@ -19,4 +19,123 @@ case
when a > b then 10
when a == b then 20
when a < b then 30
end
end
# pattern matching
case expr
in 5 then true
else false
end
case expr
in x unless x < 0
then true
in x if x < 0
then true
else false
end
case expr
in 5
in 5,
in 1, 2
in 1, 2,
in 1, 2, 3
in 1, 2, 3,
in 1, 2, 3, *
in 1, *x, 3
in *
in *, 3, 4
in *, 3, *
in *a, 3, *b
in a:
in a: 5
in a: 5,
in a: 5, b:, **
in a: 5, b:, **map
in a: 5, b:, **nil
in **nil
in [5]
in [5,]
in [1, 2]
in [1, 2,]
in [1, 2, 3]
in [1, 2, 3,]
in [1, 2, 3, *]
in [1, *x, 3]
in [*]
in [*, 3, 4]
in [*, 3, *]
in [*a, 3, *b]
in {a:}
in {a: 5}
in {a: 5,}
in {a: 5, b:, **}
in {a: 5, b:, **map}
in {a: 5, b:, **nil}
in {**nil}
in {}
in []
end
# more pattern matching
foo = 42
case expr
in 5
in ^foo
in var
in "string"
in %w(foo bar)
in %i(foo bar)
in /.*abc[0-9]/
in 5 .. 10
in .. 10
in 5 ..
in 5 => x
in 5 | ^foo | var | "string"
in Foo
in Foo::Bar
in ::Foo::Bar
in nil | self | true | false | __LINE__ | __FILE__ | __ENCODING__
in -> x { x == 10 }
in :foo
in :"foo bar"
in -5 | +10
end
# array patterns
case expr
in [];
in [x];
in [x, ];
in Foo::Bar[];
in Foo();
in Bar(*);
in Bar(a, b, *c, d, e);
end
# find patterns
case expr
in [*, x, *];
in [*x, 1, 2, *y];
in Foo::Bar[*, 1, *];
in Foo(*, Bar, *);
end
# hash patterns
case expr
in {};
in {x:};
in Foo::Bar[ x:1 ];
in Foo::Bar[ x:1, a:, **rest ];
in Foo( y:);
in Bar( ** );
in Bar( a: 1, **nil);
end

View File

@@ -33,6 +33,8 @@ idParams
| params.rb:70:35:70:35 | a | a |
| params.rb:70:38:70:38 | b | b |
| params.rb:70:48:70:48 | c | c |
| params.rb:73:27:73:32 | wibble | wibble |
| params.rb:77:16:77:18 | val | val |
blockParams
| params.rb:46:28:46:33 | &block | block |
| params.rb:62:29:62:34 | &block | block |
@@ -83,6 +85,8 @@ paramsInMethods
| params.rb:58:1:59:3 | method_with_optional_params | 1 | params.rb:58:39:58:42 | val2 | OptionalParameter |
| params.rb:58:1:59:3 | method_with_optional_params | 2 | params.rb:58:49:58:52 | val3 | OptionalParameter |
| params.rb:62:1:64:3 | use_block_with_optional | 0 | params.rb:62:29:62:34 | &block | BlockParameter |
| params.rb:73:1:74:3 | method_with_nil_splat | 0 | params.rb:73:27:73:32 | wibble | SimpleParameter |
| params.rb:73:1:74:3 | method_with_nil_splat | 1 | params.rb:73:35:73:39 | **nil | HashSplatNilParameter |
paramsInBlocks
| params.rb:9:11:11:3 | do ... end | 0 | params.rb:9:15:9:17 | key | SimpleParameter |
| params.rb:9:11:11:3 | do ... end | 1 | params.rb:9:20:9:24 | value | SimpleParameter |
@@ -94,6 +98,8 @@ paramsInBlocks
| params.rb:49:24:51:3 | do ... end | 1 | params.rb:49:33:49:34 | yy | KeywordParameter |
| params.rb:65:25:67:3 | do ... end | 0 | params.rb:65:29:65:32 | name | SimpleParameter |
| params.rb:65:25:67:3 | do ... end | 1 | params.rb:65:35:65:37 | age | OptionalParameter |
| params.rb:77:12:78:3 | do ... end | 0 | params.rb:77:16:77:18 | val | SimpleParameter |
| params.rb:77:12:78:3 | do ... end | 1 | params.rb:77:21:77:25 | **nil | HashSplatNilParameter |
paramsInLambdas
| params.rb:14:7:14:33 | -> { ... } | 0 | params.rb:14:11:14:13 | foo | SimpleParameter |
| params.rb:14:7:14:33 | -> { ... } | 1 | params.rb:14:16:14:18 | bar | SimpleParameter |
@@ -147,3 +153,7 @@ params
| params.rb:70:35:70:35 | a | 0 | SimpleParameter |
| params.rb:70:38:70:38 | b | 1 | OptionalParameter |
| params.rb:70:48:70:48 | c | 2 | OptionalParameter |
| params.rb:73:27:73:32 | wibble | 0 | SimpleParameter |
| params.rb:73:35:73:39 | **nil | 1 | HashSplatNilParameter |
| params.rb:77:16:77:18 | val | 0 | SimpleParameter |
| params.rb:77:21:77:25 | **nil | 1 | HashSplatNilParameter |

View File

@@ -67,4 +67,13 @@ use_block_with_optional do |name, age = 99|
end
# Lambda containing optional parameters
lambda_with_optional_params = -> (a, b = 1000, c = 20) { a+b+c }
lambda_with_optional_params = -> (a, b = 1000, c = 20) { a+b+c }
# Method containing nil hash-splat params
def method_with_nil_splat(wibble, **nil)
end
# Block with nil hash-splat parameter
array.each do |val, **nil|
end

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,331 @@
callsWithNoArguments
| break_ensure.rb:8:6:8:13 | [ensure: raise] call to elements |
| break_ensure.rb:8:6:8:13 | call to elements |
| break_ensure.rb:8:6:8:18 | [ensure: raise] call to nil? |
| break_ensure.rb:8:6:8:18 | call to nil? |
| break_ensure.rb:20:10:20:15 | [ensure: break] call to nil? |
| break_ensure.rb:20:10:20:15 | [ensure: raise] call to nil? |
| break_ensure.rb:20:10:20:15 | call to nil? |
| break_ensure.rb:29:8:29:13 | call to nil? |
| case.rb:2:8:2:9 | call to x1 |
| case.rb:3:21:3:22 | call to x2 |
| cfg.html.erb:12:24:12:24 | call to a |
| cfg.html.erb:15:32:15:32 | call to a |
| cfg.html.erb:16:32:16:32 | call to b |
| cfg.html.erb:18:17:18:22 | call to admin? |
| cfg.html.erb:19:32:19:32 | call to d |
| cfg.html.erb:21:32:21:32 | call to e |
| cfg.html.erb:29:8:29:17 | call to collection |
| cfg.html.erb:29:8:31:10 | call to each |
| cfg.rb:25:1:25:22 | call to times |
| cfg.rb:29:1:29:24 | call to new |
| cfg.rb:29:17:29:22 | call to call |
| cfg.rb:60:17:60:17 | call to b |
| cfg.rb:62:7:62:12 | * ... |
| cfg.rb:62:17:62:27 | * ... |
| cfg.rb:90:1:93:3 | call to each |
| cfg.rb:98:10:98:15 | ** ... |
| cfg.rb:98:30:98:35 | ** ... |
| cfg.rb:138:17:138:23 | * ... |
| cfg.rb:141:1:141:8 | call to itself |
| cfg.rb:143:10:143:21 | call to itself |
| cfg.rb:147:10:147:14 | call to super |
| cfg.rb:147:10:147:22 | call to print |
| cfg.rb:151:9:151:17 | call to new |
| cfg.rb:158:16:158:21 | * ... |
| cfg.rb:160:22:160:29 | call to __FILE__ |
| cfg.rb:167:5:167:10 | ! ... |
| cfg.rb:168:5:168:8 | - ... |
| cfg.rb:194:1:194:23 | call to run_block |
| desugar.rb:6:3:6:7 | call to foo |
| desugar.rb:10:3:10:7 | call to foo |
| desugar.rb:14:3:14:7 | call to foo |
| desugar.rb:14:3:14:13 | call to count |
| desugar.rb:18:3:18:7 | call to foo |
| desugar.rb:18:12:18:16 | call to bar |
| desugar.rb:18:19:18:23 | call to baz |
| desugar.rb:22:10:22:10 | call to z |
| desugar.rb:22:18:22:29 | * ... |
| desugar.rb:26:6:26:11 | * ... |
| desugar.rb:26:15:26:25 | * ... |
| ifs.rb:4:30:4:38 | [false] ! ... |
| ifs.rb:4:30:4:38 | [true] ! ... |
| ifs.rb:20:9:20:10 | - ... |
| ifs.rb:41:6:41:10 | [false] ! ... |
| loops.rb:25:3:27:5 | call to each |
| raise.rb:155:3:155:50 | call to each |
| raise.rb:155:37:155:48 | call to nil? |
| raise.rb:159:3:163:5 | call to foo |
positionalArguments
| break_ensure.rb:2:9:2:13 | ... < ... | break_ensure.rb:2:13:2:13 | 0 |
| break_ensure.rb:3:8:3:12 | ... > ... | break_ensure.rb:3:12:3:12 | 0 |
| break_ensure.rb:9:5:9:23 | [ensure: raise] call to puts | break_ensure.rb:9:10:9:23 | [ensure: raise] "elements nil" |
| break_ensure.rb:9:5:9:23 | call to puts | break_ensure.rb:9:10:9:23 | "elements nil" |
| break_ensure.rb:14:9:14:13 | ... < ... | break_ensure.rb:14:13:14:13 | 0 |
| break_ensure.rb:16:10:16:14 | ... > ... | break_ensure.rb:16:14:16:14 | 0 |
| break_ensure.rb:21:9:21:20 | [ensure: break] call to puts | break_ensure.rb:21:14:21:20 | [ensure: break] "y nil" |
| break_ensure.rb:21:9:21:20 | [ensure: raise] call to puts | break_ensure.rb:21:14:21:20 | [ensure: raise] "y nil" |
| break_ensure.rb:21:9:21:20 | call to puts | break_ensure.rb:21:14:21:20 | "y nil" |
| break_ensure.rb:33:11:33:15 | ... < ... | break_ensure.rb:33:15:33:15 | 0 |
| break_ensure.rb:33:11:33:15 | [ensure: raise] ... < ... | break_ensure.rb:33:15:33:15 | [ensure: raise] 0 |
| break_ensure.rb:33:11:33:15 | [ensure: return] ... < ... | break_ensure.rb:33:15:33:15 | [ensure: return] 0 |
| break_ensure.rb:35:12:35:16 | ... > ... | break_ensure.rb:35:16:35:16 | 0 |
| break_ensure.rb:35:12:35:16 | [ensure: raise] ... > ... | break_ensure.rb:35:16:35:16 | [ensure: raise] 0 |
| break_ensure.rb:35:12:35:16 | [ensure: return] ... > ... | break_ensure.rb:35:16:35:16 | [ensure: return] 0 |
| break_ensure.rb:41:3:41:13 | call to puts | break_ensure.rb:41:8:41:13 | "Done" |
| break_ensure.rb:45:9:45:13 | ... < ... | break_ensure.rb:45:13:45:13 | 0 |
| break_ensure.rb:47:10:47:14 | ... > ... | break_ensure.rb:47:14:47:14 | 1 |
| break_ensure.rb:48:9:48:16 | call to raise | break_ensure.rb:48:15:48:16 | "" |
| break_ensure.rb:51:10:51:14 | ... > ... | break_ensure.rb:51:14:51:14 | 0 |
| break_ensure.rb:51:10:51:14 | [ensure: raise] ... > ... | break_ensure.rb:51:14:51:14 | [ensure: raise] 0 |
| case.rb:3:29:3:37 | call to puts | case.rb:3:34:3:37 | "x2" |
| case.rb:4:17:4:24 | call to puts | case.rb:4:22:4:24 | "2" |
| cfg.html.erb:6:9:6:58 | call to stylesheet_link_tag | cfg.html.erb:6:29:6:41 | "application" |
| cfg.html.erb:6:9:6:58 | call to stylesheet_link_tag | cfg.html.erb:6:44:6:58 | Pair |
| cfg.html.erb:12:11:12:33 | call to link_to | cfg.html.erb:12:19:12:21 | "A" |
| cfg.html.erb:12:11:12:33 | call to link_to | cfg.html.erb:12:24:12:24 | call to a |
| cfg.html.erb:12:11:12:33 | call to link_to | cfg.html.erb:12:27:12:33 | Pair |
| cfg.html.erb:15:19:15:32 | call to link_to | cfg.html.erb:15:27:15:29 | "B" |
| cfg.html.erb:15:19:15:32 | call to link_to | cfg.html.erb:15:32:15:32 | call to a |
| cfg.html.erb:16:19:16:32 | call to link_to | cfg.html.erb:16:27:16:29 | "C" |
| cfg.html.erb:16:19:16:32 | call to link_to | cfg.html.erb:16:32:16:32 | call to b |
| cfg.html.erb:19:19:19:32 | call to link_to | cfg.html.erb:19:27:19:29 | "D" |
| cfg.html.erb:19:19:19:32 | call to link_to | cfg.html.erb:19:32:19:32 | call to d |
| cfg.html.erb:21:19:21:32 | call to link_to | cfg.html.erb:21:27:21:29 | "E" |
| cfg.html.erb:21:19:21:32 | call to link_to | cfg.html.erb:21:32:21:32 | call to e |
| cfg.rb:7:1:7:21 | call to [] | cfg.rb:7:4:7:12 | :"one#{...}" |
| cfg.rb:7:1:7:21 | call to [] | cfg.rb:7:14:7:20 | :"another" |
| cfg.rb:9:1:9:21 | call to [] | cfg.rb:9:4:9:12 | "one#{...}" |
| cfg.rb:9:1:9:21 | call to [] | cfg.rb:9:14:9:20 | "another" |
| cfg.rb:12:3:12:8 | call to puts | cfg.rb:12:8:12:8 | 4 |
| cfg.rb:16:3:16:14 | call to puts | cfg.rb:16:8:16:14 | "hello" |
| cfg.rb:23:1:23:6 | ... + ... | cfg.rb:23:6:23:6 | 1 |
| cfg.rb:25:15:25:20 | call to puts | cfg.rb:25:20:25:20 | x |
| cfg.rb:27:1:27:11 | call to puts | cfg.rb:27:6:27:11 | &... |
| cfg.rb:39:1:39:12 | call to puts | cfg.rb:39:11:39:12 | 42 |
| cfg.rb:42:15:42:24 | call to puts | cfg.rb:42:20:42:24 | "one" |
| cfg.rb:43:21:43:31 | call to puts | cfg.rb:43:26:43:31 | "some" |
| cfg.rb:44:8:44:18 | call to puts | cfg.rb:44:13:44:18 | "many" |
| cfg.rb:48:8:48:13 | ... == ... | cfg.rb:48:13:48:13 | 1 |
| cfg.rb:48:20:48:29 | call to puts | cfg.rb:48:25:48:29 | "one" |
| cfg.rb:49:8:49:13 | ... == ... | cfg.rb:49:13:49:13 | 0 |
| cfg.rb:49:16:49:20 | ... > ... | cfg.rb:49:20:49:20 | 1 |
| cfg.rb:49:27:49:37 | call to puts | cfg.rb:49:32:49:37 | "some" |
| cfg.rb:60:17:60:22 | ... < ... | cfg.rb:60:21:60:22 | 10 |
| cfg.rb:62:4:62:4 | call to [] | cfg.rb:62:4:62:4 | 0 |
| cfg.rb:62:7:62:12 | call to [] | cfg.rb:62:7:62:12 | 1 |
| cfg.rb:62:8:62:8 | call to [] | cfg.rb:62:8:62:8 | 0 |
| cfg.rb:62:11:62:11 | call to [] | cfg.rb:62:11:62:11 | 1 |
| cfg.rb:62:17:62:27 | call to [] | cfg.rb:62:18:62:18 | 1 |
| cfg.rb:62:17:62:27 | call to [] | cfg.rb:62:21:62:26 | call to [] |
| cfg.rb:62:21:62:26 | call to [] | cfg.rb:62:22:62:22 | 2 |
| cfg.rb:62:21:62:26 | call to [] | cfg.rb:62:25:62:25 | 3 |
| cfg.rb:64:5:64:10 | call to puts | cfg.rb:64:10:64:10 | a |
| cfg.rb:65:5:65:10 | call to puts | cfg.rb:65:10:65:10 | b |
| cfg.rb:67:11:67:19 | call to [] | cfg.rb:67:12:67:12 | 1 |
| cfg.rb:67:11:67:19 | call to [] | cfg.rb:67:15:67:15 | 2 |
| cfg.rb:67:11:67:19 | call to [] | cfg.rb:67:18:67:18 | 3 |
| cfg.rb:68:3:68:15 | call to puts | cfg.rb:68:8:68:15 | ...[...] |
| cfg.rb:68:8:68:15 | ...[...] | cfg.rb:68:14:68:14 | 2 |
| cfg.rb:70:5:70:16 | call to puts | cfg.rb:70:10:70:16 | "silly" |
| cfg.rb:75:4:75:8 | ... < ... | cfg.rb:75:8:75:8 | 0 |
| cfg.rb:75:23:75:28 | ... > ... | cfg.rb:75:27:75:28 | 10 |
| cfg.rb:83:3:83:11 | call to puts | cfg.rb:83:8:83:11 | "ok" |
| cfg.rb:85:3:85:12 | call to puts | cfg.rb:85:8:85:12 | "end" |
| cfg.rb:90:10:90:26 | call to [] | cfg.rb:90:11:90:13 | 1.4 |
| cfg.rb:90:10:90:26 | call to [] | cfg.rb:90:16:90:18 | 2.5 |
| cfg.rb:90:10:90:26 | call to [] | cfg.rb:90:21:90:25 | 3.4e5 |
| cfg.rb:91:6:91:10 | ... > ... | cfg.rb:91:10:91:10 | 3 |
| cfg.rb:92:3:92:8 | call to puts | cfg.rb:92:8:92:8 | x |
| cfg.rb:102:3:102:12 | call to puts | cfg.rb:102:8:102:12 | value |
| cfg.rb:103:10:103:20 | ...[...] | cfg.rb:103:17:103:19 | key |
| cfg.rb:108:1:108:12 | call to puts | cfg.rb:108:6:108:12 | ( ... ) |
| cfg.rb:113:1:113:9 | call to puts | cfg.rb:113:6:113:9 | "hi" |
| cfg.rb:113:14:113:19 | ... > ... | cfg.rb:113:18:113:19 | 10 |
| cfg.rb:120:21:120:26 | call to [] | cfg.rb:120:22:120:22 | y |
| cfg.rb:120:21:120:26 | call to [] | cfg.rb:120:25:120:25 | x |
| cfg.rb:125:7:125:8 | ... + ... | cfg.rb:125:10:125:11 | 10 |
| cfg.rb:128:9:128:12 | ... / ... | cfg.rb:128:11:128:12 | 3r |
| cfg.rb:128:9:128:19 | ... + ... | cfg.rb:128:16:128:19 | ... / ... |
| cfg.rb:128:16:128:19 | ... / ... | cfg.rb:128:18:128:19 | 6r |
| cfg.rb:136:1:136:3 | ... / ... | cfg.rb:136:3:136:3 | 0 |
| cfg.rb:136:12:136:29 | call to puts | cfg.rb:136:17:136:29 | "div by zero" |
| cfg.rb:138:3:138:6 | call to [] | cfg.rb:138:3:138:6 | _ .. _ |
| cfg.rb:138:9:138:12 | call to [] | cfg.rb:138:9:138:12 | -1 |
| cfg.rb:146:5:146:20 | call to puts | cfg.rb:146:10:146:20 | "singleton" |
| cfg.rb:147:5:147:22 | call to puts | cfg.rb:147:10:147:22 | call to print |
| cfg.rb:153:3:153:8 | call to puts | cfg.rb:153:8:153:8 | x |
| cfg.rb:158:1:158:22 | call to two_parameters | cfg.rb:158:16:158:21 | * ... |
| cfg.rb:158:17:158:21 | call to [] | cfg.rb:158:18:158:18 | 1 |
| cfg.rb:158:17:158:21 | call to [] | cfg.rb:158:20:158:20 | 2 |
| cfg.rb:164:33:164:39 | ... + ... | cfg.rb:164:38:164:39 | 13 |
| cfg.rb:172:8:172:14 | ... == ... | cfg.rb:172:13:172:14 | 10 |
| cfg.rb:172:21:172:29 | call to puts | cfg.rb:172:26:172:29 | "hi" |
| cfg.rb:172:36:172:45 | call to puts | cfg.rb:172:41:172:45 | "bye" |
| cfg.rb:174:1:174:9 | call to puts | cfg.rb:174:6:174:9 | "hi" |
| cfg.rb:174:18:174:23 | ... == ... | cfg.rb:174:23:174:23 | 0 |
| cfg.rb:176:7:176:12 | ... > ... | cfg.rb:176:11:176:12 | 10 |
| cfg.rb:176:19:176:20 | ... + ... | cfg.rb:176:22:176:23 | 10 |
| cfg.rb:176:26:176:37 | call to puts | cfg.rb:176:31:176:37 | "hello" |
| cfg.rb:179:2:179:13 | call to puts | cfg.rb:179:7:179:13 | "hello" |
| cfg.rb:179:18:179:19 | ... + ... | cfg.rb:179:21:179:21 | 1 |
| cfg.rb:179:30:179:36 | ... == ... | cfg.rb:179:35:179:36 | 10 |
| cfg.rb:182:7:182:12 | ... < ... | cfg.rb:182:11:182:12 | 10 |
| cfg.rb:183:5:183:6 | ... + ... | cfg.rb:183:8:183:8 | 1 |
| cfg.rb:184:6:184:11 | ... == ... | cfg.rb:184:11:184:11 | 5 |
| cfg.rb:185:3:185:8 | call to puts | cfg.rb:185:8:185:8 | x |
| cfg.rb:188:2:188:13 | call to puts | cfg.rb:188:7:188:13 | "hello" |
| cfg.rb:188:18:188:19 | ... - ... | cfg.rb:188:21:188:21 | 1 |
| cfg.rb:188:30:188:35 | ... != ... | cfg.rb:188:35:188:35 | 0 |
| cfg.rb:191:3:191:10 | yield ... | cfg.rb:191:9:191:10 | 42 |
| cfg.rb:194:16:194:21 | call to puts | cfg.rb:194:21:194:21 | x |
| cfg.rb:197:3:197:13 | call to bar | cfg.rb:197:7:197:7 | b |
| desugar.rb:2:5:2:6 | ... + ... | desugar.rb:2:8:2:8 | 1 |
| desugar.rb:6:3:6:13 | call to count= | desugar.rb:6:3:6:13 | ... = ... |
| desugar.rb:10:3:10:10 | call to []= | desugar.rb:10:3:10:10 | ... = ... |
| desugar.rb:10:3:10:10 | call to []= | desugar.rb:10:9:10:9 | 0 |
| desugar.rb:14:3:14:13 | call to count= | desugar.rb:14:15:14:16 | __synth__1 |
| desugar.rb:14:15:14:16 | ... + ... | desugar.rb:14:18:14:18 | 1 |
| desugar.rb:18:3:18:28 | call to [] | desugar.rb:18:9:18:9 | __synth__1 |
| desugar.rb:18:3:18:28 | call to [] | desugar.rb:18:12:18:16 | __synth__2 |
| desugar.rb:18:3:18:28 | call to [] | desugar.rb:18:19:18:27 | __synth__3 |
| desugar.rb:18:3:18:28 | call to []= | desugar.rb:18:9:18:9 | __synth__1 |
| desugar.rb:18:3:18:28 | call to []= | desugar.rb:18:12:18:16 | __synth__2 |
| desugar.rb:18:3:18:28 | call to []= | desugar.rb:18:19:18:27 | __synth__3 |
| desugar.rb:18:3:18:28 | call to []= | desugar.rb:18:30:18:31 | __synth__4 |
| desugar.rb:18:19:18:27 | ... + ... | desugar.rb:18:27:18:27 | 3 |
| desugar.rb:18:30:18:31 | ... + ... | desugar.rb:18:33:18:33 | 1 |
| desugar.rb:22:3:22:3 | call to [] | desugar.rb:22:3:22:3 | 0 |
| desugar.rb:22:7:22:7 | call to [] | desugar.rb:22:7:22:7 | _ .. _ |
| desugar.rb:22:10:22:14 | call to [] | desugar.rb:22:10:22:14 | -1 |
| desugar.rb:22:10:22:14 | call to bar= | desugar.rb:22:10:22:14 | ... = ... |
| desugar.rb:22:18:22:29 | call to [] | desugar.rb:22:19:22:19 | 1 |
| desugar.rb:22:18:22:29 | call to [] | desugar.rb:22:22:22:22 | 2 |
| desugar.rb:22:18:22:29 | call to [] | desugar.rb:22:25:22:25 | 3 |
| desugar.rb:22:18:22:29 | call to [] | desugar.rb:22:28:22:28 | 4 |
| desugar.rb:26:3:26:3 | call to [] | desugar.rb:26:3:26:3 | 0 |
| desugar.rb:26:6:26:11 | call to [] | desugar.rb:26:6:26:11 | 1 |
| desugar.rb:26:7:26:7 | call to [] | desugar.rb:26:7:26:7 | 0 |
| desugar.rb:26:10:26:10 | call to [] | desugar.rb:26:10:26:10 | 1 |
| desugar.rb:26:15:26:25 | call to [] | desugar.rb:26:16:26:16 | 1 |
| desugar.rb:26:15:26:25 | call to [] | desugar.rb:26:19:26:24 | call to [] |
| desugar.rb:26:19:26:24 | call to [] | desugar.rb:26:20:26:20 | 2 |
| desugar.rb:26:19:26:24 | call to [] | desugar.rb:26:23:26:23 | 3 |
| desugar.rb:31:6:31:7 | ... + ... | desugar.rb:31:9:31:9 | 2 |
| desugar.rb:34:7:34:8 | ... / ... | desugar.rb:34:10:34:10 | 4 |
| desugar.rb:38:13:38:14 | ... * ... | desugar.rb:38:16:38:16 | 6 |
| exit.rb:2:6:2:10 | ... > ... | exit.rb:2:10:2:10 | 2 |
| exit.rb:3:5:3:10 | call to exit | exit.rb:3:10:3:10 | 1 |
| exit.rb:5:3:5:15 | call to puts | exit.rb:5:8:5:15 | "x <= 2" |
| exit.rb:9:6:9:10 | ... > ... | exit.rb:9:10:9:10 | 2 |
| exit.rb:10:5:10:18 | call to abort | exit.rb:10:11:10:18 | "abort!" |
| exit.rb:12:3:12:15 | call to puts | exit.rb:12:8:12:15 | "x <= 2" |
| heredoc.rb:2:3:2:16 | call to puts | heredoc.rb:2:8:2:10 | <<A |
| heredoc.rb:2:3:2:16 | call to puts | heredoc.rb:2:13:2:15 | <<A |
| ifs.rb:2:6:2:10 | ... > ... | ifs.rb:2:10:2:10 | 2 |
| ifs.rb:3:5:3:30 | call to puts | ifs.rb:3:10:3:30 | "x is greater than 2" |
| ifs.rb:4:9:4:14 | ... <= ... | ifs.rb:4:14:4:14 | 2 |
| ifs.rb:4:9:4:24 | [false] ... and ... | ifs.rb:4:20:4:24 | ... > ... |
| ifs.rb:4:9:4:24 | [true] ... and ... | ifs.rb:4:20:4:24 | ... > ... |
| ifs.rb:4:9:4:38 | [false] ... and ... | ifs.rb:4:30:4:38 | [false] ! ... |
| ifs.rb:4:9:4:38 | [true] ... and ... | ifs.rb:4:30:4:38 | [true] ! ... |
| ifs.rb:4:20:4:24 | ... > ... | ifs.rb:4:24:4:24 | 0 |
| ifs.rb:4:32:4:37 | ... == ... | ifs.rb:4:37:4:37 | 5 |
| ifs.rb:5:5:5:17 | call to puts | ifs.rb:5:10:5:17 | "x is 1" |
| ifs.rb:7:5:7:35 | call to puts | ifs.rb:7:10:7:35 | "I can't guess the number" |
| ifs.rb:19:6:19:10 | ... < ... | ifs.rb:19:10:19:10 | 0 |
| ifs.rb:21:8:21:13 | ... > ... | ifs.rb:21:12:21:13 | 10 |
| ifs.rb:22:11:22:15 | ... - ... | ifs.rb:22:15:22:15 | 1 |
| ifs.rb:25:3:25:8 | call to puts | ifs.rb:25:8:25:8 | x |
| ifs.rb:37:3:37:12 | call to puts | ifs.rb:37:8:37:12 | "bla" |
| ifs.rb:38:12:38:17 | ... == ... | ifs.rb:38:17:38:17 | 2 |
| ifs.rb:48:5:48:15 | call to puts | ifs.rb:48:10:48:15 | "true" |
| ifs.rb:51:3:51:13 | call to puts | ifs.rb:51:8:51:13 | "done" |
| loops.rb:2:9:2:14 | ... >= ... | loops.rb:2:14:2:14 | 0 |
| loops.rb:3:5:3:10 | call to puts | loops.rb:3:10:3:10 | x |
| loops.rb:4:7:4:8 | ... - ... | loops.rb:4:10:4:10 | 1 |
| loops.rb:9:9:9:14 | ... >= ... | loops.rb:9:14:9:14 | 0 |
| loops.rb:10:5:10:10 | call to puts | loops.rb:10:10:10:10 | x |
| loops.rb:11:7:11:8 | ... - ... | loops.rb:11:10:11:10 | 1 |
| loops.rb:12:8:12:14 | ... > ... | loops.rb:12:12:12:14 | 100 |
| loops.rb:14:11:14:16 | ... > ... | loops.rb:14:15:14:16 | 50 |
| loops.rb:16:11:16:16 | ... > ... | loops.rb:16:15:16:16 | 10 |
| loops.rb:19:5:19:15 | call to puts | loops.rb:19:10:19:15 | "Iter" |
| loops.rb:21:3:21:13 | call to puts | loops.rb:21:8:21:13 | "Done" |
| loops.rb:25:3:25:9 | call to [] | loops.rb:25:4:25:4 | 1 |
| loops.rb:25:3:25:9 | call to [] | loops.rb:25:6:25:6 | 2 |
| loops.rb:25:3:25:9 | call to [] | loops.rb:25:8:25:8 | 3 |
| loops.rb:26:5:26:10 | call to puts | loops.rb:26:10:26:10 | x |
| loops.rb:31:9:31:13 | ... < ... | loops.rb:31:13:31:13 | y |
| raise.rb:8:6:8:10 | ... > ... | raise.rb:8:10:8:10 | 2 |
| raise.rb:9:5:9:17 | call to raise | raise.rb:9:11:9:17 | "x > 2" |
| raise.rb:11:3:11:15 | call to puts | raise.rb:11:8:11:15 | "x <= 2" |
| raise.rb:17:7:17:22 | call to raise | raise.rb:17:13:17:22 | ExceptionA |
| raise.rb:20:5:20:18 | call to puts | raise.rb:20:10:20:18 | "Rescued" |
| raise.rb:22:3:22:15 | call to puts | raise.rb:22:8:22:15 | "End m2" |
| raise.rb:28:7:28:22 | call to raise | raise.rb:28:13:28:22 | ExceptionA |
| raise.rb:31:5:31:18 | call to puts | raise.rb:31:10:31:18 | "Rescued" |
| raise.rb:33:3:33:15 | call to puts | raise.rb:33:8:33:15 | "End m3" |
| raise.rb:39:7:39:22 | call to raise | raise.rb:39:13:39:22 | ExceptionA |
| raise.rb:42:5:42:22 | call to puts | raise.rb:42:10:42:22 | "Rescued {e}" |
| raise.rb:44:3:44:15 | call to puts | raise.rb:44:8:44:15 | "End m4" |
| raise.rb:50:7:50:22 | call to raise | raise.rb:50:13:50:22 | ExceptionA |
| raise.rb:54:3:54:15 | call to puts | raise.rb:54:8:54:15 | "End m5" |
| raise.rb:60:7:60:22 | call to raise | raise.rb:60:13:60:22 | ExceptionA |
| raise.rb:63:5:63:22 | call to puts | raise.rb:63:10:63:22 | "Rescued {e}" |
| raise.rb:65:3:65:15 | call to puts | raise.rb:65:8:65:15 | "End m6" |
| raise.rb:69:6:69:10 | ... > ... | raise.rb:69:10:69:10 | 2 |
| raise.rb:70:5:70:17 | call to raise | raise.rb:70:11:70:17 | "x > 2" |
| raise.rb:71:9:71:13 | ... < ... | raise.rb:71:13:71:13 | 0 |
| raise.rb:74:3:74:20 | call to puts | raise.rb:74:8:74:20 | "0 <= x <= 2" |
| raise.rb:76:3:76:15 | [ensure: raise] call to puts | raise.rb:76:8:76:15 | [ensure: raise] "ensure" |
| raise.rb:76:3:76:15 | [ensure: return] call to puts | raise.rb:76:8:76:15 | [ensure: return] "ensure" |
| raise.rb:76:3:76:15 | call to puts | raise.rb:76:8:76:15 | "ensure" |
| raise.rb:80:3:80:17 | call to puts | raise.rb:80:8:80:17 | "Begin m8" |
| raise.rb:82:8:82:12 | ... > ... | raise.rb:82:12:82:12 | 2 |
| raise.rb:83:7:83:19 | call to raise | raise.rb:83:13:83:19 | "x > 2" |
| raise.rb:84:11:84:15 | ... < ... | raise.rb:84:15:84:15 | 0 |
| raise.rb:87:5:87:22 | call to puts | raise.rb:87:10:87:22 | "0 <= x <= 2" |
| raise.rb:89:5:89:17 | [ensure: raise] call to puts | raise.rb:89:10:89:17 | [ensure: raise] "ensure" |
| raise.rb:89:5:89:17 | [ensure: return] call to puts | raise.rb:89:10:89:17 | [ensure: return] "ensure" |
| raise.rb:89:5:89:17 | call to puts | raise.rb:89:10:89:17 | "ensure" |
| raise.rb:91:3:91:15 | call to puts | raise.rb:91:8:91:15 | "End m8" |
| raise.rb:95:3:95:17 | call to puts | raise.rb:95:8:95:17 | "Begin m9" |
| raise.rb:97:8:97:12 | ... > ... | raise.rb:97:12:97:12 | 2 |
| raise.rb:98:7:98:19 | call to raise | raise.rb:98:13:98:19 | "x > 2" |
| raise.rb:99:11:99:15 | ... < ... | raise.rb:99:15:99:15 | 0 |
| raise.rb:102:5:102:22 | call to puts | raise.rb:102:10:102:22 | "0 <= x <= 2" |
| raise.rb:104:5:104:23 | [ensure: raise] call to puts | raise.rb:104:10:104:23 | [ensure: raise] "outer ensure" |
| raise.rb:104:5:104:23 | [ensure: return] call to puts | raise.rb:104:10:104:23 | [ensure: return] "outer ensure" |
| raise.rb:104:5:104:23 | call to puts | raise.rb:104:10:104:23 | "outer ensure" |
| raise.rb:107:9:107:26 | [ensure: raise] call to raise | raise.rb:107:15:107:26 | [ensure: raise] "b1 is true" |
| raise.rb:107:9:107:26 | [ensure: return] call to raise | raise.rb:107:15:107:26 | [ensure: return] "b1 is true" |
| raise.rb:107:9:107:26 | call to raise | raise.rb:107:15:107:26 | "b1 is true" |
| raise.rb:110:7:110:25 | [ensure(1): raise] call to puts | raise.rb:110:12:110:25 | [ensure(1): raise] "inner ensure" |
| raise.rb:110:7:110:25 | [ensure: raise, ensure(1): raise] call to puts | raise.rb:110:12:110:25 | [ensure: raise, ensure(1): raise] "inner ensure" |
| raise.rb:110:7:110:25 | [ensure: raise] call to puts | raise.rb:110:12:110:25 | [ensure: raise] "inner ensure" |
| raise.rb:110:7:110:25 | [ensure: return, ensure(1): raise] call to puts | raise.rb:110:12:110:25 | [ensure: return, ensure(1): raise] "inner ensure" |
| raise.rb:110:7:110:25 | [ensure: return] call to puts | raise.rb:110:12:110:25 | [ensure: return] "inner ensure" |
| raise.rb:110:7:110:25 | call to puts | raise.rb:110:12:110:25 | "inner ensure" |
| raise.rb:113:3:113:15 | call to puts | raise.rb:113:8:113:15 | "End m9" |
| raise.rb:115:3:115:22 | [ensure: raise] call to puts | raise.rb:115:8:115:22 | [ensure: raise] "method ensure" |
| raise.rb:115:3:115:22 | [ensure: return] call to puts | raise.rb:115:8:115:22 | [ensure: return] "method ensure" |
| raise.rb:115:3:115:22 | call to puts | raise.rb:115:8:115:22 | "method ensure" |
| raise.rb:117:5:117:22 | [ensure: raise] call to raise | raise.rb:117:11:117:22 | [ensure: raise] "b2 is true" |
| raise.rb:117:5:117:22 | [ensure: return] call to raise | raise.rb:117:11:117:22 | [ensure: return] "b2 is true" |
| raise.rb:117:5:117:22 | call to raise | raise.rb:117:11:117:22 | "b2 is true" |
| raise.rb:121:14:121:30 | call to raise | raise.rb:121:20:121:30 | "Exception" |
| raise.rb:125:3:125:51 | call to puts | raise.rb:125:8:125:51 | "Will not get executed if p is..." |
| raise.rb:131:7:131:22 | call to raise | raise.rb:131:13:131:22 | ExceptionA |
| raise.rb:135:5:135:21 | call to puts | raise.rb:135:10:135:21 | "ExceptionB" |
| raise.rb:137:5:137:17 | [ensure: raise] call to puts | raise.rb:137:10:137:17 | [ensure: raise] "Ensure" |
| raise.rb:137:5:137:17 | call to puts | raise.rb:137:10:137:17 | "Ensure" |
| raise.rb:139:3:139:16 | call to puts | raise.rb:139:8:139:16 | "End m11" |
| raise.rb:144:5:144:12 | call to raise | raise.rb:144:11:144:12 | "" |
| raise.rb:155:25:155:32 | call to raise | raise.rb:155:31:155:32 | "" |
| raise.rb:160:5:162:7 | call to bar | raise.rb:160:9:162:7 | -> { ... } |
| raise.rb:161:7:161:14 | call to raise | raise.rb:161:13:161:14 | "" |
| raise.rb:168:5:168:12 | call to raise | raise.rb:168:11:168:12 | "" |
keywordArguments
| cfg.html.erb:6:9:6:58 | call to stylesheet_link_tag | media | cfg.html.erb:6:54:6:58 | "all" |
| cfg.html.erb:12:11:12:33 | call to link_to | id | cfg.html.erb:12:31:12:33 | "a" |

View File

@@ -0,0 +1,12 @@
import codeql.ruby.controlflow.CfgNodes
import codeql.ruby.controlflow.CfgNodes::ExprNodes
query predicate callsWithNoArguments(CallCfgNode c) {
not exists(c.getArgument(_)) and not exists(c.getKeywordArgument(_))
}
query predicate positionalArguments(CallCfgNode c, ExprCfgNode arg) { arg = c.getArgument(_) }
query predicate keywordArguments(CallCfgNode c, string keyword, ExprCfgNode arg) {
arg = c.getKeywordArgument(keyword)
}

View File

@@ -1,6 +1,6 @@
def m1 elements
for element in elements do
if element > 0 then
def m1 x
while x < 0
if x > 0 then
break
end
end
@@ -10,27 +10,27 @@ ensure
end
end
def m2 elements
for element in elements do
def m2(x, y)
while x < 0
begin
if element > 0 then
if x > 0 then
break
end
ensure
if elements.nil? then
puts "elements nil"
if y.nil? then
puts "y nil"
end
end
end
end
def m3 elements
def m3(x,y)
begin
if elements.nil? then
if x.nil? then
return
end
ensure
for element in elements do
while y < 0
begin
if x > 0 then
break
@@ -41,14 +41,14 @@ def m3 elements
puts "Done"
end
def m4 elements
for element in elements do
def m4 x
while x < 0
begin
if element > 1 then
if x > 1 then
raise ""
end
ensure
if element > 0 then
if x > 0 then
break 10;
end
end

View File

@@ -1,5 +1,4 @@
| local_dataflow.rb:1:1:7:3 | self (foo) | local_dataflow.rb:3:8:3:10 | self |
| local_dataflow.rb:1:1:7:3 | self (local_dataflow.rb) | local_dataflow.rb:11:1:11:2 | self |
| local_dataflow.rb:1:1:7:3 | self (local_dataflow.rb) | local_dataflow.rb:49:1:53:3 | self |
| local_dataflow.rb:1:1:7:3 | self in foo | local_dataflow.rb:3:8:3:10 | self |
| local_dataflow.rb:1:9:1:9 | a | local_dataflow.rb:1:9:1:9 | a |
@@ -26,25 +25,29 @@
| local_dataflow.rb:9:1:9:15 | ... = ... | local_dataflow.rb:10:14:10:18 | array |
| local_dataflow.rb:9:9:9:15 | call to [] | local_dataflow.rb:9:1:9:15 | ... = ... |
| local_dataflow.rb:9:9:9:15 | call to [] | local_dataflow.rb:9:1:9:15 | ... = ... |
| local_dataflow.rb:10:5:13:3 | for ... in ... | local_dataflow.rb:10:1:13:3 | ... = ... |
| local_dataflow.rb:10:9:10:9 | x | local_dataflow.rb:12:5:12:5 | x |
| local_dataflow.rb:10:14:10:18 | array | local_dataflow.rb:10:5:13:3 | for ... in ... |
| local_dataflow.rb:10:5:13:3 | ... = ... | local_dataflow.rb:12:5:12:5 | x |
| local_dataflow.rb:10:5:13:3 | <captured> | local_dataflow.rb:11:1:11:2 | self |
| local_dataflow.rb:10:5:13:3 | __synth__0__1 | local_dataflow.rb:10:5:13:3 | ... = ... |
| local_dataflow.rb:10:5:13:3 | __synth__0__1 | local_dataflow.rb:10:5:13:3 | ... = ... |
| local_dataflow.rb:10:5:13:3 | __synth__0__1 | local_dataflow.rb:10:5:13:3 | __synth__0__1 |
| local_dataflow.rb:10:5:13:3 | __synth__0__1 | local_dataflow.rb:10:5:13:3 | __synth__0__1 |
| local_dataflow.rb:10:5:13:3 | call to each | local_dataflow.rb:10:1:13:3 | ... = ... |
| local_dataflow.rb:10:14:10:18 | [post] array | local_dataflow.rb:15:10:15:14 | array |
| local_dataflow.rb:10:14:10:18 | array | local_dataflow.rb:15:10:15:14 | array |
| local_dataflow.rb:11:1:11:2 | [post] self | local_dataflow.rb:12:3:12:5 | self |
| local_dataflow.rb:11:1:11:2 | self | local_dataflow.rb:12:3:12:5 | self |
| local_dataflow.rb:12:3:12:5 | [post] self | local_dataflow.rb:11:1:11:2 | self |
| local_dataflow.rb:12:3:12:5 | [post] self | local_dataflow.rb:49:1:53:3 | self |
| local_dataflow.rb:12:3:12:5 | call to p | local_dataflow.rb:10:19:13:3 | do ... |
| local_dataflow.rb:12:3:12:5 | self | local_dataflow.rb:11:1:11:2 | self |
| local_dataflow.rb:12:3:12:5 | self | local_dataflow.rb:49:1:53:3 | self |
| local_dataflow.rb:15:10:15:14 | array | local_dataflow.rb:15:1:17:3 | for ... in ... |
| local_dataflow.rb:15:1:17:3 | __synth__0__1 | local_dataflow.rb:15:1:17:3 | ... = ... |
| local_dataflow.rb:15:1:17:3 | __synth__0__1 | local_dataflow.rb:15:1:17:3 | ... = ... |
| local_dataflow.rb:15:1:17:3 | __synth__0__1 | local_dataflow.rb:15:1:17:3 | __synth__0__1 |
| local_dataflow.rb:15:1:17:3 | __synth__0__1 | local_dataflow.rb:15:1:17:3 | __synth__0__1 |
| local_dataflow.rb:15:10:15:14 | [post] array | local_dataflow.rb:19:10:19:14 | array |
| local_dataflow.rb:15:10:15:14 | array | local_dataflow.rb:19:10:19:14 | array |
| local_dataflow.rb:16:3:16:10 | break | local_dataflow.rb:15:1:17:3 | for ... in ... |
| local_dataflow.rb:16:9:16:10 | 10 | local_dataflow.rb:16:3:16:10 | break |
| local_dataflow.rb:19:5:19:5 | x | local_dataflow.rb:20:6:20:6 | x |
| local_dataflow.rb:19:10:19:14 | array | local_dataflow.rb:19:1:21:3 | for ... in ... |
| local_dataflow.rb:20:3:20:25 | if ... | local_dataflow.rb:19:16:21:3 | do ... |
| local_dataflow.rb:20:17:20:21 | break | local_dataflow.rb:19:1:21:3 | for ... in ... |
| local_dataflow.rb:19:1:21:3 | ... = ... | local_dataflow.rb:20:6:20:6 | x |
| local_dataflow.rb:19:1:21:3 | __synth__0__1 | local_dataflow.rb:19:1:21:3 | ... = ... |
| local_dataflow.rb:19:1:21:3 | __synth__0__1 | local_dataflow.rb:19:1:21:3 | ... = ... |
| local_dataflow.rb:19:1:21:3 | __synth__0__1 | local_dataflow.rb:19:1:21:3 | __synth__0__1 |
| local_dataflow.rb:19:1:21:3 | __synth__0__1 | local_dataflow.rb:19:1:21:3 | __synth__0__1 |
| local_dataflow.rb:24:2:24:8 | break | local_dataflow.rb:23:1:25:3 | while ... |
| local_dataflow.rb:24:8:24:8 | 5 | local_dataflow.rb:24:2:24:8 | break |
| local_dataflow.rb:28:5:28:26 | M | local_dataflow.rb:28:1:28:26 | ... = ... |

View File

@@ -1,5 +1,9 @@
ret
| local_dataflow.rb:6:3:6:14 | ... = ... |
| local_dataflow.rb:12:3:12:5 | call to p |
| local_dataflow.rb:16:3:16:10 | break |
| local_dataflow.rb:20:3:20:25 | if ... |
| local_dataflow.rb:20:17:20:21 | break |
| local_dataflow.rb:32:14:32:21 | "method" |
| local_dataflow.rb:36:6:36:13 | return |
| local_dataflow.rb:38:3:38:13 | "reachable" |
@@ -17,9 +21,15 @@ arg
| local_dataflow.rb:9:10:9:10 | 1 | local_dataflow.rb:9:9:9:15 | call to [] | 0 |
| local_dataflow.rb:9:12:9:12 | 2 | local_dataflow.rb:9:9:9:15 | call to [] | 1 |
| local_dataflow.rb:9:14:9:14 | 3 | local_dataflow.rb:9:9:9:15 | call to [] | 2 |
| local_dataflow.rb:10:5:13:3 | { ... } | local_dataflow.rb:10:5:13:3 | call to each | -2 |
| local_dataflow.rb:10:14:10:18 | array | local_dataflow.rb:10:5:13:3 | call to each | -1 |
| local_dataflow.rb:11:1:11:2 | self | local_dataflow.rb:11:1:11:2 | call to do | -1 |
| local_dataflow.rb:12:3:12:5 | self | local_dataflow.rb:12:3:12:5 | call to p | -1 |
| local_dataflow.rb:12:5:12:5 | x | local_dataflow.rb:12:3:12:5 | call to p | 0 |
| local_dataflow.rb:15:1:17:3 | { ... } | local_dataflow.rb:15:1:17:3 | call to each | -2 |
| local_dataflow.rb:15:10:15:14 | array | local_dataflow.rb:15:1:17:3 | call to each | -1 |
| local_dataflow.rb:19:1:21:3 | { ... } | local_dataflow.rb:19:1:21:3 | call to each | -2 |
| local_dataflow.rb:19:10:19:14 | array | local_dataflow.rb:19:1:21:3 | call to each | -1 |
| local_dataflow.rb:20:6:20:6 | x | local_dataflow.rb:20:6:20:10 | ... > ... | -1 |
| local_dataflow.rb:20:10:20:10 | 1 | local_dataflow.rb:20:6:20:10 | ... > ... | 0 |
| local_dataflow.rb:35:6:35:6 | x | local_dataflow.rb:35:6:35:11 | ... == ... | -1 |

View File

@@ -1,12 +0,0 @@
| Excon.rb:3:9:3:40 | call to get | Excon.rb:4:1:4:10 | call to body |
| Excon.rb:6:9:6:60 | call to post | Excon.rb:7:1:7:10 | call to body |
| Excon.rb:9:9:9:59 | call to put | Excon.rb:10:1:10:10 | call to body |
| Excon.rb:12:9:12:61 | call to patch | Excon.rb:13:1:13:10 | call to body |
| Excon.rb:15:9:15:43 | call to delete | Excon.rb:16:1:16:10 | call to body |
| Excon.rb:18:9:18:41 | call to head | Excon.rb:19:1:19:10 | call to body |
| Excon.rb:21:9:21:44 | call to options | Excon.rb:22:1:22:10 | call to body |
| Excon.rb:24:9:24:42 | call to trace | Excon.rb:25:1:25:10 | call to body |
| Excon.rb:28:9:28:34 | call to get | Excon.rb:29:1:29:10 | call to body |
| Excon.rb:31:10:31:39 | call to post | Excon.rb:32:1:32:11 | call to body |
| Excon.rb:35:9:35:34 | call to get | Excon.rb:36:1:36:10 | call to body |
| Excon.rb:38:10:38:39 | call to post | Excon.rb:39:1:39:11 | call to body |

View File

@@ -1,4 +0,0 @@
import codeql.ruby.frameworks.http_clients.Excon
import codeql.ruby.DataFlow
query DataFlow::Node exconHttpRequests(ExconHttpRequest e) { result = e.getResponseBody() }

View File

@@ -1,9 +0,0 @@
| Faraday.rb:3:9:3:42 | call to get | Faraday.rb:4:1:4:10 | call to body |
| Faraday.rb:6:9:6:62 | call to post | Faraday.rb:7:1:7:10 | call to body |
| Faraday.rb:9:9:9:61 | call to put | Faraday.rb:10:1:10:10 | call to body |
| Faraday.rb:12:9:12:63 | call to patch | Faraday.rb:13:1:13:10 | call to body |
| Faraday.rb:15:9:15:45 | call to delete | Faraday.rb:16:1:16:10 | call to body |
| Faraday.rb:18:9:18:43 | call to head | Faraday.rb:19:1:19:10 | call to body |
| Faraday.rb:24:9:24:44 | call to trace | Faraday.rb:25:1:25:10 | call to body |
| Faraday.rb:28:9:28:27 | call to get | Faraday.rb:29:1:29:10 | call to body |
| Faraday.rb:31:10:31:46 | call to post | Faraday.rb:32:1:32:11 | call to body |

Some files were not shown because too many files have changed in this diff Show More