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

This commit is contained in:
Nick Rolfe
2021-11-23 15:42:05 +00:00
91 changed files with 1920 additions and 947 deletions

View File

@@ -0,0 +1,2 @@
lgtm,codescanning
* A new query (`rb/csrf-protection-disabled`) has been added. The query finds cases where cross-site forgery protection is explictly disabled.

View File

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

View File

@@ -596,6 +596,37 @@ module OrmInstantiation {
}
}
/**
* A data-flow node that may set or unset Cross-site request forgery protection.
*
* Extend this class to refine existing API models. If you want to model new APIs,
* extend `CSRFProtectionSetting::Range` instead.
*/
class CSRFProtectionSetting extends DataFlow::Node instanceof CSRFProtectionSetting::Range {
/**
* Gets the boolean value corresponding to if CSRF protection is enabled
* (`true`) or disabled (`false`) by this node.
*/
boolean getVerificationSetting() { result = super.getVerificationSetting() }
}
/** Provides a class for modeling new CSRF protection setting APIs. */
module CSRFProtectionSetting {
/**
* A data-flow node that may set or unset Cross-site request forgery protection.
*
* Extend this class to model new APIs. If you want to refine existing API models,
* extend `CSRFProtectionSetting` instead.
*/
abstract class Range extends DataFlow::Node {
/**
* Gets the boolean value corresponding to if CSRF protection is enabled
* (`true`) or disabled (`false`) by this node.
*/
abstract boolean getVerificationSetting();
}
}
/** Provides classes for modeling path-related APIs. */
module Path {
/**

View File

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

View File

@@ -583,7 +583,7 @@ class ForExpr extends Loop, TForExpr {
final override string getAPrimaryQlClass() { result = "ForExpr" }
/** Gets the body of this `for` loop. */
final override Stmt getBody() { toGenerated(result) = g.getBody() }
final override StmtSequence getBody() { toGenerated(result) = g.getBody() }
/** Gets the pattern representing the iteration argument. */
final Pattern getPattern() { toGenerated(result) = g.getPattern() }

View File

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

View File

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

View File

@@ -57,7 +57,11 @@ class NamedParameter extends Parameter, TNamedParameter {
final VariableAccess getAnAccess() { result = this.getVariable().getAnAccess() }
/** Gets the access that defines the underlying local variable. */
final VariableAccess getDefiningAccess() { result = this.getVariable().getDefiningAccess() }
final VariableAccess getDefiningAccess() {
result = this.getVariable().getDefiningAccess()
or
result = this.(SimpleParameterSynthImpl).getDefininingAccess()
}
override AstNode getAChild(string pred) {
result = super.getAChild(pred)
@@ -68,14 +72,12 @@ class NamedParameter extends Parameter, TNamedParameter {
}
/** A simple (normal) parameter. */
class SimpleParameter extends NamedParameter, PatternParameter, VariablePattern, TSimpleParameter {
private Ruby::Identifier g;
class SimpleParameter extends NamedParameter, PatternParameter, VariablePattern, TSimpleParameter instanceof SimpleParameterImpl {
final override string getName() { result = SimpleParameterImpl.super.getNameImpl() }
SimpleParameter() { this = TSimpleParameter(g) }
final override string getName() { result = g.getValue() }
final override LocalVariable getVariable() { result = TLocalVariableReal(_, _, g) }
final override LocalVariable getVariable() {
result = SimpleParameterImpl.super.getVariableImpl()
}
final override LocalVariable getAVariable() { result = this.getVariable() }

View File

@@ -4,6 +4,7 @@ private import internal.AST
private import internal.Pattern
private import internal.TreeSitter
private import internal.Variable
private import internal.Parameter
/** A pattern. */
class Pattern extends AstNode {
@@ -15,6 +16,8 @@ class Pattern extends AstNode {
implicitParameterAssignmentNode(toGenerated(this), _)
or
this = getSynthChild(any(AssignExpr ae), 0)
or
this instanceof SimpleParameterImpl
}
/** Gets a variable used in (or introduced by) this pattern. */

View File

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

View File

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

View File

@@ -93,7 +93,8 @@ 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
@@ -131,7 +132,6 @@ private module Cached {
TFalseLiteral(Ruby::False 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
@@ -238,7 +238,10 @@ 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
@@ -291,12 +294,12 @@ private module Cached {
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
TBlockParameter or TBraceBlockReal or TBreakStmt or TCaseEqExpr or TCaseExpr 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
TForExpr 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
@@ -308,7 +311,7 @@ private module Cached {
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
@@ -319,12 +322,13 @@ private module Cached {
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
@@ -359,7 +363,7 @@ 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
@@ -385,7 +389,6 @@ private module Cached {
n = TFalseLiteral(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
@@ -439,7 +442,7 @@ private module Cached {
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
@@ -488,6 +491,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 +527,8 @@ private module Cached {
or
result = TSelfSynth(parent, i, _)
or
result = TSimpleParameterSynth(parent, i)
or
result = TSplatExprSynth(parent, i)
or
result = TStmtSequenceSynth(parent, i)
@@ -639,6 +646,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;
@@ -730,6 +739,8 @@ class TParameter =
TPatternParameter or TBlockParameter or THashSplatParameter or TKeywordParameter or
TOptionalParameter or TSplatParameter or TForwardParameter;
class TSimpleParameter = TSimpleParameterReal or TSimpleParameterSynth;
class TPatternParameter = TSimpleParameter or TTuplePatternParameter;
class TNamedParameter =

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -327,7 +327,7 @@ private module Cached {
cached
predicate isCapturedAccess(LocalVariableAccess access) {
toGenerated(access.getVariable().getDeclaringScope()) != scopeOf(toGenerated(access))
access.getVariable().getDeclaringScope() != access.getCfgScope()
}
cached
@@ -524,7 +524,7 @@ private class LocalVariableAccessReal extends LocalVariableAccessImpl, TLocalVar
final override string toString() { result = g.getValue() }
}
private class LocalVariableAccessSynth extends LocalVariableAccessImpl, TLocalVariableAccessSynth {
class LocalVariableAccessSynth extends LocalVariableAccessImpl, TLocalVariableAccessSynth {
private LocalVariable v;
LocalVariableAccessSynth() { this = TLocalVariableAccessSynth(_, _, v) }

View File

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

View File

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

View File

@@ -61,6 +61,9 @@ module CfgScope {
final override predicate exit(AstNode last, Completion c) {
last(this.(Trees::EndBlockTree).getLastBodyChild(), last, c)
or
last(this.(Trees::EndBlockTree).getBodyChild(_, _), last, c) and
not c instanceof NormalCompletion
}
}
@@ -79,6 +82,9 @@ module CfgScope {
final override predicate exit(AstNode last, Completion c) {
last(this.(Trees::BraceBlockTree).getLastBodyChild(), last, c)
or
last(this.(Trees::BraceBlockTree).getBodyChild(_, _), last, c) and
not c instanceof NormalCompletion
}
}
}
@@ -91,25 +97,6 @@ predicate succEntry(CfgScope::Range_ scope, AstNode first) { scope.entry(first)
pragma[nomagic]
predicate succExit(CfgScope::Range_ scope, AstNode last, Completion c) { scope.exit(last, c) }
// TODO: remove this class; it should be replaced with an implicit non AST node
private class ForIn extends AstNode, ASTInternal::TForIn {
final override string toString() { result = "In" }
}
// TODO: remove this class; it should be replaced with an implicit non AST node
private class ForRange extends ForExpr {
override AstNode getAChild(string pred) {
result = super.getAChild(pred)
or
pred = "<in>" and
result = this.getIn()
}
ForIn getIn() {
result = ASTInternal::TForIn(ASTInternal::toGenerated(this).(Ruby::For).getValue())
}
}
/** Defines the CFG by dispatch on the various AST types. */
module Trees {
private class AliasStmtTree extends StandardPreOrderTree, AliasStmt {
@@ -610,89 +597,6 @@ 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 {
@@ -1137,6 +1041,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 +1055,6 @@ CfgScope getCfgScope(AstNode n) {
cached
private module Cached {
/** Gets the CFG scope of node `n`. */
cached
CfgScope getCfgScopeImpl(AstNode n) {
forceCachingInSameStage() and
result = parent*(ASTInternal::fromGenerated(scopeOf(ASTInternal::toGeneratedInclSynth(n))))
}
cached
newtype TSuccessorType =
TSuccessorSuccessor() or

View File

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

View File

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

View File

@@ -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

View File

@@ -645,17 +645,18 @@ module Consistency {
query predicate nonUniqueDef(RelevantDefinition def, SourceVariable v, BasicBlock bb, int i) {
ssaDefReachesRead(v, def, bb, i) and
not exists(unique(Definition def0 | ssaDefReachesRead(_, def0, bb, i)))
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(_, _, bb, i)
not ssaDefReachesRead(v, _, bb, i)
}
query predicate deadDef(RelevantDefinition def, SourceVariable v) {
v = def.getSourceVariable() and
not ssaDefReachesRead(_, def, _, _) and
not phiHasInputFromBlock(_, def, _)
not phiHasInputFromBlock(_, def, _) and
not uncertainWriteDefinitionInput(_, def)
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -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] ... = ...
@@ -1422,7 +1440,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 +1452,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 +1466,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 +1484,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
@@ -2140,7 +2154,6 @@ erb/template.html.erb:
# 25| getAnOperand/getRightOperand: [StringLiteral] ""
# 27| getStmt: [ForExpr] for ... in ...
# 27| getPattern: [LocalVariableAccess] x
# 27| <in>: [???] In
# 27| getValue: [ArrayLiteral] [...]
# 27| getElement: [StringLiteral] "foo"
# 27| getComponent: [StringTextComponent] foo

View File

@@ -23,6 +23,30 @@ calls/calls.rb:
# 67| getAnOperand/getLeftOperand/getReceiver: [LocalVariableAccess] var1
# 67| getAnOperand/getArgument/getRightOperand: [MethodCall] call to bar
# 67| getReceiver: [ConstantReadAccess] X
# 226| [ForExpr] for ... in ...
# 226| getDesugared: [MethodCall] call to each
# 226| getBlock: [BraceBlock] { ... }
# 226| getParameter: [SimpleParameter] __synth__0__1
# 226| getDefiningAccess: [LocalVariableAccess] __synth__0__1
# 226| getStmt: [AssignExpr] ... = ...
# 226| getAnOperand/getRightOperand: [LocalVariableAccess] __synth__0__1
# 226| getAnOperand/getLeftOperand: [LocalVariableAccess] x
# 227| getStmt: [MethodCall] call to baz
# 227| getReceiver: [Self, SelfVariableAccess] self
# 226| getReceiver: [MethodCall] call to bar
# 226| getReceiver: [Self, SelfVariableAccess] self
# 229| [ForExpr] for ... in ...
# 229| getDesugared: [MethodCall] call to each
# 229| getBlock: [BraceBlock] { ... }
# 229| getParameter: [SimpleParameter] __synth__0__1
# 229| getDefiningAccess: [LocalVariableAccess] __synth__0__1
# 229| getStmt: [AssignExpr] ... = ...
# 229| getAnOperand/getRightOperand: [LocalVariableAccess] __synth__0__1
# 229| getAnOperand/getLeftOperand: [LocalVariableAccess] x
# 230| getStmt: [MethodCall] call to baz
# 230| getReceiver: [ConstantReadAccess] X
# 229| getReceiver: [MethodCall] call to bar
# 229| getReceiver: [ConstantReadAccess] X
# 314| [AssignExpr] ... = ...
# 314| getDesugared: [StmtSequence] ...
# 314| getStmt: [SetterMethodCall] call to foo=
@@ -195,6 +219,53 @@ 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
constants/constants.rb:
# 20| [ArrayLiteral] [...]
# 20| getDesugared: [MethodCall] call to []
@@ -327,48 +398,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 +690,30 @@ params/params.rb:
# 21| getDesugared: [MethodCall] call to []
# 21| getReceiver: [ConstantReadAccess] Array
erb/template.html.erb:
# 27| [ArrayLiteral] [...]
# 27| getDesugared: [MethodCall] call to []
# 27| getReceiver: [ConstantReadAccess] Array
# 27| getArgument: [StringLiteral] "foo"
# 27| getComponent: [StringTextComponent] foo
# 27| getArgument: [StringLiteral] "bar"
# 27| getComponent: [StringTextComponent] bar
# 27| getArgument: [StringLiteral] "baz"
# 27| getComponent: [StringTextComponent] baz
# 28| [AssignAddExpr] ... += ...
# 28| getDesugared: [AssignExpr] ... = ...
# 28| getAnOperand/getLeftOperand: [LocalVariableAccess] xs
# 28| getAnOperand/getRightOperand: [AddExpr] ... + ...
# 28| getAnOperand/getLeftOperand/getReceiver: [LocalVariableAccess] xs
# 28| getAnOperand/getArgument/getRightOperand: [LocalVariableAccess] x
# 27| [ForExpr] for ... in ...
# 27| getDesugared: [MethodCall] call to each
# 27| getBlock: [BraceBlock] { ... }
# 27| getParameter: [SimpleParameter] __synth__0__1
# 27| getDefiningAccess: [LocalVariableAccess] __synth__0__1
# 27| getStmt: [AssignExpr] ... = ...
# 27| getAnOperand/getRightOperand: [LocalVariableAccess] __synth__0__1
# 27| getAnOperand/getLeftOperand: [LocalVariableAccess] x
# 28| getStmt: [AssignAddExpr] ... += ...
# 28| getDesugared: [AssignExpr] ... = ...
# 28| getAnOperand/getLeftOperand: [LocalVariableAccess] xs
# 28| getAnOperand/getRightOperand: [AddExpr] ... + ...
# 28| getAnOperand/getLeftOperand/getReceiver: [LocalVariableAccess] xs
# 28| getAnOperand/getArgument/getRightOperand: [LocalVariableAccess] x
# 29| getStmt: [LocalVariableAccess] xs
# 27| getReceiver: [ArrayLiteral] [...]
# 27| getDesugared: [MethodCall] call to []
# 27| getReceiver: [ConstantReadAccess] Array
# 27| getArgument: [StringLiteral] "foo"
# 27| getComponent: [StringTextComponent] foo
# 27| getArgument: [StringLiteral] "bar"
# 27| getComponent: [StringTextComponent] bar
# 27| getArgument: [StringLiteral] "baz"
# 27| getComponent: [StringTextComponent] baz
gems/test.gemspec:
# 2| [AssignExpr] ... = ...
# 2| getDesugared: [StmtSequence] ...

View File

@@ -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 |
@@ -150,10 +159,14 @@
| control/loops.rb:9:13:9:14 | 10 | 10 |
| control/loops.rb:16:10:16:10 | 1 | 1 |
| control/loops.rb:16:13:16:14 | 10 | 10 |
| control/loops.rb:22:5:22:7 | 0 | 0 |
| control/loops.rb:22:10:22:14 | 1 | 1 |
| control/loops.rb:22:20:22:22 | :foo | foo |
| control/loops.rb:22:25:22:25 | 0 | 0 |
| control/loops.rb:22:28:22:30 | :bar | bar |
| control/loops.rb:22:33:22:33 | 1 | 1 |
| control/loops.rb:28:6:28:8 | 0 | 0 |
| control/loops.rb:28:11:28:15 | 1 | 1 |
| control/loops.rb:28:22:28:24 | :foo | foo |
| control/loops.rb:28:27:28:27 | 0 | 0 |
| control/loops.rb:28:30:28:32 | :bar | bar |

View File

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

View File

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

View File

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

File diff suppressed because it is too large Load Diff

View File

@@ -1,12 +1,12 @@
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:22 | [ensure: break] call to nil? |
| break_ensure.rb:20:10:20:22 | [ensure: raise] call to nil? |
| break_ensure.rb:20:10:20:22 | call to nil? |
| break_ensure.rb:29:8:29:20 | call to nil? |
| break_ensure.rb:35:12:35:12 | [ensure: raise] call to x |
| break_ensure.rb:35:12:35:12 | [ensure: return] call to x |
| break_ensure.rb:35:12:35:12 | call to x |
| 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 |
@@ -23,6 +23,7 @@ callsWithNoArguments
| 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 | * ... |
@@ -56,20 +57,27 @@ callsWithNoArguments
| raise.rb:155:37:155:48 | call to nil? |
| raise.rb:159:3:163:5 | call to foo |
positionalArguments
| break_ensure.rb:3:8:3:18 | ... > ... | break_ensure.rb:3:18:3:18 | 0 |
| 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:16:10:16:20 | ... > ... | break_ensure.rb:16:20:16:20 | 0 |
| break_ensure.rb:21:9:21:27 | [ensure: break] call to puts | break_ensure.rb:21:14:21:27 | [ensure: break] "elements nil" |
| break_ensure.rb:21:9:21:27 | [ensure: raise] call to puts | break_ensure.rb:21:14:21:27 | [ensure: raise] "elements nil" |
| break_ensure.rb:21:9:21:27 | call to puts | break_ensure.rb:21:14:21:27 | "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:47:10:47:20 | ... > ... | break_ensure.rb:47:20:47:20 | 1 |
| 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:20 | ... > ... | break_ensure.rb:51:20:51:20 | 0 |
| break_ensure.rb:51:10:51:20 | [ensure: raise] ... > ... | break_ensure.rb:51:20:51:20 | [ensure: raise] 0 |
| 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" |
@@ -91,7 +99,6 @@ positionalArguments
| 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:20:3:20:14 | call to puts | cfg.rb:20:8:20:14 | "world" |
| 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 | &... |

View File

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

View File

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

View File

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

View File

@@ -28,6 +28,7 @@ parameterVariable
| ssa.rb:1:7:1:7 | b | ssa.rb:1:7:1:7 | b |
| ssa.rb:18:8:18:8 | x | ssa.rb:18:8:18:8 | x |
| ssa.rb:25:8:25:15 | elements | ssa.rb:25:8:25:15 | elements |
| ssa.rb:26:3:28:5 | __synth__0__1 | ssa.rb:26:3:28:5 | __synth__0__1 |
| ssa.rb:33:20:33:20 | x | ssa.rb:33:20:33:20 | x |
| ssa.rb:44:8:44:8 | b | ssa.rb:44:8:44:8 | b |
| ssa.rb:49:9:49:9 | x | ssa.rb:49:9:49:9 | x |

View File

@@ -82,8 +82,12 @@ definition
| scopes.rb:13:14:13:14 | ... = ... | scopes.rb:13:14:13:14 | d |
| scopes.rb:13:19:13:32 | ... = ... | scopes.rb:13:4:13:32 | __synth__0 |
| scopes.rb:27:1:27:5 | ... = ... | scopes.rb:27:1:27:1 | x |
| scopes.rb:29:3:29:7 | ... = ... | scopes.rb:29:3:29:3 | x |
| scopes.rb:32:3:32:7 | ... = ... | scopes.rb:32:3:32:3 | x |
| scopes.rb:35:3:35:7 | ... = ... | scopes.rb:35:3:35:3 | x |
| scopes.rb:41:1:49:3 | self (M) | scopes.rb:41:1:49:3 | self |
| scopes.rb:42:2:42:9 | ... = ... | scopes.rb:42:2:42:4 | var |
| scopes.rb:43:2:43:13 | ... = ... | scopes.rb:43:2:43:4 | foo |
| scopes.rb:46:5:46:13 | ... = ... | scopes.rb:46:5:46:8 | var2 |
| ssa.rb:1:1:16:3 | self (m) | ssa.rb:1:1:16:3 | self |
| ssa.rb:1:7:1:7 | b | ssa.rb:1:7:1:7 | b |
@@ -98,8 +102,10 @@ definition
| ssa.rb:25:1:30:3 | <uninitialized> | ssa.rb:26:7:26:10 | elem |
| ssa.rb:25:1:30:3 | self (m2) | ssa.rb:25:1:30:3 | self |
| ssa.rb:25:8:25:15 | elements | ssa.rb:25:8:25:15 | elements |
| ssa.rb:26:7:26:10 | elem | ssa.rb:26:7:26:10 | elem |
| ssa.rb:26:12:26:22 | phi | ssa.rb:26:7:26:10 | elem |
| ssa.rb:26:3:28:5 | ... = ... | ssa.rb:26:7:26:10 | elem |
| ssa.rb:26:3:28:5 | <captured> | ssa.rb:25:1:30:3 | self |
| ssa.rb:26:3:28:5 | __synth__0__1 | ssa.rb:26:3:28:5 | __synth__0__1 |
| ssa.rb:26:3:28:5 | call to each | ssa.rb:26:7:26:10 | elem |
| ssa.rb:33:16:35:5 | <captured> | ssa.rb:32:1:36:3 | self |
| ssa.rb:33:20:33:20 | x | ssa.rb:33:20:33:20 | x |
| ssa.rb:38:1:42:3 | self (m4) | ssa.rb:38:1:42:3 | self |
@@ -264,11 +270,12 @@ read
| ssa.rb:19:9:19:9 | phi | ssa.rb:18:8:18:8 | x | ssa.rb:19:9:19:9 | x |
| ssa.rb:19:9:19:9 | phi | ssa.rb:18:8:18:8 | x | ssa.rb:20:10:20:10 | x |
| ssa.rb:19:9:19:9 | phi | ssa.rb:18:8:18:8 | x | ssa.rb:21:5:21:5 | x |
| ssa.rb:25:1:30:3 | self (m2) | ssa.rb:25:1:30:3 | self | ssa.rb:27:5:27:13 | self |
| ssa.rb:25:1:30:3 | self (m2) | ssa.rb:25:1:30:3 | self | ssa.rb:29:3:29:11 | self |
| ssa.rb:25:8:25:15 | elements | ssa.rb:25:8:25:15 | elements | ssa.rb:26:15:26:22 | elements |
| ssa.rb:26:7:26:10 | elem | ssa.rb:26:7:26:10 | elem | ssa.rb:27:10:27:13 | elem |
| ssa.rb:26:12:26:22 | phi | ssa.rb:26:7:26:10 | elem | ssa.rb:29:8:29:11 | elem |
| ssa.rb:26:3:28:5 | ... = ... | ssa.rb:26:7:26:10 | elem | ssa.rb:27:10:27:13 | elem |
| ssa.rb:26:3:28:5 | <captured> | ssa.rb:25:1:30:3 | self | ssa.rb:27:5:27:13 | self |
| ssa.rb:26:3:28:5 | __synth__0__1 | ssa.rb:26:3:28:5 | __synth__0__1 | ssa.rb:26:3:28:5 | __synth__0__1 |
| ssa.rb:26:3:28:5 | call to each | ssa.rb:26:7:26:10 | elem | ssa.rb:29:8:29:11 | elem |
| ssa.rb:33:16:35:5 | <captured> | ssa.rb:32:1:36:3 | self | ssa.rb:34:5:34:10 | self |
| ssa.rb:33:20:33:20 | x | ssa.rb:33:20:33:20 | x | ssa.rb:34:10:34:10 | x |
| ssa.rb:38:1:42:3 | self (m4) | ssa.rb:38:1:42:3 | self | ssa.rb:39:3:39:9 | self |
@@ -392,11 +399,12 @@ firstRead
| ssa.rb:10:5:10:9 | ... = ... | ssa.rb:2:3:2:3 | i | ssa.rb:11:10:11:10 | i |
| ssa.rb:18:1:23:3 | self (m1) | ssa.rb:18:1:23:3 | self | ssa.rb:20:5:20:10 | self |
| ssa.rb:19:9:19:9 | phi | ssa.rb:18:8:18:8 | x | ssa.rb:19:9:19:9 | x |
| ssa.rb:25:1:30:3 | self (m2) | ssa.rb:25:1:30:3 | self | ssa.rb:27:5:27:13 | self |
| ssa.rb:25:1:30:3 | self (m2) | ssa.rb:25:1:30:3 | self | ssa.rb:29:3:29:11 | self |
| ssa.rb:25:8:25:15 | elements | ssa.rb:25:8:25:15 | elements | ssa.rb:26:15:26:22 | elements |
| ssa.rb:26:7:26:10 | elem | ssa.rb:26:7:26:10 | elem | ssa.rb:27:10:27:13 | elem |
| ssa.rb:26:12:26:22 | phi | ssa.rb:26:7:26:10 | elem | ssa.rb:29:8:29:11 | elem |
| ssa.rb:26:3:28:5 | ... = ... | ssa.rb:26:7:26:10 | elem | ssa.rb:27:10:27:13 | elem |
| ssa.rb:26:3:28:5 | <captured> | ssa.rb:25:1:30:3 | self | ssa.rb:27:5:27:13 | self |
| ssa.rb:26:3:28:5 | __synth__0__1 | ssa.rb:26:3:28:5 | __synth__0__1 | ssa.rb:26:3:28:5 | __synth__0__1 |
| ssa.rb:26:3:28:5 | call to each | ssa.rb:26:7:26:10 | elem | ssa.rb:29:8:29:11 | elem |
| ssa.rb:33:16:35:5 | <captured> | ssa.rb:32:1:36:3 | self | ssa.rb:34:5:34:10 | self |
| ssa.rb:33:20:33:20 | x | ssa.rb:33:20:33:20 | x | ssa.rb:34:10:34:10 | x |
| ssa.rb:38:1:42:3 | self (m4) | ssa.rb:38:1:42:3 | self | ssa.rb:39:3:39:9 | self |
@@ -519,8 +527,10 @@ lastRead
| ssa.rb:19:9:19:9 | phi | ssa.rb:18:8:18:8 | x | ssa.rb:21:5:21:5 | x |
| ssa.rb:25:1:30:3 | self (m2) | ssa.rb:25:1:30:3 | self | ssa.rb:29:3:29:11 | self |
| ssa.rb:25:8:25:15 | elements | ssa.rb:25:8:25:15 | elements | ssa.rb:26:15:26:22 | elements |
| ssa.rb:26:7:26:10 | elem | ssa.rb:26:7:26:10 | elem | ssa.rb:27:10:27:13 | elem |
| ssa.rb:26:12:26:22 | phi | ssa.rb:26:7:26:10 | elem | ssa.rb:29:8:29:11 | elem |
| ssa.rb:26:3:28:5 | ... = ... | ssa.rb:26:7:26:10 | elem | ssa.rb:27:10:27:13 | elem |
| ssa.rb:26:3:28:5 | <captured> | ssa.rb:25:1:30:3 | self | ssa.rb:27:5:27:13 | self |
| ssa.rb:26:3:28:5 | __synth__0__1 | ssa.rb:26:3:28:5 | __synth__0__1 | ssa.rb:26:3:28:5 | __synth__0__1 |
| ssa.rb:26:3:28:5 | call to each | ssa.rb:26:7:26:10 | elem | ssa.rb:29:8:29:11 | elem |
| ssa.rb:33:16:35:5 | <captured> | ssa.rb:32:1:36:3 | self | ssa.rb:34:5:34:10 | self |
| ssa.rb:33:20:33:20 | x | ssa.rb:33:20:33:20 | x | ssa.rb:34:10:34:10 | x |
| ssa.rb:38:1:42:3 | self (m4) | ssa.rb:38:1:42:3 | self | ssa.rb:41:3:41:13 | self |
@@ -591,8 +601,6 @@ adjacentReads
| ssa.rb:18:1:23:3 | self (m1) | ssa.rb:18:1:23:3 | self | ssa.rb:20:5:20:10 | self | ssa.rb:20:5:20:10 | self |
| ssa.rb:19:9:19:9 | phi | ssa.rb:18:8:18:8 | x | ssa.rb:19:9:19:9 | x | ssa.rb:20:10:20:10 | x |
| ssa.rb:19:9:19:9 | phi | ssa.rb:18:8:18:8 | x | ssa.rb:20:10:20:10 | x | ssa.rb:21:5:21:5 | x |
| ssa.rb:25:1:30:3 | self (m2) | ssa.rb:25:1:30:3 | self | ssa.rb:27:5:27:13 | self | ssa.rb:27:5:27:13 | self |
| ssa.rb:25:1:30:3 | self (m2) | ssa.rb:25:1:30:3 | self | ssa.rb:27:5:27:13 | self | ssa.rb:29:3:29:11 | self |
| ssa.rb:38:1:42:3 | self (m4) | ssa.rb:38:1:42:3 | self | ssa.rb:39:3:39:9 | self | ssa.rb:39:8:39:9 | self |
| ssa.rb:38:1:42:3 | self (m4) | ssa.rb:38:1:42:3 | self | ssa.rb:39:8:39:9 | self | ssa.rb:41:3:41:13 | self |
| ssa.rb:66:11:70:5 | <captured> | ssa.rb:64:1:72:3 | self | ssa.rb:67:5:67:10 | self | ssa.rb:68:5:68:17 | self |
@@ -608,8 +616,6 @@ phi
| ssa.rb:5:3:13:5 | phi | ssa.rb:2:3:2:3 | i | ssa.rb:10:5:10:9 | ... = ... |
| ssa.rb:19:9:19:9 | phi | ssa.rb:18:8:18:8 | x | ssa.rb:18:8:18:8 | x |
| ssa.rb:19:9:19:9 | phi | ssa.rb:18:8:18:8 | x | ssa.rb:21:5:21:10 | ... = ... |
| ssa.rb:26:12:26:22 | phi | ssa.rb:26:7:26:10 | elem | ssa.rb:25:1:30:3 | <uninitialized> |
| ssa.rb:26:12:26:22 | phi | ssa.rb:26:7:26:10 | elem | ssa.rb:26:7:26:10 | elem |
| ssa.rb:45:3:45:12 | phi | ssa.rb:45:3:45:3 | x | ssa.rb:44:1:47:3 | <uninitialized> |
| ssa.rb:45:3:45:12 | phi | ssa.rb:45:3:45:3 | x | ssa.rb:45:3:45:7 | ... = ... |
| ssa.rb:50:3:50:8 | phi | ssa.rb:49:14:49:14 | y | ssa.rb:49:1:51:3 | <uninitialized> |

View File

@@ -316,6 +316,7 @@ explicitWrite
| ssa.rb:10:5:10:5 | i | ssa.rb:10:5:10:9 | ... = ... |
| ssa.rb:21:5:21:5 | x | ssa.rb:21:5:21:10 | ... -= ... |
| ssa.rb:21:5:21:5 | x | ssa.rb:21:5:21:10 | ... = ... |
| ssa.rb:26:7:26:10 | elem | ssa.rb:26:3:28:5 | ... = ... |
| ssa.rb:40:3:40:4 | m3 | ssa.rb:40:3:40:9 | ... = ... |
| ssa.rb:45:3:45:3 | x | ssa.rb:45:3:45:7 | ... = ... |
| ssa.rb:49:14:49:14 | y | ssa.rb:49:14:49:19 | ... = ... |
@@ -358,6 +359,7 @@ implicitWrite
| ssa.rb:1:7:1:7 | b |
| ssa.rb:18:8:18:8 | x |
| ssa.rb:25:8:25:15 | elements |
| ssa.rb:26:3:28:5 | __synth__0__1 |
| ssa.rb:26:7:26:10 | elem |
| ssa.rb:33:20:33:20 | x |
| ssa.rb:44:8:44:8 | b |
@@ -502,6 +504,7 @@ readAccess
| ssa.rb:20:5:20:10 | self |
| ssa.rb:20:10:20:10 | x |
| ssa.rb:21:5:21:5 | x |
| ssa.rb:26:3:28:5 | __synth__0__1 |
| ssa.rb:26:15:26:22 | elements |
| ssa.rb:27:5:27:13 | self |
| ssa.rb:27:10:27:13 | elem |

View File

@@ -123,6 +123,7 @@
| ssa.rb:18:8:18:8 | x |
| ssa.rb:25:1:30:3 | self |
| ssa.rb:25:8:25:15 | elements |
| ssa.rb:26:3:28:5 | __synth__0__1 |
| ssa.rb:26:7:26:10 | elem |
| ssa.rb:32:1:36:3 | self |
| ssa.rb:33:20:33:20 | x |

View File

@@ -59,6 +59,7 @@
| ssa.rb:1:1:88:3 | ssa.rb |
| ssa.rb:18:1:23:3 | m1 |
| ssa.rb:25:1:30:3 | m2 |
| ssa.rb:26:3:28:5 | { ... } |
| ssa.rb:32:1:36:3 | m3 |
| ssa.rb:33:16:35:5 | do ... end |
| ssa.rb:38:1:42:3 | m4 |

View File

@@ -0,0 +1,4 @@
| railsapp/app/controllers/users_controller.rb:4:3:4:47 | call to skip_before_action | Potential CSRF vulnerability due to forgery protection being disabled. |
| railsapp/config/application.rb:15:5:15:53 | call to allow_forgery_protection= | Potential CSRF vulnerability due to forgery protection being disabled. |
| railsapp/config/environments/development.rb:5:3:5:51 | call to allow_forgery_protection= | Potential CSRF vulnerability due to forgery protection being disabled. |
| railsapp/config/environments/production.rb:5:3:5:51 | call to allow_forgery_protection= | Potential CSRF vulnerability due to forgery protection being disabled. |

View File

@@ -0,0 +1 @@
queries/security/cwe-352/CSRFProtectionDisabled.ql

View File

@@ -0,0 +1,2 @@
class ApplicationController < ActionController::Base
end

View File

@@ -0,0 +1,11 @@
class UsersController < ApplicationController
# BAD: Disabling forgery protection may open the application to CSRF attacks
skip_before_action :verify_authenticity_token
def change_email
user = User.find_by(name: params[:user_name])
user.email = params[:new_email]
user.save!
end
end

View File

@@ -0,0 +1,17 @@
require_relative 'boot'
require 'rails/all'
# Require the gems listed in Gemfile, including any gems
# you've limited to :test, :development, or :production.
Bundler.require(*Rails.groups)
module Railsapp
class Application < Rails::Application
# Initialize configuration defaults for originally generated Rails version.
config.load_defaults 6.0
# BAD: Disabling forgery protection may open the application to CSRF attacks
config.action_controller.allow_forgery_protection = false
end
end

View File

@@ -0,0 +1,5 @@
# Load the Rails application.
require_relative 'application'
# Initialize the Rails application.
Rails.application.initialize!

View File

@@ -0,0 +1,6 @@
Rails.application.configure do
# Settings specified here will take precedence over those in config/application.rb.
# GOOD: disabling CSRF protection in the development environment should not be flagged
config.action_controller.allow_forgery_protection = false
end

View File

@@ -0,0 +1,6 @@
Rails.application.configure do
# Settings specified here will take precedence over those in config/application.rb.
# BAD: Disabling forgery protection may open the application to CSRF attacks
config.action_controller.allow_forgery_protection = false
end

View File

@@ -0,0 +1,11 @@
# The test environment is used exclusively to run your application's
# test suite. You never need to work with it otherwise. Remember that
# your test database is "scratch space" for the test suite and is wiped
# and recreated between test runs. Don't rely on the data there!
Rails.application.configure do
# Settings specified here will take precedence over those in config/application.rb.
# GOOD: disabling CSRF protection in the test environment should not be flagged
config.action_controller.allow_forgery_protection = false
end

View File

@@ -0,0 +1,8 @@
require "test_helper"
class UsersControllerTest < ActiveSupport::TestCase
setup do
# GOOD: disabling CSRF protection in tests should not be flagged
config.action_controller.allow_forgery_protection = false
end
end