mirror of
https://github.com/github/codeql.git
synced 2026-04-24 00:05:14 +02:00
Merge remote-tracking branch 'origin/main' into nickrolfe/regexp_g_anchor
This commit is contained in:
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 {
|
||||
/**
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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() }
|
||||
|
||||
@@ -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()) }
|
||||
|
||||
|
||||
@@ -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`.
|
||||
|
||||
@@ -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" }
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 { }
|
||||
|
||||
@@ -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 { }
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
36
ruby/ql/lib/codeql/ruby/ast/internal/Control.qll
Normal file
36
ruby/ql/lib/codeql/ruby/ast/internal/Control.qll
Normal 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()
|
||||
}
|
||||
}
|
||||
75
ruby/ql/lib/codeql/ruby/ast/internal/Expr.qll
Normal file
75
ruby/ql/lib/codeql/ruby/ast/internal/Expr.qll
Normal 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)
|
||||
}
|
||||
28
ruby/ql/lib/codeql/ruby/ast/internal/Method.qll
Normal file
28
ruby/ql/lib/codeql/ruby/ast/internal/Method.qll
Normal 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)
|
||||
}
|
||||
}
|
||||
@@ -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() }
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
@@ -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
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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) }
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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() }
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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, _))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
|
||||
16
ruby/ql/lib/codeql/ruby/dataflow/Sanitizers.qll
Normal file
16
ruby/ql/lib/codeql/ruby/dataflow/Sanitizers.qll
Normal 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)
|
||||
}
|
||||
}
|
||||
@@ -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()
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -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()
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -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."
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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`.
|
||||
*/
|
||||
|
||||
@@ -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)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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() }
|
||||
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 }
|
||||
}
|
||||
|
||||
@@ -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" }
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
131
ruby/ql/lib/codeql/ruby/frameworks/Rails.qll
Normal file
131
ruby/ql/lib/codeql/ruby/frameworks/Rails.qll
Normal 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
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 |
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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`
|
||||
|
||||
@@ -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 }
|
||||
|
||||
|
||||
@@ -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())
|
||||
|
||||
@@ -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") }
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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 { }
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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)))
|
||||
|
||||
@@ -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"])
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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 }
|
||||
}
|
||||
@@ -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.
|
||||
|
||||
@@ -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
@@ -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
|
||||
@@ -0,0 +1,3 @@
|
||||
description: Re-number @ruby_token.kind
|
||||
compatibility: full
|
||||
ruby_tokeninfo.rel: run ruby_tokeninfo.qlo
|
||||
@@ -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) {
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -9,7 +9,6 @@
|
||||
* @id rb/sql-injection
|
||||
* @tags security
|
||||
* external/cwe/cwe-089
|
||||
* external/owasp/owasp-a1
|
||||
*/
|
||||
|
||||
import ruby
|
||||
|
||||
@@ -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
|
||||
|
||||
49
ruby/ql/src/queries/security/cwe-1333/RegExpInjection.qhelp
Normal file
49
ruby/ql/src/queries/security/cwe-1333/RegExpInjection.qhelp
Normal 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>
|
||||
26
ruby/ql/src/queries/security/cwe-1333/RegExpInjection.ql
Normal file
26
ruby/ql/src/queries/security/cwe-1333/RegExpInjection.ql
Normal 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"
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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>
|
||||
@@ -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."
|
||||
@@ -0,0 +1,3 @@
|
||||
class UsersController < ApplicationController
|
||||
skip_before_action :verify_authenticity_token
|
||||
end
|
||||
@@ -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>
|
||||
@@ -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"
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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] ...
|
||||
|
||||
@@ -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 ... |
|
||||
@@ -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 |
|
||||
|
||||
@@ -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 |
|
||||
|
||||
@@ -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 ... |
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 ... |
|
||||
@@ -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 ... |
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
@@ -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 |
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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 |
|
||||
|
||||
@@ -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
331
ruby/ql/test/library-tests/controlflow/graph/Nodes.expected
Normal file
331
ruby/ql/test/library-tests/controlflow/graph/Nodes.expected
Normal 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" |
|
||||
12
ruby/ql/test/library-tests/controlflow/graph/Nodes.ql
Normal file
12
ruby/ql/test/library-tests/controlflow/graph/Nodes.ql
Normal 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)
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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 | ... = ... |
|
||||
|
||||
@@ -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 |
|
||||
|
||||
@@ -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 |
|
||||
@@ -1,4 +0,0 @@
|
||||
import codeql.ruby.frameworks.http_clients.Excon
|
||||
import codeql.ruby.DataFlow
|
||||
|
||||
query DataFlow::Node exconHttpRequests(ExconHttpRequest e) { result = e.getResponseBody() }
|
||||
@@ -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
Reference in New Issue
Block a user