Merge remote-tracking branch 'origin/main' into ruby/clear-text-logging

This commit is contained in:
Alex Ford
2022-02-17 15:32:31 +00:00
1113 changed files with 249627 additions and 41134 deletions

View File

@@ -1,5 +1,4 @@
name: codeql/ruby-consistency-queries
version: 0.0.1
groups: [ruby, test, consistency-queries]
dependencies:
codeql/ruby-all: 0.0.1
codeql/ruby-all: "*"

View File

@@ -1,3 +1,7 @@
## 0.0.9
## 0.0.8
## 0.0.7
## 0.0.6

View File

@@ -0,0 +1 @@
## 0.0.8

View File

@@ -0,0 +1 @@
## 0.0.9

View File

@@ -1,2 +1,2 @@
---
lastReleaseVersion: 0.0.7
lastReleaseVersion: 0.0.9

View File

@@ -126,7 +126,7 @@ class AstNode extends TAstNode {
/** A Ruby source file */
class RubyFile extends File {
RubyFile() { ruby_ast_node_parent(_, this, _) }
RubyFile() { ruby_ast_node_info(_, this, _, _) }
/** Gets a token in this file. */
private Ruby::Token getAToken() { result.getLocation().getFile() = this }

View File

@@ -11,6 +11,8 @@ import codeql.ruby.DataFlow
import codeql.ruby.typetracking.TypeTracker
import codeql.ruby.ast.internal.Module
private import codeql.ruby.controlflow.CfgNodes
private import codeql.ruby.dataflow.internal.DataFlowPrivate as DataFlowPrivate
private import codeql.ruby.dataflow.internal.DataFlowDispatch as DataFlowDispatch
/**
* Provides classes and predicates for working with APIs used in a database.
@@ -43,6 +45,16 @@ module API {
*/
DataFlow::LocalSourceNode getAnImmediateUse() { Impl::use(this, result) }
/**
* Gets a data-flow node corresponding the value flowing into this API component.
*/
DataFlow::Node getARhs() { Impl::def(this, result) }
/**
* Gets a data-flow node that may interprocedurally flow to the value escaping into this API component.
*/
DataFlow::Node getAValueReachingRhs() { result = Impl::trackDefNode(this.getARhs()) }
/**
* Gets a call to a method on the receiver represented by this API component.
*/
@@ -88,12 +100,37 @@ module API {
* This predicate may have multiple results when there are multiple constructor calls invoking this API component.
* Consider using `getAnInstantiation()` if there is a need to distinguish between individual constructor calls.
*/
Node getInstance() { result = this.getASuccessor(Label::instance()) }
Node getInstance() { result = this.getASubclass().getReturn("new") }
/**
* Gets a node representing a call to `method` on the receiver represented by this node.
*/
Node getMethod(string method) {
result = this.getASubclass().getASuccessor(Label::method(method))
}
/**
* Gets a node representing the result of this call.
*/
Node getReturn() { result = this.getASuccessor(Label::return()) }
/**
* Gets a node representing the result of calling a method on the receiver represented by this node.
*/
Node getReturn(string method) { result = this.getASuccessor(Label::return(method)) }
Node getReturn(string method) { result = this.getMethod(method).getReturn() }
/** Gets an API node representing the `n`th positional parameter. */
pragma[nomagic]
Node getParameter(int n) { result = this.getASuccessor(Label::parameter(n)) }
/** Gets an API node representing the given keyword parameter. */
pragma[nomagic]
Node getKeywordParameter(string name) {
result = this.getASuccessor(Label::keywordParameter(name))
}
/** Gets an API node representing the block parameter. */
Node getBlock() { result = this.getASuccessor(Label::blockParameter()) }
/**
* Gets a `new` call to the function represented by this API component.
@@ -101,9 +138,26 @@ module API {
DataFlow::ExprNode getAnInstantiation() { result = this.getInstance().getAnImmediateUse() }
/**
* Gets a node representing a subclass of the class represented by this node.
* Gets a node representing a (direct or indirect) subclass of the class represented by this node.
* ```rb
* class A; end
* class B < A; end
* class C < B; end
* ```
* In the example above, `getMember("A").getASubclass()` will return uses of `A`, `B` and `C`.
*/
Node getASubclass() { result = this.getASuccessor(Label::subclass()) }
Node getASubclass() { result = this.getAnImmediateSubclass*() }
/**
* Gets a node representing a direct subclass of the class represented by this node.
* ```rb
* class A; end
* class B < A; end
* class C < B; end
* ```
* In the example above, `getMember("A").getAnImmediateSubclass()` will return uses of `B` only.
*/
Node getAnImmediateSubclass() { result = this.getASuccessor(Label::subclass()) }
/**
* Gets a string representation of the lexicographically least among all shortest access paths
@@ -117,13 +171,13 @@ module API {
* Gets a node such that there is an edge in the API graph between this node and the other
* one, and that edge is labeled with `lbl`.
*/
Node getASuccessor(string lbl) { Impl::edge(this, lbl, result) }
Node getASuccessor(Label::ApiLabel lbl) { Impl::edge(this, lbl, result) }
/**
* Gets a node such that there is an edge in the API graph between that other node and
* this one, and that edge is labeled with `lbl`
*/
Node getAPredecessor(string lbl) { this = result.getASuccessor(lbl) }
Node getAPredecessor(Label::ApiLabel lbl) { this = result.getASuccessor(lbl) }
/**
* Gets a node such that there is an edge in the API graph between this node and the other
@@ -140,7 +194,13 @@ module API {
/**
* Gets the data-flow node that gives rise to this node, if any.
*/
DataFlow::Node getInducingNode() { this = Impl::MkUse(result) }
DataFlow::Node getInducingNode() {
this = Impl::MkUse(result)
or
this = Impl::MkDef(result)
or
this = Impl::MkMethodAccessNode(result)
}
/** Gets the location of this node. */
Location getLocation() {
@@ -165,9 +225,8 @@ module API {
length = 0 and
result = ""
or
exists(Node pred, string lbl, string predpath |
exists(Node pred, Label::ApiLabel lbl, string predpath |
Impl::edge(pred, lbl, this) and
lbl != "" and
predpath = pred.getAPath(length - 1) and
exists(string dot | if length = 1 then dot = "" else dot = "." |
result = predpath + dot + lbl and
@@ -187,15 +246,26 @@ module API {
override string toString() { result = "root" }
}
private string tryGetPath(Node node) {
result = node.getPath()
or
not exists(node.getPath()) and
result = "with no path"
}
/** A node corresponding to the use of an API component. */
class Use extends Node, Impl::MkUse {
override string toString() {
exists(string type | type = "Use " |
result = type + this.getPath()
or
not exists(this.getPath()) and result = type + "with no path"
)
}
override string toString() { result = "Use " + tryGetPath(this) }
}
/** A node corresponding to a value escaping into an API component. */
class Def extends Node, Impl::MkDef {
override string toString() { result = "Def " + tryGetPath(this) }
}
/** A node corresponding to the method being invoked at a method call. */
class MethodAccessNode extends Node, Impl::MkMethodAccessNode {
override string toString() { result = "MethodAccessNode " + tryGetPath(this) }
}
/** Gets the root node. */
@@ -240,8 +310,12 @@ module API {
newtype TApiNode =
/** The root of the API graph. */
MkRoot() or
/** The method accessed at `call`, synthetically treated as a separate object. */
MkMethodAccessNode(DataFlow::CallNode call) { isUse(call) } or
/** A use of an API member at the node `nd`. */
MkUse(DataFlow::Node nd) { isUse(nd) }
MkUse(DataFlow::Node nd) { isUse(nd) } or
/** A value that escapes into an external library at the node `nd` */
MkDef(DataFlow::Node nd) { isDef(nd) }
private string resolveTopLevel(ConstantReadAccess read) {
TResolved(result) = resolveConstantReadAccess(read) and
@@ -253,11 +327,10 @@ module API {
* node labeled `lbl` in the API graph.
*/
pragma[nomagic]
private predicate useRoot(string lbl, DataFlow::Node ref) {
exists(string name, ExprNodes::ConstantAccessCfgNode access, ConstantReadAccess read |
access = ref.asExpr() and
lbl = Label::member(read.getName()) and
read = access.getExpr()
private predicate useRoot(Label::ApiLabel lbl, DataFlow::Node ref) {
exists(string name, ConstantReadAccess read |
read = ref.asExpr().getExpr() and
lbl = Label::member(read.getName())
|
name = resolveTopLevel(read)
or
@@ -271,46 +344,33 @@ module API {
* Holds if `ref` is a use of a node that should have an incoming edge labeled `lbl`,
* from a use node that flows to `node`.
*/
private predicate useStep(string lbl, ExprCfgNode node, DataFlow::Node ref) {
private predicate useStep(Label::ApiLabel lbl, DataFlow::Node node, DataFlow::Node ref) {
// // Referring to an attribute on a node that is a use of `base`:
// pred = `Rails` part of `Rails::Whatever`
// lbl = `Whatever`
// ref = `Rails::Whatever`
exists(ExprNodes::ConstantAccessCfgNode c, ConstantReadAccess read |
not exists(resolveTopLevel(read)) and
node = c.getScopeExpr() and
node.asExpr() = c.getScopeExpr() and
lbl = Label::member(read.getName()) and
ref.asExpr() = c and
read = c.getExpr()
)
or
// Calling a method on a node that is a use of `base`
exists(ExprNodes::MethodCallCfgNode call, string name |
node = call.getReceiver() and
name = call.getExpr().getMethodName() and
lbl = Label::return(name) and
name != "new" and
ref.asExpr() = call
)
or
// Calling the `new` method on a node that is a use of `base`, which creates a new instance
exists(ExprNodes::MethodCallCfgNode call |
node = call.getReceiver() and
lbl = Label::instance() and
call.getExpr().getMethodName() = "new" and
ref.asExpr() = call
)
// note: method calls are not handled here as there is no DataFlow::Node for the intermediate MkMethodAccessNode API node
}
pragma[nomagic]
private predicate isUse(DataFlow::Node nd) {
useRoot(_, nd)
or
exists(ExprCfgNode node, DataFlow::LocalSourceNode pred |
pred = useCandFwd() and
pred.flowsTo(any(DataFlow::ExprNode n | n.getExprNode() = node)) and
exists(DataFlow::Node node |
useCandFwd().flowsTo(node) and
useStep(_, node, nd)
)
or
useCandFwd().flowsTo(nd.(DataFlow::CallNode).getReceiver())
or
parameterStep(_, defCand(), nd)
}
/**
@@ -319,6 +379,13 @@ module API {
cached
predicate use(TApiNode nd, DataFlow::Node ref) { nd = MkUse(ref) }
/**
* Holds if `rhs` is a RHS of node `nd`.
*/
cached
predicate def(TApiNode nd, DataFlow::Node rhs) { nd = MkDef(rhs) }
/** Gets a node reachable from a use-node. */
private DataFlow::LocalSourceNode useCandFwd(TypeTracker t) {
t.start() and
isUse(result)
@@ -326,6 +393,7 @@ module API {
exists(TypeTracker t2 | result = useCandFwd(t2).track(t2, t))
}
/** Gets a node reachable from a use-node. */
private DataFlow::LocalSourceNode useCandFwd() { result = useCandFwd(TypeTracker::end()) }
private DataFlow::Node useCandRev(TypeBackTracker tb) {
@@ -345,6 +413,81 @@ module API {
isUse(result)
}
private predicate isDef(DataFlow::Node rhs) {
// If a call node is relevant as a use-node, treat its arguments as def-nodes
argumentStep(_, useCandFwd(), rhs)
}
/** Gets a data flow node that flows to the RHS of a def-node. */
private DataFlow::LocalSourceNode defCand(TypeBackTracker t) {
t.start() and
exists(DataFlow::Node rhs |
isDef(rhs) and
result = rhs.getALocalSource()
)
or
exists(TypeBackTracker t2 | result = defCand(t2).backtrack(t2, t))
}
/** Gets a data flow node that flows to the RHS of a def-node. */
private DataFlow::LocalSourceNode defCand() { result = defCand(TypeBackTracker::end()) }
private Label::ApiLabel getLabelFromArgumentPosition(DataFlowDispatch::ArgumentPosition pos) {
exists(int n |
pos.isPositional(n) and
result = Label::parameter(n)
)
or
exists(string name |
pos.isKeyword(name) and
result = Label::keywordParameter(name)
)
or
pos.isBlock() and
result = Label::blockParameter()
}
private Label::ApiLabel getLabelFromParameterPosition(DataFlowDispatch::ParameterPosition pos) {
exists(int n |
pos.isPositional(n) and
result = Label::parameter(n)
)
or
exists(string name |
pos.isKeyword(name) and
result = Label::keywordParameter(name)
)
or
pos.isBlock() and
result = Label::blockParameter()
}
/**
* Holds if there should be a `lbl`-edge from the given call to an argument.
*/
pragma[nomagic]
private predicate argumentStep(
Label::ApiLabel lbl, DataFlow::CallNode call, DataFlowPrivate::ArgumentNode argument
) {
exists(DataFlowDispatch::ArgumentPosition argPos |
argument.sourceArgumentOf(call.asExpr(), argPos) and
lbl = getLabelFromArgumentPosition(argPos)
)
}
/**
* Holds if there should be a `lbl`-edge from the given callable to a parameter.
*/
pragma[nomagic]
private predicate parameterStep(
Label::ApiLabel lbl, DataFlow::Node callable, DataFlowPrivate::ParameterNodeImpl paramNode
) {
exists(DataFlowDispatch::ParameterPosition paramPos |
paramNode.isSourceParameterOf(callable.asExpr().getExpr(), paramPos) and
lbl = getLabelFromParameterPosition(paramPos)
)
}
/**
* Gets a data-flow node to which `src`, which is a use of an API-graph node, flows.
*
@@ -373,21 +516,79 @@ module API {
result = trackUseNode(src, TypeTracker::end())
}
/** Gets a data flow node reaching the RHS of the given def node. */
private DataFlow::LocalSourceNode trackDefNode(DataFlow::Node rhs, TypeBackTracker t) {
t.start() and
isDef(rhs) and
result = rhs.getALocalSource()
or
exists(TypeBackTracker t2 | result = trackDefNode(rhs, t2).backtrack(t2, t))
}
/** Gets a data flow node reaching the RHS of the given def node. */
cached
DataFlow::LocalSourceNode trackDefNode(DataFlow::Node rhs) {
result = trackDefNode(rhs, TypeBackTracker::end())
}
pragma[nomagic]
private predicate useNodeReachesReceiver(DataFlow::Node use, DataFlow::CallNode call) {
trackUseNode(use).flowsTo(call.getReceiver())
}
/**
* Holds if there is an edge from `pred` to `succ` in the API graph that is labeled with `lbl`.
*/
cached
predicate edge(TApiNode pred, string lbl, TApiNode succ) {
predicate edge(TApiNode pred, Label::ApiLabel lbl, TApiNode succ) {
/* Every node that is a use of an API component is itself added to the API graph. */
exists(DataFlow::LocalSourceNode ref | succ = MkUse(ref) |
pred = MkRoot() and
useRoot(lbl, ref)
or
exists(ExprCfgNode node, DataFlow::Node src |
exists(DataFlow::Node node, DataFlow::Node src |
pred = MkUse(src) and
trackUseNode(src).flowsTo(any(DataFlow::ExprNode n | n.getExprNode() = node)) and
trackUseNode(src).flowsTo(node) and
useStep(lbl, node, ref)
)
or
exists(DataFlow::Node callback |
pred = MkDef(callback) and
parameterStep(lbl, trackDefNode(callback), ref)
)
)
or
// `pred` is a use of class A
// `succ` is a use of class B
// there exists a class declaration B < A
exists(ClassDeclaration c, DataFlow::Node a, DataFlow::Node b |
use(pred, a) and
use(succ, b) and
resolveConstant(b.asExpr().getExpr()) = resolveConstantWriteAccess(c) and
c.getSuperclassExpr() = a.asExpr().getExpr() and
lbl = Label::subclass()
)
or
exists(DataFlow::CallNode call |
// from receiver to method call node
exists(DataFlow::Node receiver |
pred = MkUse(receiver) and
useNodeReachesReceiver(receiver, call) and
lbl = Label::method(call.getMethodName()) and
succ = MkMethodAccessNode(call)
)
or
// from method call node to return and arguments
pred = MkMethodAccessNode(call) and
(
lbl = Label::return() and
succ = MkUse(call)
or
exists(DataFlow::Node rhs |
argumentStep(lbl, call, rhs) and
succ = MkDef(rhs)
)
)
)
}
@@ -399,25 +600,140 @@ module API {
/** Gets the shortest distance from the root to `nd` in the API graph. */
cached
int distanceFromRoot(TApiNode nd) = shortestDistances(MkRoot/0, edge/2)(_, nd, result)
/** All the possible labels in the API graph. */
cached
newtype TLabel =
MkLabelMember(string member) { member = any(ConstantReadAccess a).getName() } or
MkLabelUnknownMember() or
MkLabelMethod(string m) { m = any(DataFlow::CallNode c).getMethodName() } or
MkLabelReturn() or
MkLabelSubclass() or
MkLabelKeywordParameter(string name) {
any(DataFlowDispatch::ArgumentPosition arg).isKeyword(name)
or
any(DataFlowDispatch::ParameterPosition arg).isKeyword(name)
} or
MkLabelParameter(int n) {
any(DataFlowDispatch::ArgumentPosition c).isPositional(n)
or
any(DataFlowDispatch::ParameterPosition c).isPositional(n)
} or
MkLabelBlockParameter()
}
/** Provides classes modeling the various edges (labels) in the API graph. */
module Label {
/** A label in the API-graph */
class ApiLabel extends Impl::TLabel {
/** Gets a string representation of this label. */
string toString() { result = "???" }
}
private import LabelImpl
private module LabelImpl {
private import Impl
/** A label for a member, for example a constant. */
class LabelMember extends ApiLabel {
private string member;
LabelMember() { this = MkLabelMember(member) }
/** Gets the member name associated with this label. */
string getMember() { result = member }
override string toString() { result = "getMember(\"" + member + "\")" }
}
/** A label for a member with an unknown name. */
class LabelUnknownMember extends ApiLabel {
LabelUnknownMember() { this = MkLabelUnknownMember() }
override string toString() { result = "getUnknownMember()" }
}
/** A label for a method. */
class LabelMethod extends ApiLabel {
private string method;
LabelMethod() { this = MkLabelMethod(method) }
/** Gets the method name associated with this label. */
string getMethod() { result = method }
override string toString() { result = "getMethod(\"" + method + "\")" }
}
/** A label for the return value of a method. */
class LabelReturn extends ApiLabel {
LabelReturn() { this = MkLabelReturn() }
override string toString() { result = "getReturn()" }
}
/** A label for the subclass relationship. */
class LabelSubclass extends ApiLabel {
LabelSubclass() { this = MkLabelSubclass() }
override string toString() { result = "getASubclass()" }
}
/** A label for a keyword parameter. */
class LabelKeywordParameter extends ApiLabel {
private string name;
LabelKeywordParameter() { this = MkLabelKeywordParameter(name) }
/** Gets the name of the keyword parameter associated with this label. */
string getName() { result = name }
override string toString() { result = "getKeywordParameter(\"" + name + "\")" }
}
/** A label for a parameter. */
class LabelParameter extends ApiLabel {
private int n;
LabelParameter() { this = MkLabelParameter(n) }
/** Gets the parameter number associated with this label. */
int getIndex() { result = n }
override string toString() { result = "getParameter(" + n + ")" }
}
/** A label for a block parameter. */
class LabelBlockParameter extends ApiLabel {
LabelBlockParameter() { this = MkLabelBlockParameter() }
override string toString() { result = "getBlock()" }
}
}
/** Gets the `member` edge label for member `m`. */
LabelMember member(string m) { result.getMember() = m }
/** Gets the `member` edge label for the unknown member. */
LabelUnknownMember unknownMember() { any() }
/** Gets the `method` edge label. */
LabelMethod method(string m) { result.getMethod() = m }
/** Gets the `return` edge label. */
LabelReturn return() { any() }
/** Gets the `subclass` edge label. */
LabelSubclass subclass() { any() }
/** Gets the label representing the given keword argument/parameter. */
LabelKeywordParameter keywordParameter(string name) { result.getName() = name }
/** Gets the label representing the `n`th positional argument/parameter. */
LabelParameter parameter(int n) { result.getIndex() = n }
/** Gets the label representing the block argument/parameter. */
LabelBlockParameter blockParameter() { any() }
}
}
private module Label {
/** Gets the `member` edge label for member `m`. */
bindingset[m]
bindingset[result]
string member(string m) { result = "getMember(\"" + m + "\")" }
/** Gets the `member` edge label for the unknown member. */
string unknownMember() { result = "getUnknownMember()" }
/** Gets the `instance` edge label. */
string instance() { result = "instance" }
/** Gets the `return` edge label. */
bindingset[m]
bindingset[result]
string return(string m) { result = "getReturn(\"" + m + "\")" }
string subclass() { result = "getASubclass()" }
}

View File

@@ -6,9 +6,11 @@ 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.ActiveSupport
private import codeql.ruby.frameworks.GraphQL
private import codeql.ruby.frameworks.Rails
private import codeql.ruby.frameworks.StandardLibrary
private import codeql.ruby.frameworks.Files
private import codeql.ruby.frameworks.HttpClients
private import codeql.ruby.frameworks.XmlParsing
private import codeql.ruby.frameworks.ActionDispatch

View File

@@ -187,7 +187,10 @@ class BlockArgument extends Expr, TBlockArgument {
* foo(&bar)
* ```
*/
final Expr getValue() { toGenerated(result) = g.getChild() }
final Expr getValue() {
toGenerated(result) = g.getChild() or
synthChild(this, 0, result)
}
final override string toString() { result = "&..." }

View File

@@ -157,6 +157,7 @@ class ErbDirective extends TDirectiveNode, ErbAstNode {
* Gets a statement that starts in directive that is not a child of any other
* statement starting in this directive.
*/
cached
Stmt getAChildStmt() {
this.containsAstNodeStart(result) and
not this.containsAstNodeStart(result.getParent())

View File

@@ -293,13 +293,16 @@ class Pair extends Expr, TPair {
final Expr getKey() { toGenerated(result) = g.getKey() }
/**
* Gets the value expression of this pair. For example, the `InteralLiteral`
* Gets the value expression of this pair. For example, the `IntegerLiteral`
* 123 in the following hash pair:
* ```rb
* { 'foo' => 123 }
* ```
*/
final Expr getValue() { toGenerated(result) = g.getValue() }
final Expr getValue() {
toGenerated(result) = g.getValue() or
synthChild(this, 0, result)
}
final override string toString() { result = "Pair" }

View File

@@ -148,11 +148,19 @@ class BlockParameter extends NamedParameter, TBlockParameter {
BlockParameter() { this = TBlockParameter(g) }
/** Gets the name of this parameter, if any. */
final override string getName() { result = g.getName().getValue() }
final override LocalVariable getVariable() { result = TLocalVariableReal(_, _, g.getName()) }
final override LocalVariable getVariable() {
result = TLocalVariableReal(_, _, g.getName()) or
result = TLocalVariableSynth(this, 0)
}
final override string toString() { result = "&" + this.getName() }
final override string toString() {
result = "&" + this.getName()
or
not exists(this.getName()) and result = "&"
}
final override string getAPrimaryQlClass() { result = "BlockParameter" }
}

View File

@@ -80,7 +80,7 @@ deprecated class TuplePattern extends Pattern, TTuplePattern {
private class TPatternNode =
TArrayPattern or TFindPattern or THashPattern or TAlternativePattern or TAsPattern or
TParenthesizedPattern or TVariableReferencePattern;
TParenthesizedPattern or TExpressionReferencePattern or TVariableReferencePattern;
private class TPattern =
TPatternNode or TLiteral or TLambda or TConstantAccess or TLocalVariableAccess or
@@ -404,6 +404,42 @@ class ParenthesizedPattern extends CasePattern, TParenthesizedPattern {
}
/**
* A variable or value reference in a pattern, i.e. `^x`, and `^(2 * x)` in the following example:
* ```rb
* x = 10
* case expr
* in ^x then puts "ok"
* in ^(2 * x) then puts "ok"
* end
* ```
*/
class ReferencePattern extends CasePattern, TReferencePattern {
private Ruby::AstNode value;
ReferencePattern() {
value = any(Ruby::VariableReferencePattern g | this = TVariableReferencePattern(g)).getName()
or
value =
any(Ruby::ExpressionReferencePattern g | this = TExpressionReferencePattern(g)).getValue()
}
/** Gets the value this reference pattern matches against. For example `2 * x` in `^(2 * x)` */
final Expr getExpr() { toGenerated(result) = value }
final override string getAPrimaryQlClass() { result = "ReferencePattern" }
final override string toString() { result = "^..." }
final override AstNode getAChild(string pred) {
result = super.getAChild(pred)
or
pred = "getExpr" and result = this.getExpr()
}
}
/**
* DEPRECATED: Use `ReferencePattern` instead.
*
* A variable reference in a pattern, i.e. `^x` in the following example:
* ```rb
* x = 10
@@ -412,21 +448,7 @@ class ParenthesizedPattern extends CasePattern, TParenthesizedPattern {
* end
* ```
*/
class VariableReferencePattern extends CasePattern, TVariableReferencePattern {
private Ruby::VariableReferencePattern g;
VariableReferencePattern() { this = TVariableReferencePattern(g) }
deprecated class VariableReferencePattern extends ReferencePattern, TVariableReferencePattern {
/** Gets the variable access corresponding to this variable reference pattern. */
LocalVariableReadAccess getVariableAccess() { toGenerated(result) = g.getName() }
final override string getAPrimaryQlClass() { result = "VariableReferencePattern" }
final override string toString() { result = "^..." }
final override AstNode getAChild(string pred) {
result = super.getAChild(pred)
or
pred = "getVariableAccess" and result = this.getVariableAccess()
}
final VariableReadAccess getVariableAccess() { result = this.getExpr() }
}

View File

@@ -35,7 +35,10 @@ class LocalVariable extends Variable, TLocalVariable {
override LocalVariableAccess getAnAccess() { result.getVariable() = this }
/** Gets the access where this local variable is first introduced. */
VariableAccess getDefiningAccess() { result = this.(LocalVariableReal).getDefiningAccessImpl() }
VariableAccess getDefiningAccess() {
result = this.(LocalVariableReal).getDefiningAccessImpl() or
synthChild(any(BlockParameter p | this = p.getVariable()), 0, result)
}
/**
* Holds if this variable is captured. For example in
@@ -117,6 +120,8 @@ class VariableAccess extends Expr instanceof VariableAccessImpl {
this = any(SimpleParameterSynthImpl p).getDefininingAccess()
or
this = any(HashPattern p).getValue(_)
or
synthChild(any(BlockParameter p), 0, this)
}
final override string toString() { result = VariableAccessImpl.super.toString() }

View File

@@ -320,11 +320,14 @@ private module Cached {
TUntilExpr(Ruby::Until g) or
TUntilModifierExpr(Ruby::UntilModifier g) or
TVariableReferencePattern(Ruby::VariableReferencePattern g) or
TExpressionReferencePattern(Ruby::ExpressionReferencePattern g) or
TWhenClause(Ruby::When g) or
TWhileExpr(Ruby::While g) or
TWhileModifierExpr(Ruby::WhileModifier g) or
TYieldCall(Ruby::Yield g)
class TReferencePattern = TVariableReferencePattern or TExpressionReferencePattern;
class TAstNodeReal =
TAddExprReal or TAliasStmt or TAlternativePattern or TArgumentList or TArrayPattern or
TAsPattern or TAssignAddExpr or TAssignBitwiseAndExpr or TAssignBitwiseOrExpr or
@@ -359,7 +362,7 @@ private module Cached {
TTernaryIfExpr or TThen or TTokenConstantAccess or TTokenMethodName or TTokenSuperCall or
TToplevel or TTrueLiteral or TUnaryMinusExpr or TUnaryPlusExpr or TUndefStmt or
TUnlessExpr or TUnlessModifierExpr or TUntilExpr or TUntilModifierExpr or
TVariableReferencePattern or TWhenClause or TWhileExpr or TWhileModifierExpr or TYieldCall;
TReferencePattern or TWhenClause or TWhileExpr or TWhileModifierExpr or TYieldCall;
class TAstNodeSynth =
TAddExprSynth or TAssignExprSynth or TBitwiseAndExprSynth or TBitwiseOrExprSynth or
@@ -529,6 +532,7 @@ private module Cached {
n = TUntilExpr(result) or
n = TUntilModifierExpr(result) or
n = TVariableReferencePattern(result) or
n = TExpressionReferencePattern(result) or
n = TWhenClause(result) or
n = TWhileExpr(result) or
n = TWhileModifierExpr(result) or

View File

@@ -233,12 +233,17 @@ private module ResolveImpl {
pragma[nomagic]
private string resolveConstantReadAccessNonRec(ConstantReadAccess c, int priority) {
// ::B
c.hasGlobalScope() and result = c.getName() and priority = 0
or
// A::B
exists(string name, string s | result = isDefinedConstantNonRec(s, name) |
s = resolveConstantReadAccessScopeNonRec(c, priority, name)
)
or
// module A
// B
// end
exists(string name |
exists(Namespace n, string qname |
n = constantReadAccessEnclosingNameSpace(c, priority, name) and

View File

@@ -975,24 +975,107 @@ private module ForLoopDesugar {
* ```
*/
private module ImplicitHashValueSynthesis {
private Ruby::AstNode keyWithoutValue(HashPattern parent, int i) {
private Ruby::AstNode keyWithoutValue(AstNode parent, int i) {
exists(Ruby::KeywordPattern pair |
result = pair.getKey() and
result = toGenerated(parent.getKey(i)) and
result = toGenerated(parent.(HashPattern).getKey(i)) and
not exists(pair.getValue())
)
or
exists(Ruby::Pair pair |
i = 0 and
result = pair.getKey() and
pair = toGenerated(parent) and
not exists(pair.getValue())
)
}
private string keyName(Ruby::AstNode key) {
result = key.(Ruby::String).getChild(0).(Ruby::StringContent).getValue() or
result = key.(Ruby::HashKeySymbol).getValue()
}
private class ImplicitHashValueSynthesis extends Synthesis {
final override predicate child(AstNode parent, int i, Child child) {
exists(TVariableReal variable |
access(keyWithoutValue(parent, i), variable) and
child = SynthChild(LocalVariableAccessRealKind(variable))
exists(Ruby::AstNode key | key = keyWithoutValue(parent, i) |
exists(TVariableReal variable |
access(key, variable) and
child = SynthChild(LocalVariableAccessRealKind(variable))
)
or
not access(key, _) and
exists(string name | name = keyName(key) |
child = SynthChild(ConstantReadAccessKind(name)) or
child = SynthChild(MethodCallKind(name, false, 0))
)
)
}
final override predicate methodCall(string name, boolean setter, int arity) {
setter = false and
arity = 0 and
name = keyName(keyWithoutValue(_, _)) and
not name.charAt(0).isUppercase()
}
final override predicate constantReadAccess(string name) {
name = keyName(keyWithoutValue(_, _)) and
name.charAt(0).isUppercase()
}
final override predicate location(AstNode n, Location l) {
exists(HashPattern p, int i | n = p.getValue(i) and l = keyWithoutValue(p, i).getLocation())
exists(AstNode p, int i | l = keyWithoutValue(p, i).getLocation() |
n = p.(HashPattern).getValue(i)
or
i = 0 and n = p.(Pair).getValue()
)
}
}
}
/**
* ```rb
* def foo(&)
* bar(&)
* end
* ```
* desugars to,
* ```rb
* def foo(&__synth_0)
* bar(&__synth_0)
* end
* ```
*/
private module AnonymousBlockParameterSynth {
private BlockParameter anonymousBlockParameter() {
exists(Ruby::BlockParameter p | not exists(p.getName()) and toGenerated(result) = p)
}
private BlockArgument anonymousBlockArgument() {
exists(Ruby::BlockArgument p | not exists(p.getChild()) and toGenerated(result) = p)
}
private class AnonymousBlockParameterSynthesis extends Synthesis {
final override predicate child(AstNode parent, int i, Child child) {
i = 0 and
parent = anonymousBlockParameter() and
child = SynthChild(LocalVariableAccessSynthKind(TLocalVariableSynth(parent, 0)))
}
final override predicate localVariable(AstNode n, int i) {
n = anonymousBlockParameter() and i = 0
}
}
private class AnonymousBlockArgumentSynthesis extends Synthesis {
final override predicate child(AstNode parent, int i, Child child) {
i = 0 and
parent = anonymousBlockArgument() and
exists(BlockParameter param |
param = anonymousBlockParameter() and
scopeOf(toGenerated(parent)).getEnclosingMethod() = scopeOf(toGenerated(param)) and
child = SynthChild(LocalVariableAccessSynthKind(TLocalVariableSynth(param, 0)))
)
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -116,6 +116,11 @@ private string variableNameInScope(Ruby::AstNode i, Scope::Range scope) {
result = i.(Ruby::String).getChild(0).(Ruby::StringContent).getValue() or
result = i.(Ruby::HashKeySymbol).getValue()
)
or
exists(Ruby::Pair p | i = p.getKey() and not exists(p.getValue()) |
result = i.(Ruby::String).getChild(0).(Ruby::StringContent).getValue() or
result = i.(Ruby::HashKeySymbol).getValue()
)
)
}
@@ -307,6 +312,8 @@ private module Cached {
i = any(Ruby::WhileModifier x).getCondition()
or
i = any(Ruby::WhileModifier x).getBody()
or
i = any(Ruby::ExpressionReferencePattern x).getValue()
}
pragma[nomagic]
@@ -414,10 +421,10 @@ class TVariableReal =
class TLocalVariable = TLocalVariableReal or TLocalVariableSynth or TSelfVariable;
/**
* This class only exists to avoid negative recursion warnings. Ideally,
* we would use `VariableImpl` directly, but that results in incorrect
* negative recursion warnings. Adding new root-defs for the predicates
* below works around this.
* A "real" (i.e. non-synthesized) variable. This class only exists to
* avoid negative recursion warnings. Ideally, we would use `VariableImpl`
* directly, but that results in incorrect negative recursion warnings.
* Adding new root-defs for the predicates below works around this.
*/
abstract class VariableReal extends TVariableReal {
abstract string getNameImpl();

View File

@@ -309,6 +309,17 @@ private module Cached {
jbp order by JoinBlockPredecessors::getId(jbp), JoinBlockPredecessors::getSplitString(jbp)
)
}
cached
predicate immediatelyControls(ConditionBlock cb, BasicBlock succ, BooleanSuccessor s) {
succ = cb.getASuccessor(s) and
forall(BasicBlock pred | pred = succ.getAPredecessor() and pred != cb | succ.dominates(pred))
}
cached
predicate controls(ConditionBlock cb, BasicBlock controlled, BooleanSuccessor s) {
exists(BasicBlock succ | cb.immediatelyControls(succ, s) | succ.dominates(controlled))
}
}
private import Cached
@@ -395,10 +406,8 @@ class ConditionBlock extends BasicBlock {
* successor of this block, and `succ` can only be reached from
* the callable entry point by going via the `s` edge out of this basic block.
*/
pragma[nomagic]
predicate immediatelyControls(BasicBlock succ, BooleanSuccessor s) {
succ = this.getASuccessor(s) and
forall(BasicBlock pred | pred = succ.getAPredecessor() and pred != this | succ.dominates(pred))
immediatelyControls(this, succ, s)
}
/**
@@ -406,7 +415,5 @@ class ConditionBlock extends BasicBlock {
* conditional value `s`. That is, `controlled` can only be reached from
* the callable entry point by going via the `s` edge out of this basic block.
*/
predicate controls(BasicBlock controlled, BooleanSuccessor s) {
exists(BasicBlock succ | this.immediatelyControls(succ, s) | succ.dominates(controlled))
}
predicate controls(BasicBlock controlled, BooleanSuccessor s) { controls(this, controlled, s) }
}

View File

@@ -235,7 +235,7 @@ private predicate inMatchingContext(AstNode n) {
or
n instanceof CasePattern
or
n = any(VariableReferencePattern p).getVariableAccess()
n = any(ReferencePattern p).getExpr()
or
n.(Trees::DefaultValueParameterTree).hasDefaultValue()
}

View File

@@ -731,8 +731,8 @@ module Trees {
override ControlFlowTree getChildElement(int i) { result = this.getPattern() and i = 0 }
}
private class VariableReferencePatternTree extends StandardPreOrderTree, VariableReferencePattern {
override ControlFlowTree getChildElement(int i) { result = this.getVariableAccess() and i = 0 }
private class ReferencePatternTree extends StandardPreOrderTree, ReferencePattern {
override ControlFlowTree getChildElement(int i) { result = this.getExpr() and i = 0 }
}
private class InClauseTree extends PreOrderTree, InClause {

View File

@@ -214,7 +214,7 @@ abstract class SplitKind extends SplitKindBase {
abstract string toString();
}
/** Provides the interface for implementing an entity to split on. */
/** An interface for implementing an entity to split on. */
abstract class SplitImpl extends Split {
/** Gets the kind of this split. */
abstract SplitKind getKind();
@@ -900,10 +900,25 @@ module TestOutput {
}
query predicate edges(RelevantNode pred, RelevantNode succ, string attr, string val) {
attr = "semmle.label" and
exists(SuccessorType t | succ = getASuccessor(pred, t) |
attr = "semmle.label" and
if successorTypeIsSimple(t) then val = "" else val = t.toString()
)
or
attr = "semmle.order" and
val =
any(int i |
succ =
rank[i](RelevantNode s, SuccessorType t, Location l |
s = getASuccessor(pred, t) and
l = s.getLocation()
|
s
order by
l.getFile().getBaseName(), l.getFile().getAbsolutePath(), l.getStartLine(),
l.getStartColumn(), l.getEndLine(), l.getEndColumn(), t.toString()
)
).toString()
}
}

View File

@@ -134,10 +134,12 @@ abstract class SummarizedCallable extends LibraryCallable {
* calls to a method with the same name are considered relevant.
*/
abstract class SimpleSummarizedCallable extends SummarizedCallable {
bindingset[this]
SimpleSummarizedCallable() { any() }
MethodCall mc;
final override MethodCall getACall() { result.getMethodName() = this }
bindingset[this]
SimpleSummarizedCallable() { mc.getMethodName() = this }
final override MethodCall getACall() { result = mc }
}
private class SummarizedCallableAdapter extends Impl::Public::SummarizedCallable {

View File

@@ -241,7 +241,11 @@ private module Cached {
or
FlowSummaryImplSpecific::ParsePositions::isParsedParameterPosition(_, pos)
} or
TKeywordArgumentPosition(string name) { name = any(KeywordParameter kp).getName() }
TKeywordArgumentPosition(string name) {
name = any(KeywordParameter kp).getName()
or
exists(any(Call c).getKeywordArgument(name))
}
cached
newtype TParameterPosition =
@@ -463,18 +467,7 @@ predicate mayBenefitFromCallContext(DataFlowCall call, DataFlowCallable c) { non
*/
DataFlowCallable viableImplInCallContext(DataFlowCall call, DataFlowCall ctx) { none() }
/**
* Holds if `e` is an `ExprNode` that may be returned by a call to `c`.
*/
predicate exprNodeReturnedFrom(DataFlow::ExprNode e, Callable c) {
exists(ReturningNode r |
nodeGetEnclosingCallable(r).asCallable() = c and
(
r.(ExplicitReturnNode).getReturningNode().getReturnedValueNode() = e.asExpr() or
r.(ExprReturnNode) = e
)
)
}
predicate exprNodeReturnedFrom = exprNodeReturnedFromCached/2;
/** A parameter position. */
class ParameterPosition extends TParameterPosition {

View File

@@ -1290,7 +1290,7 @@ class DataFlowCallOption extends TDataFlowCallOption {
}
}
/** Content tagged with the type of a containing object. */
/** A `Content` tagged with the type of a containing object. */
class TypedContent extends MkTypedContent {
private Content c;
private DataFlowType t;

View File

@@ -285,11 +285,17 @@ private module Cached {
// and we can remove this case.
n.asExpr().getExpr() instanceof Self
or
// Nodes that can't be reached from another parameter or expression.
not localFlowStepTypeTracker+(any(Node e |
e instanceof ExprNode
or
e instanceof ParameterNode
), n)
or
// Ensure all parameter SSA nodes are local sources -- this is needed by type tracking.
// Note that when the parameter has a default value, it will be reachable from an
// expression (the default value) and therefore won't be caught by the rule above.
n = LocalFlow::getParameterDefNode(_)
}
cached
@@ -297,6 +303,20 @@ private module Cached {
TKnownArrayElementContent(int i) { i in [0 .. 10] } or
TUnknownArrayElementContent() or
TAnyArrayElementContent()
/**
* Holds if `e` is an `ExprNode` that may be returned by a call to `c`.
*/
cached
predicate exprNodeReturnedFromCached(ExprNode e, Callable c) {
exists(ReturningNode r |
nodeGetEnclosingCallable(r).asCallable() = c and
(
r.(ExplicitReturnNode).getReturningNode().getReturnedValueNode() = e.asExpr() or
r.(ExprReturnNode) = e
)
)
}
}
class TArrayElementContent = TKnownArrayElementContent or TUnknownArrayElementContent;
@@ -306,9 +326,13 @@ import Cached
/** Holds if `n` should be hidden from path explanations. */
predicate nodeIsHidden(Node n) {
exists(Ssa::Definition def | def = n.(SsaDefinitionNode).getDefinition() |
def instanceof Ssa::PhiNode
def instanceof Ssa::PhiNode or
def instanceof Ssa::CapturedEntryDefinition or
def instanceof Ssa::CapturedCallDefinition
)
or
n = LocalFlow::getParameterDefNode(_)
or
isDesugarNode(n.(ExprNode).getExprNode().getExpr())
or
n instanceof SummaryNode

View File

@@ -18,11 +18,11 @@ class Node extends TNode {
Parameter asParameter() { result = this.(ParameterNode).getParameter() }
/** Gets a textual representation of this node. */
// TODO: cache
cached
final string toString() { result = this.(NodeImpl).toStringImpl() }
/** Gets the location of this node. */
// TODO: cache
cached
final Location getLocation() { result = this.(NodeImpl).getLocationImpl() }
/**
@@ -121,7 +121,8 @@ class LocalSourceNode extends Node {
LocalSourceNode backtrack(TypeBackTracker t2, TypeBackTracker t) { t2 = t.step(result, this) }
}
predicate hasLocalSource(Node sink, Node source) {
cached
private predicate hasLocalSource(Node sink, Node source) {
// Declaring `source` to be a `SourceNode` currently causes a redundant check in the
// recursive case, so instead we check it explicitly here.
source = sink and
@@ -151,12 +152,14 @@ predicate localFlowStep = localFlowStepImpl/2;
* Holds if data flows from `source` to `sink` in zero or more local
* (intra-procedural) steps.
*/
pragma[inline]
predicate localFlow(Node source, Node sink) { localFlowStep*(source, sink) }
/**
* Holds if data can flow from `e1` to `e2` in zero or more
* local (intra-procedural) steps.
*/
pragma[inline]
predicate localExprFlow(CfgNodes::ExprCfgNode e1, CfgNodes::ExprCfgNode e2) {
localFlow(exprNode(e1), exprNode(e2))
}

View File

@@ -90,7 +90,9 @@ module Public {
predicate contains(SummaryComponent c) { c = this.drop(_).head() }
/** Gets the bottom element of this stack. */
SummaryComponent bottom() { result = this.drop(this.length() - 1).head() }
SummaryComponent bottom() {
this = TSingletonSummaryComponentStack(result) or result = this.tail().bottom()
}
/** Gets a textual representation of this stack. */
string toString() {

View File

@@ -20,15 +20,6 @@ private predicate hasCapturedVariableRead(BasicBlock bb, LocalVariable v) {
)
}
/**
* Holds if an entry definition is needed for captured variable `v` at index
* `i` in entry block `bb`.
*/
predicate capturedEntryWrite(EntryBasicBlock bb, int i, LocalVariable v) {
hasCapturedVariableRead(bb.getASuccessor*(), v) and
i = -1
}
/** Holds if `bb` contains a caputured write to variable `v`. */
pragma[noinline]
private predicate writesCapturedVariable(BasicBlock bb, LocalVariable v) {
@@ -132,6 +123,16 @@ private predicate hasVariableReadWithCapturedWrite(BasicBlock bb, LocalVariable
cached
private module Cached {
/**
* Holds if an entry definition is needed for captured variable `v` at index
* `i` in entry block `bb`.
*/
cached
predicate capturedEntryWrite(EntryBasicBlock bb, int i, LocalVariable v) {
hasCapturedVariableRead(bb.getASuccessor*(), v) and
i = -1
}
/**
* Holds if the call at index `i` in basic block `bb` may reach a callable
* that writes captured variable `v`.

View File

@@ -8,12 +8,14 @@ private import FlowSummaryImpl as FlowSummaryImpl
* Holds if taint propagates from `source` to `sink` in zero or more local
* (intra-procedural) steps.
*/
pragma[inline]
predicate localTaint(DataFlow::Node source, DataFlow::Node sink) { localTaintStep*(source, sink) }
/**
* Holds if taint can flow from `e1` to `e2` in zero or more
* local (intra-procedural) steps.
*/
pragma[inline]
predicate localExprTaint(CfgNodes::ExprCfgNode e1, CfgNodes::ExprCfgNode e2) {
localTaint(DataFlow::exprNode(e1), DataFlow::exprNode(e2))
}

View File

@@ -4,21 +4,9 @@ private import codeql.ruby.controlflow.CfgNodes
private import codeql.ruby.DataFlow
private import codeql.ruby.dataflow.RemoteFlowSources
private import codeql.ruby.ast.internal.Module
private import codeql.ruby.ApiGraphs
private import ActionView
private class ActionControllerBaseAccess extends ConstantReadAccess {
ActionControllerBaseAccess() {
this.getName() = "Base" and
this.getScopeExpr().(ConstantAccess).getName() = "ActionController"
}
}
// ApplicationController extends ActionController::Base, but we
// treat it separately in case the ApplicationController definition
// is not in the database
private class ApplicationControllerAccess extends ConstantReadAccess {
ApplicationControllerAccess() { this.getName() = "ApplicationController" }
}
private import codeql.ruby.frameworks.ActionDispatch
/**
* A `ClassDeclaration` for a class that extends `ActionController::Base`.
@@ -35,16 +23,13 @@ private class ApplicationControllerAccess extends ConstantReadAccess {
*/
class ActionControllerControllerClass extends ClassDeclaration {
ActionControllerControllerClass() {
// class FooController < ActionController::Base
this.getSuperclassExpr() instanceof ActionControllerBaseAccess
or
// class FooController < ApplicationController
this.getSuperclassExpr() instanceof ApplicationControllerAccess
or
// class BarController < FooController
exists(ActionControllerControllerClass other |
other.getModule() = resolveConstantReadAccess(this.getSuperclassExpr())
)
this.getSuperclassExpr() =
[
API::getTopLevelMember("ActionController").getMember("Base"),
// In Rails applications `ApplicationController` typically extends `ActionController::Base`, but we
// treat it separately in case the `ApplicationController` definition is not in the database.
API::getTopLevelMember("ApplicationController")
].getASubclass().getAUse().asExpr().getExpr()
}
/**
@@ -85,6 +70,26 @@ class ActionControllerActionMethod extends Method, HTTP::Server::RequestHandler:
// not end at an explicit render or redirect
/** Gets the controller class containing this method. */
ActionControllerControllerClass getControllerClass() { result = controllerClass }
/**
* Gets a route to this handler, if one exists.
* May return multiple results.
*/
ActionDispatch::Route getARoute() {
exists(string name |
isRoute(result, name, controllerClass) and
isActionControllerMethod(this, name, controllerClass)
)
}
}
pragma[nomagic]
private predicate isRoute(
ActionDispatch::Route route, string name, ActionControllerControllerClass controllerClass
) {
route.getController() + "_controller" =
ActionDispatch::underscore(namespaceDeclaration(controllerClass)) and
name = route.getAction()
}
// A method call with a `self` receiver from within a controller class

View File

@@ -0,0 +1,951 @@
/**
* Models routing configuration specified using the `ActionDispatch` library, which is part of Rails.
*/
private import codeql.ruby.AST
private import codeql.ruby.Concepts
private import codeql.ruby.DataFlow
/**
* Models routing configuration specified using the `ActionDispatch` library, which is part of Rails.
*/
module ActionDispatch {
/**
* A block that defines some routes.
* Route blocks can contribute to the path or controller namespace of their child routes.
* For example, in the block below
* ```rb
* scope path: "/admin" do
* get "/dashboard", to: "admin_dashboard#show"
* end
* ```
* the route defined by the call to `get` has the full path `/admin/dashboard`.
* We track these contributions via `getPathComponent` and `getControllerComponent`.
*/
abstract private class RouteBlock extends TRouteBlock {
/**
* Gets the name of a primary CodeQL class to which this route block belongs.
*/
string getAPrimaryQlClass() { result = "RouteBlock" }
/**
* Gets a string representation of this route block.
*/
string toString() { none() }
/**
* Gets a `Stmt` within this route block.
*/
abstract Stmt getAStmt();
/**
* Gets the parent of this route block, if one exists.
*/
abstract RouteBlock getParent();
/**
* Gets the `n`th parent of this route block.
* The zeroth parent is this block, the first parent is the direct parent of this block, etc.
*/
RouteBlock getParent(int n) {
if n = 0 then result = this else result = this.getParent().getParent(n - 1)
}
/**
* Gets the component of the path defined by this block, if it exists.
*/
abstract string getPathComponent();
/**
* Gets the component of the controller namespace defined by this block, if it exists.
*/
abstract string getControllerComponent();
/**
* Gets the location of this route block.
*/
abstract Location getLocation();
}
/**
* A route block that is not the top-level block.
* This block will always have a parent.
*/
abstract private class NestedRouteBlock extends RouteBlock {
RouteBlock parent;
override RouteBlock getParent() { result = parent }
override string getAPrimaryQlClass() { result = "NestedRouteBlock" }
}
/**
* A top-level routes block.
* ```rb
* Rails.application.routes.draw do
* ...
* end
* ```
*/
private class TopLevelRouteBlock extends RouteBlock, TTopLevelRouteBlock {
MethodCall call;
// Routing blocks create scopes which define the namespace for controllers and paths,
// though they can be overridden in various ways.
// The namespaces can differ, so we track them separately.
Block block;
TopLevelRouteBlock() { this = TTopLevelRouteBlock(_, call, block) }
override string getAPrimaryQlClass() { result = "TopLevelRouteBlock" }
Block getBlock() { result = block }
override Stmt getAStmt() { result = block.getAStmt() }
override RouteBlock getParent() { none() }
override string toString() { result = call.toString() }
override Location getLocation() { result = call.getLocation() }
override string getPathComponent() { none() }
override string getControllerComponent() { none() }
}
/**
* A route block defined by a call to `constraints`.
* ```rb
* constraints(foo: /some_regex/) do
* get "/posts/:foo", to "posts#something"
* end
* ```
* https://api.rubyonrails.org/classes/ActionDispatch/Routing/Mapper/Scoping.html#method-i-constraints
*/
private class ConstraintsRouteBlock extends NestedRouteBlock, TConstraintsRouteBlock {
private Block block;
private MethodCall call;
ConstraintsRouteBlock() { this = TConstraintsRouteBlock(parent, call, block) }
override string getAPrimaryQlClass() { result = "ConstraintsRouteBlock" }
override Stmt getAStmt() { result = block.getAStmt() }
override string getPathComponent() { result = "" }
override string getControllerComponent() { result = "" }
override string toString() { result = call.toString() }
override Location getLocation() { result = call.getLocation() }
}
/**
* A route block defined by a call to `scope`.
* ```rb
* scope(path: "/some_path", module: "some_module") do
* get "/posts/:foo", to "posts#something"
* end
* ```
* https://api.rubyonrails.org/classes/ActionDispatch/Routing/Mapper/Scoping.html#method-i-scope
*/
private class ScopeRouteBlock extends NestedRouteBlock, TScopeRouteBlock {
private MethodCall call;
private Block block;
ScopeRouteBlock() { this = TScopeRouteBlock(parent, call, block) }
override string getAPrimaryQlClass() { result = "ScopeRouteBlock" }
override Stmt getAStmt() { result = block.getAStmt() }
override string toString() { result = call.toString() }
override Location getLocation() { result = call.getLocation() }
override string getPathComponent() {
call.getKeywordArgument("path").getConstantValue().isStringOrSymbol(result)
or
not exists(call.getKeywordArgument("path")) and
call.getArgument(0).getConstantValue().isStringOrSymbol(result)
}
override string getControllerComponent() {
call.getKeywordArgument(["controller", "module"]).getConstantValue().isStringOrSymbol(result)
}
}
/**
* A route block defined by a call to `resources`.
* ```rb
* resources :articles do
* get "/comments", to "comments#index"
* end
* ```
* https://api.rubyonrails.org/classes/ActionDispatch/Routing/Mapper/Resources.html#method-i-resources
*/
private class ResourcesRouteBlock extends NestedRouteBlock, TResourcesRouteBlock {
private MethodCall call;
private Block block;
ResourcesRouteBlock() { this = TResourcesRouteBlock(parent, call, block) }
override string getAPrimaryQlClass() { result = "ResourcesRouteBlock" }
override Stmt getAStmt() { result = block.getAStmt() }
/**
* Gets the `resources` call that gives rise to this route block.
*/
MethodCall getDefiningMethodCall() { result = call }
override string getPathComponent() {
exists(string resource | call.getArgument(0).getConstantValue().isStringOrSymbol(resource) |
result = resource + "/:" + singularize(resource) + "_id"
)
}
override string getControllerComponent() { result = "" }
override string toString() { result = call.toString() }
override Location getLocation() { result = call.getLocation() }
}
/**
* A route block that is guarded by a conditional statement.
* For example:
* ```rb
* if Rails.env.test?
* get "/foo/bar", to: "foo#bar"
* end
* ```
* We ignore the condition and analyze both branches to obtain as
* much routing information as possible.
*/
private class ConditionalRouteBlock extends NestedRouteBlock, TConditionalRouteBlock {
private ConditionalExpr e;
ConditionalRouteBlock() { this = TConditionalRouteBlock(parent, e) }
override string getAPrimaryQlClass() { result = "ConditionalRouteBlock" }
override Stmt getAStmt() { result = e.getBranch(_).(StmtSequence).getAStmt() }
override string getPathComponent() { none() }
override string getControllerComponent() { none() }
override string toString() { result = e.toString() }
override Location getLocation() { result = e.getLocation() }
}
/**
* A route block defined by a call to `namespace`.
* ```rb
* namespace :admin do
* resources :posts
* end
* ```
* https://api.rubyonrails.org/classes/ActionDispatch/Routing/Mapper/Scoping.html#method-i-namespace
*/
private class NamespaceRouteBlock extends NestedRouteBlock, TNamespaceRouteBlock {
private MethodCall call;
private Block block;
NamespaceRouteBlock() { this = TNamespaceRouteBlock(parent, call, block) }
override Stmt getAStmt() { result = block.getAStmt() }
override string getPathComponent() { result = this.getNamespace() }
override string getControllerComponent() { result = this.getNamespace() }
private string getNamespace() {
call.getArgument(0).getConstantValue().isStringOrSymbol(result)
}
override string toString() { result = call.toString() }
override Location getLocation() { result = call.getLocation() }
}
/**
* A route configuration. This defines a combination of HTTP method and URL
* path which should be routed to a particular controller-action pair.
* This can arise from an explicit call to a routing method, for example:
* ```rb
* get "/photos", to: "photos#index"
* ```
* or via a convenience method like `resources`, which defines mutiple routes at once:
* ```rb
* resources :photos
* ```
*/
class Route extends TRoute instanceof RouteImpl {
/**
* Gets the name of a primary CodeQL class to which this route belongs.
*/
string getAPrimaryQlClass() { result = "Route" }
/** Gets a string representation of this route. */
string toString() { result = super.toString() }
/**
* Gets the location of the method call that defines this route.
*/
Location getLocation() { result = super.getLocation() }
/**
* Gets the full controller targeted by this route.
*/
string getController() { result = super.getController() }
/**
* Gets the action targeted by this route.
*/
string getAction() { result = super.getAction() }
/**
* Gets the HTTP method of this route.
* The result is one of [get, post, put, patch, delete].
*/
string getHttpMethod() { result = super.getHttpMethod() }
/**
* Gets the full path of the route.
*/
string getPath() { result = super.getPath() }
/**
* Get a URL capture. This is a wildcard URL segment whose value is placed in `params`.
* For example, in
* ```ruby
* get "/foo/:bar/baz", to: "users#index"
* ```
* the capture is `:bar`.
*/
string getACapture() { result = super.getACapture() }
}
/**
* The implementation of `Route`.
* This class is abstract and is thus kept private so we don't expose it to
* users.
* Extend this class to add new instances of routes.
*/
abstract private class RouteImpl extends TRoute {
/**
* Gets the name of a primary CodeQL class to which this route belongs.
*/
string getAPrimaryQlClass() { result = "RouteImpl" }
MethodCall method;
/** Gets a string representation of this route. */
string toString() { result = method.toString() }
/**
* Gets the location of the method call that defines this route.
*/
Location getLocation() { result = method.getLocation() }
/**
* Gets the method call that defines this route.
*/
MethodCall getDefiningMethodCall() { result = method }
/**
* Get the last component of the path. For example, in
* ```rb
* get "/photos", to: "photos#index"
* ```
* this is `/photos`.
* If the string has any interpolations, this predicate will have no result.
*/
abstract string getLastPathComponent();
/**
* Gets the HTTP method of this route.
* The result is one of [get, post, put, patch, delete].
*/
abstract string getHttpMethod();
/**
* Gets the last controller component.
* This is the controller specified in the route itself.
*/
abstract string getLastControllerComponent();
/**
* Gets a component of the controller.
* This behaves identically to `getPathComponent`, but for controller information.
*/
string getControllerComponent(int n) {
if n = 0
then result = this.getLastControllerComponent()
else result = this.getParentBlock().getParent(n - 1).getControllerComponent()
}
/**
* Gets the full controller targeted by this route.
*/
string getController() {
result =
concat(int n |
this.getControllerComponent(n) != ""
|
this.getControllerComponent(n), "/" order by n desc
)
}
/**
* Gets the action targeted by this route.
*/
abstract string getAction();
/**
* Gets the parent `RouteBlock` of this route.
*/
abstract RouteBlock getParentBlock();
/**
* Gets a component of the path. Components are numbered from 0 up, where 0
* is the last component, 1 is the second-last, etc.
* For example, in the following route:
*
* ```rb
* namespace path: "foo" do
* namespace path: "bar" do
* get "baz", to: "foo#bar
* end
* end
* ```
*
* the components are:
*
* | n | component
* |---|----------
* | 0 | baz
* | 1 | bar
* | 2 | foo
*/
string getPathComponent(int n) {
if n = 0
then result = this.getLastPathComponent()
else result = this.getParentBlock().getParent(n - 1).getPathComponent()
}
/**
* Gets the full path of the route.
*/
string getPath() {
result =
concat(int n |
this.getPathComponent(n) != ""
|
// Strip leading and trailing slashes from each path component before combining
stripSlashes(this.getPathComponent(n)), "/" order by n desc
)
}
/**
* Get a URL capture. This is a wildcard URL segment whose value is placed in `params`.
* For example, in
* ```ruby
* get "/foo/:bar/baz", to: "users#index"
* ```
* the capture is `:bar`.
* We don't currently make use of this, but it may be useful in future to more accurately
* model the contents of the `params` hash.
*/
string getACapture() { result = this.getPathComponent(_).regexpFind(":[^:/]+", _, _) }
}
/**
* A route generated by an explicit call to `get`, `post`, etc.
*
* ```ruby
* get "/photos", to: "photos#index"
* put "/photos/:id", to: "photos#update"
* ```
*/
private class ExplicitRoute extends RouteImpl, TExplicitRoute {
RouteBlock parentBlock;
ExplicitRoute() { this = TExplicitRoute(parentBlock, method) }
override string getAPrimaryQlClass() { result = "ExplicitRoute" }
override RouteBlock getParentBlock() { result = parentBlock }
override string getLastPathComponent() {
method.getArgument(0).getConstantValue().isStringOrSymbol(result)
}
override string getLastControllerComponent() {
method.getKeywordArgument("controller").getConstantValue().isStringOrSymbol(result)
or
not exists(method.getKeywordArgument("controller")) and
(
result = extractController(this.getActionString())
or
// If controller is not specified, and we're in a `resources` route block, use the controller of that route.
// For example, in
//
// resources :posts do
// get "timestamp", to: :timestamp
// end
//
// The route is GET /posts/:post_id/timestamp => posts/timestamp
not exists(extractController(this.getActionString())) and
exists(ResourcesRoute r |
r.getDefiningMethodCall() = parentBlock.(ResourcesRouteBlock).getDefiningMethodCall()
|
result = r.getLastControllerComponent()
)
)
}
private string getActionString() {
method.getKeywordArgument("to").getConstantValue().isStringOrSymbol(result)
or
method.getKeywordArgument("to").(MethodCall).getMethodName() = "redirect" and
result = "<redirect>#<redirect>"
}
override string getAction() {
// get "/photos", action: "index"
method.getKeywordArgument("action").getConstantValue().isStringOrSymbol(result)
or
not exists(method.getKeywordArgument("action")) and
(
// get "/photos", to: "photos#index"
// get "/photos", to: redirect("some_url")
result = extractAction(this.getActionString())
or
// resources :photos, only: [] do
// get "/", to: "index"
// end
not exists(extractAction(this.getActionString())) and result = this.getActionString()
or
// get :some_action
not exists(this.getActionString()) and
method.getArgument(0).getConstantValue().isStringOrSymbol(result)
)
}
override string getHttpMethod() { result = method.getMethodName().toString() }
}
/**
* A route generated by a call to `resources`.
*
* ```ruby
* resources :photos
* ```
* This creates eight routes, equivalent to the following code:
* ```ruby
* get "/photos", to: "photos#index"
* get "/photos/new", to: "photos#new"
* post "/photos", to: "photos#create"
* get "/photos/:id", to: "photos#show"
* get "/photos/:id/edit", to: "photos#edit"
* patch "/photos/:id", to: "photos#update"
* put "/photos/:id", to: "photos#update"
* delete "/photos/:id", to: "photos#delete"
* ```
*
* `resources` can take a block. Any routes defined inside the block will inherit a path component of
* `/<resource>/:<resource>_id`. For example:
*
* ```ruby
* resources :photos do
* get "/foo", to: "photos#foo"
* end
* ```
* This creates the eight default routes, plus one more, which is nested under "/photos/:photo_id", equivalent to:
* ```ruby
* get "/photos/:photo_id/foo", to: "photos#foo"
* ```
*/
private class ResourcesRoute extends RouteImpl, TResourcesRoute {
RouteBlock parent;
string resource;
string action;
string httpMethod;
string pathComponent;
ResourcesRoute() {
this = TResourcesRoute(parent, method, action) and
method.getArgument(0).getConstantValue().isStringOrSymbol(resource) and
isDefaultResourceRoute(resource, httpMethod, pathComponent, action)
}
override string getAPrimaryQlClass() { result = "ResourcesRoute" }
override RouteBlock getParentBlock() { result = parent }
override string getLastPathComponent() { result = pathComponent }
override string getLastControllerComponent() {
method.getArgument(0).getConstantValue().isStringOrSymbol(result)
}
override string getAction() { result = action }
override string getHttpMethod() { result = httpMethod }
}
/**
* A route generated by a call to `resource`.
* This is like a `resources` route, but creates routes for a singular resource.
* This means there's no index route, no id parameter, and the resource name is expected to be singular.
* It will still be routed to a pluralised controller name.
* ```ruby
* resource :account
* ```
*/
private class SingularResourceRoute extends RouteImpl, TResourceRoute {
RouteBlock parent;
string resource;
string action;
string httpMethod;
string pathComponent;
SingularResourceRoute() {
this = TResourceRoute(parent, method, action) and
method.getArgument(0).getConstantValue().isStringOrSymbol(resource) and
isDefaultSingularResourceRoute(resource, httpMethod, pathComponent, action)
}
override string getAPrimaryQlClass() { result = "SingularResourceRoute" }
override RouteBlock getParentBlock() { result = parent }
override string getLastPathComponent() { result = pathComponent }
override string getLastControllerComponent() {
method.getArgument(0).getConstantValue().isStringOrSymbol(result)
}
override string getAction() { result = action }
override string getHttpMethod() { result = httpMethod }
}
/**
* A route generated by a call to `match`.
* This is a lower level primitive that powers `get`, `post` etc.
* The first argument can be a path or a (path, controller-action) pair.
* The controller, action and HTTP method can be specified with the
* `controller:`, `action:` and `via:` keyword arguments, respectively.
* ```ruby
* match 'photos/:id' => 'photos#show', via: :get
* match 'photos/:id', to: 'photos#show', via: :get
* match 'photos/:id', to 'photos#show', via: [:get, :post]
* match 'photos/:id', controller: 'photos', action: 'show', via: :get
* ```
*/
private class MatchRoute extends RouteImpl, TMatchRoute {
private RouteBlock parent;
MatchRoute() { this = TMatchRoute(parent, method) }
override string getAPrimaryQlClass() { result = "MatchRoute" }
override RouteBlock getParentBlock() { result = parent }
override string getLastPathComponent() {
[method.getArgument(0), method.getArgument(0).(Pair).getKey()]
.getConstantValue()
.isStringOrSymbol(result)
}
override string getLastControllerComponent() {
result =
extractController(method.getKeywordArgument("to").getConstantValue().getStringOrSymbol()) or
method.getKeywordArgument("controller").getConstantValue().isStringOrSymbol(result) or
result =
extractController(method
.getArgument(0)
.(Pair)
.getValue()
.getConstantValue()
.getStringOrSymbol())
}
override string getHttpMethod() {
exists(string via |
method.getKeywordArgument("via").getConstantValue().isStringOrSymbol(via)
|
via = "all" and result = anyHttpMethod()
or
via != "all" and result = via
)
or
result =
method
.getKeywordArgument("via")
.(ArrayLiteral)
.getElement(_)
.getConstantValue()
.getStringOrSymbol()
}
override string getAction() {
result = extractAction(method.getKeywordArgument("to").getConstantValue().getStringOrSymbol()) or
method.getKeywordArgument("action").getConstantValue().isStringOrSymbol(result) or
result =
extractAction(method.getArgument(0).(Pair).getValue().getConstantValue().getStringOrSymbol())
}
}
private import Cached
/**
* This module contains the IPA types backing `RouteBlock` and `Route`, cached for performance.
*/
cached
private module Cached {
cached
newtype TRouteBlock =
TTopLevelRouteBlock(MethodCall routes, MethodCall draw, Block b) {
routes.getMethodName() = "routes" and
draw.getMethodName() = "draw" and
draw.getReceiver() = routes and
draw.getBlock() = b
} or
// constraints(foo: /some_regex/) do
// get "/posts/:foo", to "posts#something"
// end
TConstraintsRouteBlock(RouteBlock parent, MethodCall constraints, Block b) {
parent.getAStmt() = constraints and
constraints.getMethodName() = "constraints" and
constraints.getBlock() = b
} or
// scope(path: "/some_path", module: "some_module") do
// get "/posts/:foo", to "posts#something"
// end
TScopeRouteBlock(RouteBlock parent, MethodCall scope, Block b) {
parent.getAStmt() = scope and scope.getMethodName() = "scope" and scope.getBlock() = b
} or
// resources :articles do
// get "/comments", to "comments#index"
// end
TResourcesRouteBlock(RouteBlock parent, MethodCall resources, Block b) {
parent.getAStmt() = resources and
resources.getMethodName() = "resources" and
resources.getBlock() = b
} or
// A conditional statement guarding some routes.
// We ignore the condition and analyze both branches to obtain as
// much routing information as possible.
TConditionalRouteBlock(RouteBlock parent, ConditionalExpr e) { parent.getAStmt() = e } or
// namespace :admin do
// resources :posts
// end
TNamespaceRouteBlock(RouteBlock parent, MethodCall namespace, Block b) {
parent.getAStmt() = namespace and
namespace.getMethodName() = "namespace" and
namespace.getBlock() = b
}
/**
* A route configuration. See `Route` for more info
*/
cached
newtype TRoute =
/**
* See `ExplicitRoute`
*/
TExplicitRoute(RouteBlock b, MethodCall m) {
b.getAStmt() = m and m.getMethodName() = anyHttpMethod()
} or
/**
* See `ResourcesRoute`
*/
TResourcesRoute(RouteBlock b, MethodCall m, string action) {
b.getAStmt() = m and
m.getMethodName() = "resources" and
action in ["show", "index", "new", "edit", "create", "update", "destroy"] and
applyActionFilters(m, action)
} or
/**
* See `SingularResourceRoute`
*/
TResourceRoute(RouteBlock b, MethodCall m, string action) {
b.getAStmt() = m and
m.getMethodName() = "resource" and
action in ["show", "new", "edit", "create", "update", "destroy"] and
applyActionFilters(m, action)
} or
/**
* See `MatchRoute`
*/
TMatchRoute(RouteBlock b, MethodCall m) { b.getAStmt() = m and m.getMethodName() = "match" }
}
/**
* Several routing methods support the keyword arguments `only:` and `except:`.
* - `only:` restricts the set of actions to just those in the argument.
* - `except:` removes the given actions from the set.
*/
bindingset[action]
private predicate applyActionFilters(MethodCall m, string action) {
// Respect the `only` keyword argument, which restricts the set of actions.
(
not exists(m.getKeywordArgument("only"))
or
exists(Expr only | only = m.getKeywordArgument("only") |
[only.(ArrayLiteral).getElement(_), only].getConstantValue().isStringOrSymbol(action)
)
) and
// Respect the `except` keyword argument, which removes actions from the default set.
(
not exists(m.getKeywordArgument("except"))
or
exists(Expr except | except = m.getKeywordArgument("except") |
[except.(ArrayLiteral).getElement(_), except].getConstantValue().getStringOrSymbol() !=
action
)
)
}
/**
* Holds if the (resource, method, path, action) combination would be generated by a call to `resources :<resource>`.
*/
bindingset[resource]
private predicate isDefaultResourceRoute(
string resource, string method, string path, string action
) {
action = "create" and
(method = "post" and path = "/" + resource)
or
action = "index" and
(method = "get" and path = "/" + resource)
or
action = "new" and
(method = "get" and path = "/" + resource + "/new")
or
action = "edit" and
(method = "get" and path = "/" + resource + ":id/edit")
or
action = "show" and
(method = "get" and path = "/" + resource + "/:id")
or
action = "update" and
(method in ["put", "patch"] and path = "/" + resource + "/:id")
or
action = "destroy" and
(method = "delete" and path = "/" + resource + "/:id")
}
/**
* Holds if the (resource, method, path, action) combination would be generated by a call to `resource :<resource>`.
*/
bindingset[resource]
private predicate isDefaultSingularResourceRoute(
string resource, string method, string path, string action
) {
action = "create" and
(method = "post" and path = "/" + resource)
or
action = "new" and
(method = "get" and path = "/" + resource + "/new")
or
action = "edit" and
(method = "get" and path = "/" + resource + "/edit")
or
action = "show" and
(method = "get" and path = "/" + resource)
or
action = "update" and
(method in ["put", "patch"] and path = "/" + resource)
or
action = "destroy" and
(method = "delete" and path = "/" + resource)
}
/**
* Extract the controller from a Rails routing string
* ```
* extractController("posts#show") = "posts"
* ```
*/
bindingset[input]
private string extractController(string input) { result = input.regexpCapture("([^#]+)#.+", 1) }
/**
* Extract the action from a Rails routing string
* ```
* extractAction("posts#show") = "show"
*/
bindingset[input]
private string extractAction(string input) { result = input.regexpCapture("[^#]+#(.+)", 1) }
/**
* Returns the lowercase name of every HTTP method we support.
*/
private string anyHttpMethod() { result = ["get", "post", "put", "patch", "delete"] }
/**
* The inverse of `pluralize`
* photos => photo
* stories => story
* not_plural => not_plural
*/
bindingset[input]
private string singularize(string input) {
exists(string prefix | prefix = input.regexpCapture("(.*)ies", 1) | result = prefix + "y")
or
not input.matches("%ies") and
exists(string prefix | prefix = input.regexpCapture("(.*)s", 1) | result = prefix)
or
not input.regexpMatch(".*(ies|s)") and result = input
}
/**
* Convert a camel-case string to underscore case. Converts `::` to `/`.
* This can be used to convert ActiveRecord controller names to a canonical form that matches the routes they handle.
* Note: All-uppercase words like `CONSTANT` are not handled correctly.
*/
bindingset[base]
string underscore(string base) {
base = "" and result = ""
or
result =
base.charAt(0).toLowerCase() +
// Walk along the string, keeping track of the previous character
// in order to determine if we've crossed a boundary.
// Boundaries are:
// - lower case to upper case: B in FooBar
// - entering a namespace: B in Foo::Bar
concat(int i, string prev, string char |
prev = base.charAt(i) and
char = base.charAt(i + 1)
|
any(string s |
char.regexpMatch("[A-Za-z0-9]") and
if prev = ":"
then s = "/" + char.toLowerCase()
else
if prev.isLowercase() and char.isUppercase()
then s = "_" + char.toLowerCase()
else s = char.toLowerCase()
)
order by
i
)
}
/**
* Strip leading and trailing forward slashes from the string.
*/
bindingset[input]
private string stripSlashes(string input) {
result = input.regexpReplaceAll("^/+(.+)$", "$1").regexpReplaceAll("^(.*[^/])/+$", "$1")
}
}

View File

@@ -7,20 +7,6 @@ private import codeql.ruby.ast.internal.Module
private import codeql.ruby.ApiGraphs
private import codeql.ruby.frameworks.StandardLibrary
private class ActiveRecordBaseAccess extends ConstantReadAccess {
ActiveRecordBaseAccess() {
this.getName() = "Base" and
this.getScopeExpr().(ConstantAccess).getName() = "ActiveRecord"
}
}
// ApplicationRecord extends ActiveRecord::Base, but we
// treat it separately in case the ApplicationRecord definition
// is not in the database
private class ApplicationRecordAccess extends ConstantReadAccess {
ApplicationRecordAccess() { this.getName() = "ApplicationRecord" }
}
/// See https://api.rubyonrails.org/classes/ActiveRecord/Persistence.html
private string activeRecordPersistenceInstanceMethodName() {
result =
@@ -41,26 +27,28 @@ private predicate isBuiltInMethodForActiveRecordModelInstance(string methodName)
}
/**
* A `ClassDeclaration` for a class that extends `ActiveRecord::Base`. For example,
* A `ClassDeclaration` for a class that inherits from `ActiveRecord::Base`. For example,
*
* ```rb
* class UserGroup < ActiveRecord::Base
* has_many :users
* end
*
* class SpecialUserGroup < UserGroup
* end
* ```
*/
class ActiveRecordModelClass extends ClassDeclaration {
ActiveRecordModelClass() {
// class Foo < ActiveRecord::Base
this.getSuperclassExpr() instanceof ActiveRecordBaseAccess
or
// class Foo < ApplicationRecord
this.getSuperclassExpr() instanceof ApplicationRecordAccess
or
// class Bar < Foo
exists(ActiveRecordModelClass other |
other.getModule() = resolveConstantReadAccess(this.getSuperclassExpr())
)
this.getSuperclassExpr() =
[
API::getTopLevelMember("ActiveRecord").getMember("Base"),
// In Rails applications `ApplicationRecord` typically extends `ActiveRecord::Base`, but we
// treat it separately in case the `ApplicationRecord` definition is not in the database.
API::getTopLevelMember("ApplicationRecord")
].getASubclass().getAUse().asExpr().getExpr()
}
// Gets the class declaration for this class and all of its super classes

View File

@@ -0,0 +1,37 @@
/**
* Modeling for `ActiveSupport`, which is a utility gem that ships with Rails.
* https://rubygems.org/gems/activesupport
*/
import codeql.ruby.Concepts
import codeql.ruby.DataFlow
import codeql.ruby.frameworks.StandardLibrary
/**
* Modeling for `ActiveSupport`.
*/
module ActiveSupport {
/**
* Extensions to core classes
*/
module CoreExtensions {
/**
* Extensions to the `String` class
*/
module String {
/**
* A call to `String#constantize`, which tries to find a declared constant with the given name.
* Passing user input to this method may result in instantiation of arbitrary Ruby classes.
*/
class Constantize extends CodeExecution::Range, DataFlow::CallNode {
// We treat this an `UnknownMethodCall` in order to match every call to `constantize` that isn't overridden.
// We can't (yet) rely on API Graphs or dataflow to tell us that the receiver is a String.
Constantize() {
this.asExpr().getExpr().(UnknownMethodCall).getMethodName() = "constantize"
}
override DataFlow::Node getCode() { result = this.getReceiver() }
}
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -212,6 +212,15 @@ module File {
FileInstance() { this = fileInstance() }
}
/**
* A call to `File.open`, considered as a `FileSystemAccess`.
*/
class FileOpen extends DataFlow::CallNode, FileSystemAccess::Range {
FileOpen() { this = API::getTopLevelMember("File").getAMethodCall("open") }
override DataFlow::Node getAPathArgument() { result = this.getArgument(0) }
}
/**
* A read using the `File` module, e.g. the `f.read` call in
*

View File

@@ -10,44 +10,7 @@ private import codeql.ruby.dataflow.RemoteFlowSources
private import codeql.ruby.ast.internal.Module
private import codeql.ruby.ApiGraphs
private class GraphqlRelayClassicMutationAccess extends ConstantReadAccess {
//GraphQL::Schema::RelayClassicMutation
GraphqlRelayClassicMutationAccess() {
this =
API::getTopLevelMember("GraphQL")
.getMember("Schema")
.getMember("RelayClassicMutation")
.getAUse()
.asExpr()
.getExpr()
}
}
private class GraphqlSchemaResolverAccess extends ConstantReadAccess {
//GraphQL::Schema::Resolver
GraphqlSchemaResolverAccess() {
this =
API::getTopLevelMember("GraphQL")
.getMember("Schema")
.getMember("Resolver")
.getAUse()
.asExpr()
.getExpr()
}
}
private class GraphqlSchemaObjectAccess extends ConstantReadAccess {
//GraphQL::Schema::Object
GraphqlSchemaObjectAccess() {
this =
API::getTopLevelMember("GraphQL")
.getMember("Schema")
.getMember("Object")
.getAUse()
.asExpr()
.getExpr()
}
}
private API::Node graphQlSchema() { result = API::getTopLevelMember("GraphQL").getMember("Schema") }
/**
* A `ClassDeclaration` for a class that extends `GraphQL::Schema::RelayClassicMutation`.
@@ -77,13 +40,8 @@ private class GraphqlSchemaObjectAccess extends ConstantReadAccess {
*/
private class GraphqlRelayClassicMutationClass extends ClassDeclaration {
GraphqlRelayClassicMutationClass() {
// class BaseMutation < GraphQL::Schema::RelayClassicMutation
this.getSuperclassExpr() instanceof GraphqlRelayClassicMutationAccess
or
// class MyMutation < BaseMutation
exists(GraphqlRelayClassicMutationClass other |
other.getModule() = resolveConstantReadAccess(this.getSuperclassExpr())
)
this.getSuperclassExpr() =
graphQlSchema().getMember("RelayClassicMutation").getASubclass*().getAUse().asExpr().getExpr()
}
}
@@ -112,13 +70,8 @@ private class GraphqlRelayClassicMutationClass extends ClassDeclaration {
*/
private class GraphqlSchemaResolverClass extends ClassDeclaration {
GraphqlSchemaResolverClass() {
// class BaseResolver < GraphQL::Schema::Resolver
this.getSuperclassExpr() instanceof GraphqlSchemaResolverAccess
or
// class MyResolver < BaseResolver
exists(GraphqlSchemaResolverClass other |
other.getModule() = resolveConstantReadAccess(this.getSuperclassExpr())
)
this.getSuperclassExpr() =
graphQlSchema().getMember("Resolver").getASubclass().getAUse().asExpr().getExpr()
}
}
@@ -138,13 +91,8 @@ private class GraphqlSchemaResolverClass extends ClassDeclaration {
*/
class GraphqlSchemaObjectClass extends ClassDeclaration {
GraphqlSchemaObjectClass() {
// class BaseObject < GraphQL::Schema::Object
this.getSuperclassExpr() instanceof GraphqlSchemaObjectAccess
or
// class MyObject < BaseObject
exists(GraphqlSchemaObjectClass other |
other.getModule() = resolveConstantReadAccess(this.getSuperclassExpr())
)
this.getSuperclassExpr() =
graphQlSchema().getMember("Object").getASubclass().getAUse().asExpr().getExpr()
}
/** Gets a `GraphqlFieldDefinitionMethodCall` called in this class. */

File diff suppressed because it is too large Load Diff

View File

@@ -19,8 +19,10 @@ class OpenUriRequest extends HTTP::Client::Request::Range {
OpenUriRequest() {
requestNode =
[API::getTopLevelMember("URI"), API::getTopLevelMember("URI").getReturn("parse")]
.getReturn("open") and
[
[API::getTopLevelMember("URI"), API::getTopLevelMember("URI").getReturn("parse")]
.getReturn("open"), API::getTopLevelMember("OpenURI").getReturn("open_uri")
] and
requestUse = requestNode.getAnImmediateUse() and
this = requestUse.asExpr().getExpr()
}

View File

@@ -46,6 +46,6 @@ module ServerSideRequestForgery {
HttpRequestAsSink() { exists(HTTP::Client::Request req | req.getURL() = this) }
}
/** String interpolation with a fixed prefix, considered as a flow sanitizer. */
/** A string interpolation with a fixed prefix, considered as a flow sanitizer. */
class StringInterpolationAsSanitizer extends PrefixedStringInterpolation, Sanitizer { }
}

View File

@@ -10,6 +10,7 @@ private import codeql.ruby.Concepts
private import codeql.ruby.dataflow.RemoteFlowSources
private import codeql.ruby.dataflow.BarrierGuards
private import codeql.ruby.dataflow.Sanitizers
private import codeql.ruby.frameworks.ActionController
/**
* Provides default sources, sinks and sanitizers for detecting
@@ -54,15 +55,21 @@ module UrlRedirect {
*/
class RedirectLocationAsSink extends Sink {
RedirectLocationAsSink() {
exists(HTTP::Server::HttpRedirectResponse e |
exists(HTTP::Server::HttpRedirectResponse e, MethodBase method |
this = e.getRedirectLocation() and
// As a rough heuristic, assume that methods with these names are handlers for POST/PUT/PATCH/DELETE requests,
// which are not as vulnerable to URL redirection because browsers will not initiate them from clicking a link.
not this.asExpr()
.getExpr()
.getEnclosingMethod()
.getName()
.regexpMatch(".*(create|update|destroy).*")
// We only want handlers for GET requests.
// Handlers for other HTTP methods are not as vulnerable to URL
// redirection as browsers will not initiate them from clicking a link.
method = this.asExpr().getExpr().getEnclosingMethod() and
(
// If there's a Rails GET route to this handler, we can be certain that it is a candidate.
method.(ActionControllerActionMethod).getARoute().getHttpMethod() = "get"
or
// Otherwise, we have to rely on a heuristic to filter out invulnerable handlers.
// We exclude any handlers with names containing create/update/destroy, as these are not likely to handle GET requests.
not exists(method.(ActionControllerActionMethod).getARoute()) and
not method.getName().regexpMatch(".*(create|update|destroy).*")
)
)
}
}

View File

@@ -1052,13 +1052,13 @@ private module SuffixConstruction {
*/
pragma[noinline]
private string relevant(RegExpRoot root) {
exists(ascii(result))
exists(ascii(result)) and exists(root)
or
exists(InputSymbol s | belongsTo(s, root) | result = intersect(s, _))
or
// The characters from `hasSimpleRejectEdge`. Only `\n` is really needed (as `\n` is not in the `ascii` relation).
// The three chars must be kept in sync with `hasSimpleRejectEdge`.
result = ["|", "\n", "Z"]
result = ["|", "\n", "Z"] and exists(root)
}
/**

View File

@@ -3,7 +3,7 @@
private import TypeTrackerSpecific
/**
* Any string that may appear as the name of a piece of content. This will usually include things like:
* A string that may appear as the name of a piece of content. This will usually include things like:
* - Attribute names (in Python)
* - Property names (in JavaScript)
*
@@ -18,7 +18,7 @@ class ContentName extends string {
ContentName() { this = getPossibleContentName() }
}
/** Either a content name, or the empty string (representing no content). */
/** A content name, or the empty string (representing no content). */
class OptionalContentName extends string {
OptionalContentName() { this instanceof ContentName or this = "" }
}
@@ -200,7 +200,7 @@ module StepSummary {
private newtype TTypeTracker = MkTypeTracker(Boolean hasCall, OptionalContentName content)
/**
* Summary of the steps needed to track a value to a given dataflow node.
* A summary of the steps needed to track a value to a given dataflow node.
*
* This can be used to track objects that implement a certain API in order to
* recognize calls to that API. Note that type-tracking does not by itself provide a
@@ -347,7 +347,7 @@ module TypeTracker {
private newtype TTypeBackTracker = MkTypeBackTracker(Boolean hasReturn, OptionalContentName content)
/**
* Summary of the steps needed to back-track a use of a value to a given dataflow node.
* A summary of the steps needed to back-track a use of a value to a given dataflow node.
*
* This can for example be used to track callbacks that are passed to a certain API,
* so we can model specific parameters of that callback as having a certain type.

View File

@@ -1,5 +1,5 @@
name: codeql/ruby-all
version: 0.0.8-dev
version: 0.0.10-dev
groups: ruby
extractor: ruby
dbscheme: ruby.dbscheme

View File

@@ -56,13 +56,15 @@ case @diagnostic.severity of
@ruby_underscore_lhs = @ruby_call | @ruby_element_reference | @ruby_scope_resolution | @ruby_token_false | @ruby_token_nil | @ruby_token_true | @ruby_underscore_variable
@ruby_underscore_method_name = @ruby_delimited_symbol | @ruby_setter | @ruby_token_class_variable | @ruby_token_constant | @ruby_token_global_variable | @ruby_token_identifier | @ruby_token_instance_variable | @ruby_token_operator | @ruby_token_simple_symbol
@ruby_underscore_method_name = @ruby_delimited_symbol | @ruby_setter | @ruby_token_constant | @ruby_token_identifier | @ruby_token_operator | @ruby_token_simple_symbol | @ruby_underscore_nonlocal_variable
@ruby_underscore_nonlocal_variable = @ruby_token_class_variable | @ruby_token_global_variable | @ruby_token_instance_variable
@ruby_underscore_pattern_constant = @ruby_scope_resolution | @ruby_token_constant
@ruby_underscore_pattern_expr = @ruby_alternative_pattern | @ruby_as_pattern | @ruby_underscore_pattern_expr_basic
@ruby_underscore_pattern_expr_basic = @ruby_array_pattern | @ruby_find_pattern | @ruby_hash_pattern | @ruby_parenthesized_pattern | @ruby_range | @ruby_token_identifier | @ruby_underscore_pattern_constant | @ruby_underscore_pattern_primitive | @ruby_variable_reference_pattern
@ruby_underscore_pattern_expr_basic = @ruby_array_pattern | @ruby_expression_reference_pattern | @ruby_find_pattern | @ruby_hash_pattern | @ruby_parenthesized_pattern | @ruby_range | @ruby_token_identifier | @ruby_underscore_pattern_constant | @ruby_underscore_pattern_primitive | @ruby_variable_reference_pattern
@ruby_underscore_pattern_primitive = @ruby_delimited_symbol | @ruby_lambda | @ruby_regex | @ruby_string__ | @ruby_string_array | @ruby_symbol_array | @ruby_token_encoding | @ruby_token_false | @ruby_token_file | @ruby_token_line | @ruby_token_nil | @ruby_token_self | @ruby_token_simple_symbol | @ruby_token_true | @ruby_unary | @ruby_underscore_simple_numeric
@@ -74,13 +76,12 @@ case @diagnostic.severity of
@ruby_underscore_statement = @ruby_alias | @ruby_begin_block | @ruby_end_block | @ruby_if_modifier | @ruby_rescue_modifier | @ruby_undef | @ruby_underscore_expression | @ruby_unless_modifier | @ruby_until_modifier | @ruby_while_modifier
@ruby_underscore_variable = @ruby_token_class_variable | @ruby_token_constant | @ruby_token_global_variable | @ruby_token_identifier | @ruby_token_instance_variable | @ruby_token_self | @ruby_token_super
@ruby_underscore_variable = @ruby_token_constant | @ruby_token_identifier | @ruby_token_self | @ruby_token_super | @ruby_underscore_nonlocal_variable
ruby_alias_def(
unique int id: @ruby_alias,
int alias: @ruby_underscore_method_name ref,
int name: @ruby_underscore_method_name ref,
int loc: @location ref
int name: @ruby_underscore_method_name ref
);
#keyset[ruby_alternative_pattern, index]
@@ -91,8 +92,7 @@ ruby_alternative_pattern_alternatives(
);
ruby_alternative_pattern_def(
unique int id: @ruby_alternative_pattern,
int loc: @location ref
unique int id: @ruby_alternative_pattern
);
@ruby_argument_list_child_type = @ruby_block_argument | @ruby_hash_splat_argument | @ruby_pair | @ruby_splat_argument | @ruby_token_forward_argument | @ruby_underscore_expression
@@ -105,8 +105,7 @@ ruby_argument_list_child(
);
ruby_argument_list_def(
unique int id: @ruby_argument_list,
int loc: @location ref
unique int id: @ruby_argument_list
);
@ruby_array_child_type = @ruby_block_argument | @ruby_hash_splat_argument | @ruby_pair | @ruby_splat_argument | @ruby_token_forward_argument | @ruby_underscore_expression
@@ -119,8 +118,7 @@ ruby_array_child(
);
ruby_array_def(
unique int id: @ruby_array,
int loc: @location ref
unique int id: @ruby_array
);
ruby_array_pattern_class(
@@ -138,15 +136,13 @@ ruby_array_pattern_child(
);
ruby_array_pattern_def(
unique int id: @ruby_array_pattern,
int loc: @location ref
unique int id: @ruby_array_pattern
);
ruby_as_pattern_def(
unique int id: @ruby_as_pattern,
int name: @ruby_token_identifier ref,
int value: @ruby_underscore_pattern_expr ref,
int loc: @location ref
int value: @ruby_underscore_pattern_expr ref
);
@ruby_assignment_left_type = @ruby_left_assignment_list | @ruby_underscore_lhs
@@ -156,8 +152,7 @@ ruby_as_pattern_def(
ruby_assignment_def(
unique int id: @ruby_assignment,
int left: @ruby_assignment_left_type ref,
int right: @ruby_assignment_right_type ref,
int loc: @location ref
int right: @ruby_assignment_right_type ref
);
@ruby_bare_string_child_type = @ruby_interpolation | @ruby_token_escape_sequence | @ruby_token_string_content
@@ -170,8 +165,7 @@ ruby_bare_string_child(
);
ruby_bare_string_def(
unique int id: @ruby_bare_string,
int loc: @location ref
unique int id: @ruby_bare_string
);
@ruby_bare_symbol_child_type = @ruby_interpolation | @ruby_token_escape_sequence | @ruby_token_string_content
@@ -184,8 +178,7 @@ ruby_bare_symbol_child(
);
ruby_bare_symbol_def(
unique int id: @ruby_bare_symbol,
int loc: @location ref
unique int id: @ruby_bare_symbol
);
@ruby_begin_child_type = @ruby_else | @ruby_ensure | @ruby_rescue | @ruby_token_empty_statement | @ruby_underscore_statement
@@ -198,8 +191,7 @@ ruby_begin_child(
);
ruby_begin_def(
unique int id: @ruby_begin,
int loc: @location ref
unique int id: @ruby_begin
);
@ruby_begin_block_child_type = @ruby_token_empty_statement | @ruby_underscore_statement
@@ -212,8 +204,7 @@ ruby_begin_block_child(
);
ruby_begin_block_def(
unique int id: @ruby_begin_block,
int loc: @location ref
unique int id: @ruby_begin_block
);
case @ruby_binary.operator of
@@ -249,8 +240,7 @@ ruby_binary_def(
unique int id: @ruby_binary,
int left: @ruby_underscore_expression ref,
int operator: int ref,
int right: @ruby_underscore_expression ref,
int loc: @location ref
int right: @ruby_underscore_expression ref
);
ruby_block_parameters(
@@ -268,20 +258,25 @@ ruby_block_child(
);
ruby_block_def(
unique int id: @ruby_block,
int loc: @location ref
unique int id: @ruby_block
);
ruby_block_argument_child(
unique int ruby_block_argument: @ruby_block_argument ref,
unique int child: @ruby_underscore_arg ref
);
ruby_block_argument_def(
unique int id: @ruby_block_argument,
int child: @ruby_underscore_arg ref,
int loc: @location ref
unique int id: @ruby_block_argument
);
ruby_block_parameter_name(
unique int ruby_block_parameter: @ruby_block_parameter ref,
unique int name: @ruby_token_identifier ref
);
ruby_block_parameter_def(
unique int id: @ruby_block_parameter,
int name: @ruby_token_identifier ref,
int loc: @location ref
unique int id: @ruby_block_parameter
);
@ruby_block_parameters_child_type = @ruby_block_parameter | @ruby_destructured_parameter | @ruby_hash_splat_parameter | @ruby_keyword_parameter | @ruby_optional_parameter | @ruby_splat_parameter | @ruby_token_forward_parameter | @ruby_token_hash_splat_nil | @ruby_token_identifier
@@ -294,8 +289,7 @@ ruby_block_parameters_child(
);
ruby_block_parameters_def(
unique int id: @ruby_block_parameters,
int loc: @location ref
unique int id: @ruby_block_parameters
);
ruby_break_child(
@@ -304,8 +298,7 @@ ruby_break_child(
);
ruby_break_def(
unique int id: @ruby_break,
int loc: @location ref
unique int id: @ruby_break
);
ruby_call_arguments(
@@ -331,8 +324,7 @@ ruby_call_receiver(
ruby_call_def(
unique int id: @ruby_call,
int method: @ruby_call_method_type ref,
int loc: @location ref
int method: @ruby_call_method_type ref
);
ruby_case_value(
@@ -350,8 +342,7 @@ ruby_case_child(
);
ruby_case_def(
unique int id: @ruby_case__,
int loc: @location ref
unique int id: @ruby_case__
);
#keyset[ruby_case_match, index]
@@ -368,8 +359,7 @@ ruby_case_match_else(
ruby_case_match_def(
unique int id: @ruby_case_match,
int value: @ruby_underscore_statement ref,
int loc: @location ref
int value: @ruby_underscore_statement ref
);
#keyset[ruby_chained_string, index]
@@ -380,8 +370,7 @@ ruby_chained_string_child(
);
ruby_chained_string_def(
unique int id: @ruby_chained_string,
int loc: @location ref
unique int id: @ruby_chained_string
);
@ruby_class_name_type = @ruby_scope_resolution | @ruby_token_constant
@@ -402,16 +391,14 @@ ruby_class_child(
ruby_class_def(
unique int id: @ruby_class,
int name: @ruby_class_name_type ref,
int loc: @location ref
int name: @ruby_class_name_type ref
);
ruby_conditional_def(
unique int id: @ruby_conditional,
int alternative: @ruby_underscore_arg ref,
int condition: @ruby_underscore_arg ref,
int consequence: @ruby_underscore_arg ref,
int loc: @location ref
int consequence: @ruby_underscore_arg ref
);
@ruby_delimited_symbol_child_type = @ruby_interpolation | @ruby_token_escape_sequence | @ruby_token_string_content
@@ -424,8 +411,7 @@ ruby_delimited_symbol_child(
);
ruby_delimited_symbol_def(
unique int id: @ruby_delimited_symbol,
int loc: @location ref
unique int id: @ruby_delimited_symbol
);
@ruby_destructured_left_assignment_child_type = @ruby_destructured_left_assignment | @ruby_rest_assignment | @ruby_underscore_lhs
@@ -438,8 +424,7 @@ ruby_destructured_left_assignment_child(
);
ruby_destructured_left_assignment_def(
unique int id: @ruby_destructured_left_assignment,
int loc: @location ref
unique int id: @ruby_destructured_left_assignment
);
@ruby_destructured_parameter_child_type = @ruby_block_parameter | @ruby_destructured_parameter | @ruby_hash_splat_parameter | @ruby_keyword_parameter | @ruby_optional_parameter | @ruby_splat_parameter | @ruby_token_forward_parameter | @ruby_token_hash_splat_nil | @ruby_token_identifier
@@ -452,8 +437,7 @@ ruby_destructured_parameter_child(
);
ruby_destructured_parameter_def(
unique int id: @ruby_destructured_parameter,
int loc: @location ref
unique int id: @ruby_destructured_parameter
);
@ruby_do_child_type = @ruby_token_empty_statement | @ruby_underscore_statement
@@ -466,8 +450,7 @@ ruby_do_child(
);
ruby_do_def(
unique int id: @ruby_do,
int loc: @location ref
unique int id: @ruby_do
);
ruby_do_block_parameters(
@@ -485,8 +468,7 @@ ruby_do_block_child(
);
ruby_do_block_def(
unique int id: @ruby_do_block,
int loc: @location ref
unique int id: @ruby_do_block
);
@ruby_element_reference_child_type = @ruby_block_argument | @ruby_hash_splat_argument | @ruby_pair | @ruby_splat_argument | @ruby_token_forward_argument | @ruby_underscore_expression
@@ -500,8 +482,7 @@ ruby_element_reference_child(
ruby_element_reference_def(
unique int id: @ruby_element_reference,
int object: @ruby_underscore_primary ref,
int loc: @location ref
int object: @ruby_underscore_primary ref
);
@ruby_else_child_type = @ruby_token_empty_statement | @ruby_underscore_statement
@@ -514,8 +495,7 @@ ruby_else_child(
);
ruby_else_def(
unique int id: @ruby_else,
int loc: @location ref
unique int id: @ruby_else
);
@ruby_elsif_alternative_type = @ruby_else | @ruby_elsif
@@ -532,8 +512,7 @@ ruby_elsif_consequence(
ruby_elsif_def(
unique int id: @ruby_elsif,
int condition: @ruby_underscore_statement ref,
int loc: @location ref
int condition: @ruby_underscore_statement ref
);
@ruby_end_block_child_type = @ruby_token_empty_statement | @ruby_underscore_statement
@@ -546,8 +525,7 @@ ruby_end_block_child(
);
ruby_end_block_def(
unique int id: @ruby_end_block,
int loc: @location ref
unique int id: @ruby_end_block
);
@ruby_ensure_child_type = @ruby_token_empty_statement | @ruby_underscore_statement
@@ -560,14 +538,12 @@ ruby_ensure_child(
);
ruby_ensure_def(
unique int id: @ruby_ensure,
int loc: @location ref
unique int id: @ruby_ensure
);
ruby_exception_variable_def(
unique int id: @ruby_exception_variable,
int child: @ruby_underscore_lhs ref,
int loc: @location ref
int child: @ruby_underscore_lhs ref
);
@ruby_exceptions_child_type = @ruby_splat_argument | @ruby_underscore_arg
@@ -580,8 +556,12 @@ ruby_exceptions_child(
);
ruby_exceptions_def(
unique int id: @ruby_exceptions,
int loc: @location ref
unique int id: @ruby_exceptions
);
ruby_expression_reference_pattern_def(
unique int id: @ruby_expression_reference_pattern,
int value: @ruby_underscore_expression ref
);
ruby_find_pattern_class(
@@ -599,8 +579,7 @@ ruby_find_pattern_child(
);
ruby_find_pattern_def(
unique int id: @ruby_find_pattern,
int loc: @location ref
unique int id: @ruby_find_pattern
);
@ruby_for_pattern_type = @ruby_left_assignment_list | @ruby_underscore_lhs
@@ -609,8 +588,7 @@ ruby_for_def(
unique int id: @ruby_for,
int body: @ruby_do ref,
int pattern: @ruby_for_pattern_type ref,
int value: @ruby_in ref,
int loc: @location ref
int value: @ruby_in ref
);
@ruby_hash_child_type = @ruby_hash_splat_argument | @ruby_pair
@@ -623,8 +601,7 @@ ruby_hash_child(
);
ruby_hash_def(
unique int id: @ruby_hash,
int loc: @location ref
unique int id: @ruby_hash
);
ruby_hash_pattern_class(
@@ -642,14 +619,12 @@ ruby_hash_pattern_child(
);
ruby_hash_pattern_def(
unique int id: @ruby_hash_pattern,
int loc: @location ref
unique int id: @ruby_hash_pattern
);
ruby_hash_splat_argument_def(
unique int id: @ruby_hash_splat_argument,
int child: @ruby_underscore_arg ref,
int loc: @location ref
int child: @ruby_underscore_arg ref
);
ruby_hash_splat_parameter_name(
@@ -658,8 +633,7 @@ ruby_hash_splat_parameter_name(
);
ruby_hash_splat_parameter_def(
unique int id: @ruby_hash_splat_parameter,
int loc: @location ref
unique int id: @ruby_hash_splat_parameter
);
@ruby_heredoc_body_child_type = @ruby_interpolation | @ruby_token_escape_sequence | @ruby_token_heredoc_content | @ruby_token_heredoc_end
@@ -672,8 +646,7 @@ ruby_heredoc_body_child(
);
ruby_heredoc_body_def(
unique int id: @ruby_heredoc_body,
int loc: @location ref
unique int id: @ruby_heredoc_body
);
@ruby_if_alternative_type = @ruby_else | @ruby_elsif
@@ -690,27 +663,23 @@ ruby_if_consequence(
ruby_if_def(
unique int id: @ruby_if,
int condition: @ruby_underscore_statement ref,
int loc: @location ref
int condition: @ruby_underscore_statement ref
);
ruby_if_guard_def(
unique int id: @ruby_if_guard,
int condition: @ruby_underscore_expression ref,
int loc: @location ref
int condition: @ruby_underscore_expression ref
);
ruby_if_modifier_def(
unique int id: @ruby_if_modifier,
int body: @ruby_underscore_statement ref,
int condition: @ruby_underscore_expression ref,
int loc: @location ref
int condition: @ruby_underscore_expression ref
);
ruby_in_def(
unique int id: @ruby_in,
int child: @ruby_underscore_arg ref,
int loc: @location ref
int child: @ruby_underscore_arg ref
);
ruby_in_clause_body(
@@ -727,8 +696,7 @@ ruby_in_clause_guard(
ruby_in_clause_def(
unique int id: @ruby_in_clause,
int pattern: @ruby_underscore_pattern_top_expr_body ref,
int loc: @location ref
int pattern: @ruby_underscore_pattern_top_expr_body ref
);
@ruby_interpolation_child_type = @ruby_token_empty_statement | @ruby_underscore_statement
@@ -741,8 +709,7 @@ ruby_interpolation_child(
);
ruby_interpolation_def(
unique int id: @ruby_interpolation,
int loc: @location ref
unique int id: @ruby_interpolation
);
ruby_keyword_parameter_value(
@@ -752,8 +719,7 @@ ruby_keyword_parameter_value(
ruby_keyword_parameter_def(
unique int id: @ruby_keyword_parameter,
int name: @ruby_token_identifier ref,
int loc: @location ref
int name: @ruby_token_identifier ref
);
@ruby_keyword_pattern_key_type = @ruby_string__ | @ruby_token_hash_key_symbol
@@ -765,8 +731,7 @@ ruby_keyword_pattern_value(
ruby_keyword_pattern_def(
unique int id: @ruby_keyword_pattern,
int key__: @ruby_keyword_pattern_key_type ref,
int loc: @location ref
int key__: @ruby_keyword_pattern_key_type ref
);
@ruby_lambda_body_type = @ruby_block | @ruby_do_block
@@ -778,8 +743,7 @@ ruby_lambda_parameters(
ruby_lambda_def(
unique int id: @ruby_lambda,
int body: @ruby_lambda_body_type ref,
int loc: @location ref
int body: @ruby_lambda_body_type ref
);
@ruby_lambda_parameters_child_type = @ruby_block_parameter | @ruby_destructured_parameter | @ruby_hash_splat_parameter | @ruby_keyword_parameter | @ruby_optional_parameter | @ruby_splat_parameter | @ruby_token_forward_parameter | @ruby_token_hash_splat_nil | @ruby_token_identifier
@@ -792,8 +756,7 @@ ruby_lambda_parameters_child(
);
ruby_lambda_parameters_def(
unique int id: @ruby_lambda_parameters,
int loc: @location ref
unique int id: @ruby_lambda_parameters
);
@ruby_left_assignment_list_child_type = @ruby_destructured_left_assignment | @ruby_rest_assignment | @ruby_underscore_lhs
@@ -806,8 +769,7 @@ ruby_left_assignment_list_child(
);
ruby_left_assignment_list_def(
unique int id: @ruby_left_assignment_list,
int loc: @location ref
unique int id: @ruby_left_assignment_list
);
ruby_method_parameters(
@@ -826,8 +788,7 @@ ruby_method_child(
ruby_method_def(
unique int id: @ruby_method,
int name: @ruby_underscore_method_name ref,
int loc: @location ref
int name: @ruby_underscore_method_name ref
);
@ruby_method_parameters_child_type = @ruby_block_parameter | @ruby_destructured_parameter | @ruby_hash_splat_parameter | @ruby_keyword_parameter | @ruby_optional_parameter | @ruby_splat_parameter | @ruby_token_forward_parameter | @ruby_token_hash_splat_nil | @ruby_token_identifier
@@ -840,8 +801,7 @@ ruby_method_parameters_child(
);
ruby_method_parameters_def(
unique int id: @ruby_method_parameters,
int loc: @location ref
unique int id: @ruby_method_parameters
);
@ruby_module_name_type = @ruby_scope_resolution | @ruby_token_constant
@@ -857,8 +817,7 @@ ruby_module_child(
ruby_module_def(
unique int id: @ruby_module,
int name: @ruby_module_name_type ref,
int loc: @location ref
int name: @ruby_module_name_type ref
);
ruby_next_child(
@@ -867,8 +826,7 @@ ruby_next_child(
);
ruby_next_def(
unique int id: @ruby_next,
int loc: @location ref
unique int id: @ruby_next
);
case @ruby_operator_assignment.operator of
@@ -892,30 +850,30 @@ ruby_operator_assignment_def(
unique int id: @ruby_operator_assignment,
int left: @ruby_underscore_lhs ref,
int operator: int ref,
int right: @ruby_underscore_expression ref,
int loc: @location ref
int right: @ruby_underscore_expression ref
);
ruby_optional_parameter_def(
unique int id: @ruby_optional_parameter,
int name: @ruby_token_identifier ref,
int value: @ruby_underscore_arg ref,
int loc: @location ref
int value: @ruby_underscore_arg ref
);
@ruby_pair_key_type = @ruby_string__ | @ruby_token_hash_key_symbol | @ruby_underscore_arg
ruby_pair_value(
unique int ruby_pair: @ruby_pair ref,
unique int value: @ruby_underscore_arg ref
);
ruby_pair_def(
unique int id: @ruby_pair,
int key__: @ruby_pair_key_type ref,
int value: @ruby_underscore_arg ref,
int loc: @location ref
int key__: @ruby_pair_key_type ref
);
ruby_parenthesized_pattern_def(
unique int id: @ruby_parenthesized_pattern,
int child: @ruby_underscore_pattern_expr ref,
int loc: @location ref
int child: @ruby_underscore_pattern_expr ref
);
@ruby_parenthesized_statements_child_type = @ruby_token_empty_statement | @ruby_underscore_statement
@@ -928,16 +886,14 @@ ruby_parenthesized_statements_child(
);
ruby_parenthesized_statements_def(
unique int id: @ruby_parenthesized_statements,
int loc: @location ref
unique int id: @ruby_parenthesized_statements
);
@ruby_pattern_child_type = @ruby_splat_argument | @ruby_underscore_arg
ruby_pattern_def(
unique int id: @ruby_pattern,
int child: @ruby_pattern_child_type ref,
int loc: @location ref
int child: @ruby_pattern_child_type ref
);
@ruby_program_child_type = @ruby_token_empty_statement | @ruby_token_uninterpreted | @ruby_underscore_statement
@@ -950,8 +906,7 @@ ruby_program_child(
);
ruby_program_def(
unique int id: @ruby_program,
int loc: @location ref
unique int id: @ruby_program
);
@ruby_range_begin_type = @ruby_underscore_arg | @ruby_underscore_pattern_primitive
@@ -976,16 +931,14 @@ case @ruby_range.operator of
ruby_range_def(
unique int id: @ruby_range,
int operator: int ref,
int loc: @location ref
int operator: int ref
);
@ruby_rational_child_type = @ruby_token_float | @ruby_token_integer
ruby_rational_def(
unique int id: @ruby_rational,
int child: @ruby_rational_child_type ref,
int loc: @location ref
int child: @ruby_rational_child_type ref
);
ruby_redo_child(
@@ -994,8 +947,7 @@ ruby_redo_child(
);
ruby_redo_def(
unique int id: @ruby_redo,
int loc: @location ref
unique int id: @ruby_redo
);
@ruby_regex_child_type = @ruby_interpolation | @ruby_token_escape_sequence | @ruby_token_string_content
@@ -1008,8 +960,7 @@ ruby_regex_child(
);
ruby_regex_def(
unique int id: @ruby_regex,
int loc: @location ref
unique int id: @ruby_regex
);
ruby_rescue_body(
@@ -1028,8 +979,7 @@ ruby_rescue_variable(
);
ruby_rescue_def(
unique int id: @ruby_rescue,
int loc: @location ref
unique int id: @ruby_rescue
);
@ruby_rescue_modifier_body_type = @ruby_underscore_arg | @ruby_underscore_statement
@@ -1037,8 +987,7 @@ ruby_rescue_def(
ruby_rescue_modifier_def(
unique int id: @ruby_rescue_modifier,
int body: @ruby_rescue_modifier_body_type ref,
int handler: @ruby_underscore_expression ref,
int loc: @location ref
int handler: @ruby_underscore_expression ref
);
ruby_rest_assignment_child(
@@ -1047,8 +996,7 @@ ruby_rest_assignment_child(
);
ruby_rest_assignment_def(
unique int id: @ruby_rest_assignment,
int loc: @location ref
unique int id: @ruby_rest_assignment
);
ruby_retry_child(
@@ -1057,8 +1005,7 @@ ruby_retry_child(
);
ruby_retry_def(
unique int id: @ruby_retry,
int loc: @location ref
unique int id: @ruby_retry
);
ruby_return_child(
@@ -1067,8 +1014,7 @@ ruby_return_child(
);
ruby_return_def(
unique int id: @ruby_return,
int loc: @location ref
unique int id: @ruby_return
);
@ruby_right_assignment_list_child_type = @ruby_splat_argument | @ruby_underscore_arg
@@ -1081,8 +1027,7 @@ ruby_right_assignment_list_child(
);
ruby_right_assignment_list_def(
unique int id: @ruby_right_assignment_list,
int loc: @location ref
unique int id: @ruby_right_assignment_list
);
@ruby_scope_resolution_name_type = @ruby_token_constant | @ruby_token_identifier
@@ -1096,14 +1041,12 @@ ruby_scope_resolution_scope(
ruby_scope_resolution_def(
unique int id: @ruby_scope_resolution,
int name: @ruby_scope_resolution_name_type ref,
int loc: @location ref
int name: @ruby_scope_resolution_name_type ref
);
ruby_setter_def(
unique int id: @ruby_setter,
int name: @ruby_token_identifier ref,
int loc: @location ref
int name: @ruby_token_identifier ref
);
@ruby_singleton_class_child_type = @ruby_else | @ruby_ensure | @ruby_rescue | @ruby_token_empty_statement | @ruby_underscore_statement
@@ -1117,8 +1060,7 @@ ruby_singleton_class_child(
ruby_singleton_class_def(
unique int id: @ruby_singleton_class,
int value: @ruby_underscore_arg ref,
int loc: @location ref
int value: @ruby_underscore_arg ref
);
@ruby_singleton_method_object_type = @ruby_underscore_arg | @ruby_underscore_variable
@@ -1140,14 +1082,12 @@ ruby_singleton_method_child(
ruby_singleton_method_def(
unique int id: @ruby_singleton_method,
int name: @ruby_underscore_method_name ref,
int object: @ruby_singleton_method_object_type ref,
int loc: @location ref
int object: @ruby_singleton_method_object_type ref
);
ruby_splat_argument_def(
unique int id: @ruby_splat_argument,
int child: @ruby_underscore_arg ref,
int loc: @location ref
int child: @ruby_underscore_arg ref
);
ruby_splat_parameter_name(
@@ -1156,8 +1096,7 @@ ruby_splat_parameter_name(
);
ruby_splat_parameter_def(
unique int id: @ruby_splat_parameter,
int loc: @location ref
unique int id: @ruby_splat_parameter
);
@ruby_string_child_type = @ruby_interpolation | @ruby_token_escape_sequence | @ruby_token_string_content
@@ -1170,8 +1109,7 @@ ruby_string_child(
);
ruby_string_def(
unique int id: @ruby_string__,
int loc: @location ref
unique int id: @ruby_string__
);
#keyset[ruby_string_array, index]
@@ -1182,8 +1120,7 @@ ruby_string_array_child(
);
ruby_string_array_def(
unique int id: @ruby_string_array,
int loc: @location ref
unique int id: @ruby_string_array
);
@ruby_subshell_child_type = @ruby_interpolation | @ruby_token_escape_sequence | @ruby_token_string_content
@@ -1196,14 +1133,12 @@ ruby_subshell_child(
);
ruby_subshell_def(
unique int id: @ruby_subshell,
int loc: @location ref
unique int id: @ruby_subshell
);
ruby_superclass_def(
unique int id: @ruby_superclass,
int child: @ruby_underscore_expression ref,
int loc: @location ref
int child: @ruby_underscore_expression ref
);
#keyset[ruby_symbol_array, index]
@@ -1214,8 +1149,7 @@ ruby_symbol_array_child(
);
ruby_symbol_array_def(
unique int id: @ruby_symbol_array,
int loc: @location ref
unique int id: @ruby_symbol_array
);
@ruby_then_child_type = @ruby_token_empty_statement | @ruby_underscore_statement
@@ -1228,8 +1162,7 @@ ruby_then_child(
);
ruby_then_def(
unique int id: @ruby_then,
int loc: @location ref
unique int id: @ruby_then
);
@ruby_unary_operand_type = @ruby_parenthesized_statements | @ruby_underscore_expression | @ruby_underscore_simple_numeric
@@ -1247,8 +1180,7 @@ case @ruby_unary.operator of
ruby_unary_def(
unique int id: @ruby_unary,
int operand: @ruby_unary_operand_type ref,
int operator: int ref,
int loc: @location ref
int operator: int ref
);
#keyset[ruby_undef, index]
@@ -1259,8 +1191,7 @@ ruby_undef_child(
);
ruby_undef_def(
unique int id: @ruby_undef,
int loc: @location ref
unique int id: @ruby_undef
);
@ruby_unless_alternative_type = @ruby_else | @ruby_elsif
@@ -1277,41 +1208,37 @@ ruby_unless_consequence(
ruby_unless_def(
unique int id: @ruby_unless,
int condition: @ruby_underscore_statement ref,
int loc: @location ref
int condition: @ruby_underscore_statement ref
);
ruby_unless_guard_def(
unique int id: @ruby_unless_guard,
int condition: @ruby_underscore_expression ref,
int loc: @location ref
int condition: @ruby_underscore_expression ref
);
ruby_unless_modifier_def(
unique int id: @ruby_unless_modifier,
int body: @ruby_underscore_statement ref,
int condition: @ruby_underscore_expression ref,
int loc: @location ref
int condition: @ruby_underscore_expression ref
);
ruby_until_def(
unique int id: @ruby_until,
int body: @ruby_do ref,
int condition: @ruby_underscore_statement ref,
int loc: @location ref
int condition: @ruby_underscore_statement ref
);
ruby_until_modifier_def(
unique int id: @ruby_until_modifier,
int body: @ruby_underscore_statement ref,
int condition: @ruby_underscore_expression ref,
int loc: @location ref
int condition: @ruby_underscore_expression ref
);
@ruby_variable_reference_pattern_name_type = @ruby_token_identifier | @ruby_underscore_nonlocal_variable
ruby_variable_reference_pattern_def(
unique int id: @ruby_variable_reference_pattern,
int name: @ruby_token_identifier ref,
int loc: @location ref
int name: @ruby_variable_reference_pattern_name_type ref
);
ruby_when_body(
@@ -1327,22 +1254,19 @@ ruby_when_pattern(
);
ruby_when_def(
unique int id: @ruby_when,
int loc: @location ref
unique int id: @ruby_when
);
ruby_while_def(
unique int id: @ruby_while,
int body: @ruby_do ref,
int condition: @ruby_underscore_statement ref,
int loc: @location ref
int condition: @ruby_underscore_statement ref
);
ruby_while_modifier_def(
unique int id: @ruby_while_modifier,
int body: @ruby_underscore_statement ref,
int condition: @ruby_underscore_expression ref,
int loc: @location ref
int condition: @ruby_underscore_expression ref
);
ruby_yield_child(
@@ -1351,15 +1275,13 @@ ruby_yield_child(
);
ruby_yield_def(
unique int id: @ruby_yield,
int loc: @location ref
unique int id: @ruby_yield
);
ruby_tokeninfo(
unique int id: @ruby_token,
int kind: int ref,
string value: string ref,
int loc: @location ref
string value: string ref
);
case @ruby_token.kind of
@@ -1398,39 +1320,36 @@ case @ruby_token.kind of
;
@ruby_ast_node = @ruby_alias | @ruby_alternative_pattern | @ruby_argument_list | @ruby_array | @ruby_array_pattern | @ruby_as_pattern | @ruby_assignment | @ruby_bare_string | @ruby_bare_symbol | @ruby_begin | @ruby_begin_block | @ruby_binary | @ruby_block | @ruby_block_argument | @ruby_block_parameter | @ruby_block_parameters | @ruby_break | @ruby_call | @ruby_case__ | @ruby_case_match | @ruby_chained_string | @ruby_class | @ruby_conditional | @ruby_delimited_symbol | @ruby_destructured_left_assignment | @ruby_destructured_parameter | @ruby_do | @ruby_do_block | @ruby_element_reference | @ruby_else | @ruby_elsif | @ruby_end_block | @ruby_ensure | @ruby_exception_variable | @ruby_exceptions | @ruby_find_pattern | @ruby_for | @ruby_hash | @ruby_hash_pattern | @ruby_hash_splat_argument | @ruby_hash_splat_parameter | @ruby_heredoc_body | @ruby_if | @ruby_if_guard | @ruby_if_modifier | @ruby_in | @ruby_in_clause | @ruby_interpolation | @ruby_keyword_parameter | @ruby_keyword_pattern | @ruby_lambda | @ruby_lambda_parameters | @ruby_left_assignment_list | @ruby_method | @ruby_method_parameters | @ruby_module | @ruby_next | @ruby_operator_assignment | @ruby_optional_parameter | @ruby_pair | @ruby_parenthesized_pattern | @ruby_parenthesized_statements | @ruby_pattern | @ruby_program | @ruby_range | @ruby_rational | @ruby_redo | @ruby_regex | @ruby_rescue | @ruby_rescue_modifier | @ruby_rest_assignment | @ruby_retry | @ruby_return | @ruby_right_assignment_list | @ruby_scope_resolution | @ruby_setter | @ruby_singleton_class | @ruby_singleton_method | @ruby_splat_argument | @ruby_splat_parameter | @ruby_string__ | @ruby_string_array | @ruby_subshell | @ruby_superclass | @ruby_symbol_array | @ruby_then | @ruby_token | @ruby_unary | @ruby_undef | @ruby_unless | @ruby_unless_guard | @ruby_unless_modifier | @ruby_until | @ruby_until_modifier | @ruby_variable_reference_pattern | @ruby_when | @ruby_while | @ruby_while_modifier | @ruby_yield
@ruby_ast_node = @ruby_alias | @ruby_alternative_pattern | @ruby_argument_list | @ruby_array | @ruby_array_pattern | @ruby_as_pattern | @ruby_assignment | @ruby_bare_string | @ruby_bare_symbol | @ruby_begin | @ruby_begin_block | @ruby_binary | @ruby_block | @ruby_block_argument | @ruby_block_parameter | @ruby_block_parameters | @ruby_break | @ruby_call | @ruby_case__ | @ruby_case_match | @ruby_chained_string | @ruby_class | @ruby_conditional | @ruby_delimited_symbol | @ruby_destructured_left_assignment | @ruby_destructured_parameter | @ruby_do | @ruby_do_block | @ruby_element_reference | @ruby_else | @ruby_elsif | @ruby_end_block | @ruby_ensure | @ruby_exception_variable | @ruby_exceptions | @ruby_expression_reference_pattern | @ruby_find_pattern | @ruby_for | @ruby_hash | @ruby_hash_pattern | @ruby_hash_splat_argument | @ruby_hash_splat_parameter | @ruby_heredoc_body | @ruby_if | @ruby_if_guard | @ruby_if_modifier | @ruby_in | @ruby_in_clause | @ruby_interpolation | @ruby_keyword_parameter | @ruby_keyword_pattern | @ruby_lambda | @ruby_lambda_parameters | @ruby_left_assignment_list | @ruby_method | @ruby_method_parameters | @ruby_module | @ruby_next | @ruby_operator_assignment | @ruby_optional_parameter | @ruby_pair | @ruby_parenthesized_pattern | @ruby_parenthesized_statements | @ruby_pattern | @ruby_program | @ruby_range | @ruby_rational | @ruby_redo | @ruby_regex | @ruby_rescue | @ruby_rescue_modifier | @ruby_rest_assignment | @ruby_retry | @ruby_return | @ruby_right_assignment_list | @ruby_scope_resolution | @ruby_setter | @ruby_singleton_class | @ruby_singleton_method | @ruby_splat_argument | @ruby_splat_parameter | @ruby_string__ | @ruby_string_array | @ruby_subshell | @ruby_superclass | @ruby_symbol_array | @ruby_then | @ruby_token | @ruby_unary | @ruby_undef | @ruby_unless | @ruby_unless_guard | @ruby_unless_modifier | @ruby_until | @ruby_until_modifier | @ruby_variable_reference_pattern | @ruby_when | @ruby_while | @ruby_while_modifier | @ruby_yield
@ruby_ast_node_parent = @file | @ruby_ast_node
#keyset[parent, parent_index]
ruby_ast_node_parent(
int child: @ruby_ast_node ref,
ruby_ast_node_info(
unique int node: @ruby_ast_node ref,
int parent: @ruby_ast_node_parent ref,
int parent_index: int ref
int parent_index: int ref,
int loc: @location ref
);
erb_comment_directive_def(
unique int id: @erb_comment_directive,
int child: @erb_token_comment ref,
int loc: @location ref
int child: @erb_token_comment ref
);
erb_directive_def(
unique int id: @erb_directive,
int child: @erb_token_code ref,
int loc: @location ref
int child: @erb_token_code ref
);
erb_graphql_directive_def(
unique int id: @erb_graphql_directive,
int child: @erb_token_code ref,
int loc: @location ref
int child: @erb_token_code ref
);
erb_output_directive_def(
unique int id: @erb_output_directive,
int child: @erb_token_code ref,
int loc: @location ref
int child: @erb_token_code ref
);
@erb_template_child_type = @erb_comment_directive | @erb_directive | @erb_graphql_directive | @erb_output_directive | @erb_token_content
@@ -1443,15 +1362,13 @@ erb_template_child(
);
erb_template_def(
unique int id: @erb_template,
int loc: @location ref
unique int id: @erb_template
);
erb_tokeninfo(
unique int id: @erb_token,
int kind: int ref,
string value: string ref,
int loc: @location ref
string value: string ref
);
case @erb_token.kind of
@@ -1467,9 +1384,10 @@ case @erb_token.kind of
@erb_ast_node_parent = @erb_ast_node | @file
#keyset[parent, parent_index]
erb_ast_node_parent(
int child: @erb_ast_node ref,
erb_ast_node_info(
unique int node: @erb_ast_node ref,
int parent: @erb_ast_node_parent ref,
int parent_index: int ref
int parent_index: int ref,
int loc: @location ref
);

File diff suppressed because it is too large Load Diff

View File

@@ -6,122 +6,22 @@
*/
class Person extends string {
Person() {
this = "Ronil" or
this = "Dina" or
this = "Ravi" or
this = "Bruce" or
this = "Jo" or
this = "Aida" or
this = "Esme" or
this = "Charlie" or
this = "Fred" or
this = "Meera" or
this = "Maya" or
this = "Chad" or
this = "Tiana" or
this = "Laura" or
this = "George" or
this = "Will" or
this = "Mary" or
this = "Almira" or
this = "Susannah" or
this = "Rhoda" or
this = "Cynthia" or
this = "Eunice" or
this = "Olive" or
this = "Virginia" or
this = "Angeline" or
this = "Helen" or
this = "Cornelia" or
this = "Harriet" or
this = "Mahala" or
this = "Abby" or
this = "Margaret" or
this = "Deb" or
this = "Minerva" or
this = "Severus" or
this = "Lavina" or
this = "Adeline" or
this = "Cath" or
this = "Elisa" or
this = "Lucretia" or
this = "Anne" or
this = "Eleanor" or
this = "Joanna" or
this = "Adam" or
this = "Agnes" or
this = "Rosanna" or
this = "Clara" or
this = "Melissa" or
this = "Amy" or
this = "Isabel" or
this = "Jemima" or
this = "Cordelia" or
this = "Melinda" or
this = "Delila" or
this = "Jeremiah" or
this = "Elijah" or
this = "Hester" or
this = "Walter" or
this = "Oliver" or
this = "Hugh" or
this = "Aaron" or
this = "Reuben" or
this = "Eli" or
this = "Amos" or
this = "Augustus" or
this = "Theodore" or
this = "Ira" or
this = "Timothy" or
this = "Cyrus" or
this = "Horace" or
this = "Simon" or
this = "Asa" or
this = "Frank" or
this = "Nelson" or
this = "Leonard" or
this = "Harrison" or
this = "Anthony" or
this = "Louis" or
this = "Milton" or
this = "Noah" or
this = "Cornelius" or
this = "Abdul" or
this = "Warren" or
this = "Harvey" or
this = "Dennis" or
this = "Wesley" or
this = "Sylvester" or
this = "Gilbert" or
this = "Sullivan" or
this = "Edmund" or
this = "Wilson" or
this = "Perry" or
this = "Matthew" or
this = "Simba" or
this = "Nala" or
this = "Rafiki" or
this = "Shenzi" or
this = "Ernest" or
this = "Gertrude" or
this = "Oscar" or
this = "Lilian" or
this = "Raymond" or
this = "Elgar" or
this = "Elmer" or
this = "Herbert" or
this = "Maude" or
this = "Mae" or
this = "Otto" or
this = "Edwin" or
this = "Ophelia" or
this = "Parsley" or
this = "Sage" or
this = "Rosemary" or
this = "Thyme" or
this = "Garfunkel" or
this = "King Basil" or
this = "Stephen"
this =
[
"Ronil", "Dina", "Ravi", "Bruce", "Jo", "Aida", "Esme", "Charlie", "Fred", "Meera", "Maya",
"Chad", "Tiana", "Laura", "George", "Will", "Mary", "Almira", "Susannah", "Rhoda",
"Cynthia", "Eunice", "Olive", "Virginia", "Angeline", "Helen", "Cornelia", "Harriet",
"Mahala", "Abby", "Margaret", "Deb", "Minerva", "Severus", "Lavina", "Adeline", "Cath",
"Elisa", "Lucretia", "Anne", "Eleanor", "Joanna", "Adam", "Agnes", "Rosanna", "Clara",
"Melissa", "Amy", "Isabel", "Jemima", "Cordelia", "Melinda", "Delila", "Jeremiah", "Elijah",
"Hester", "Walter", "Oliver", "Hugh", "Aaron", "Reuben", "Eli", "Amos", "Augustus",
"Theodore", "Ira", "Timothy", "Cyrus", "Horace", "Simon", "Asa", "Frank", "Nelson",
"Leonard", "Harrison", "Anthony", "Louis", "Milton", "Noah", "Cornelius", "Abdul", "Warren",
"Harvey", "Dennis", "Wesley", "Sylvester", "Gilbert", "Sullivan", "Edmund", "Wilson",
"Perry", "Matthew", "Simba", "Nala", "Rafiki", "Shenzi", "Ernest", "Gertrude", "Oscar",
"Lilian", "Raymond", "Elgar", "Elmer", "Herbert", "Maude", "Mae", "Otto", "Edwin",
"Ophelia", "Parsley", "Sage", "Rosemary", "Thyme", "Garfunkel", "King Basil", "Stephen"
]
}
/** Gets the hair color of the person. If the person is bald, there is no result. */
@@ -936,25 +836,12 @@ class Person extends string {
/** Holds if the person is deceased. */
predicate isDeceased() {
this = "Ernest" or
this = "Gertrude" or
this = "Oscar" or
this = "Lilian" or
this = "Edwin" or
this = "Raymond" or
this = "Elgar" or
this = "Elmer" or
this = "Herbert" or
this = "Maude" or
this = "Mae" or
this = "Otto" or
this = "Ophelia" or
this = "Parsley" or
this = "Sage" or
this = "Rosemary" or
this = "Thyme" or
this = "Garfunkel" or
this = "King Basil"
this =
[
"Ernest", "Gertrude", "Oscar", "Lilian", "Edwin", "Raymond", "Elgar", "Elmer", "Herbert",
"Maude", "Mae", "Otto", "Ophelia", "Parsley", "Sage", "Rosemary", "Thyme", "Garfunkel",
"King Basil"
]
}
/** Gets a parent of the person (alive or deceased). */
@@ -1195,12 +1082,7 @@ class Person extends string {
}
/** Holds if the person is allowed in the region. Initially, all villagers are allowed in every region. */
predicate isAllowedIn(string region) {
region = "north" or
region = "south" or
region = "east" or
region = "west"
}
predicate isAllowedIn(string region) { region = ["north", "south", "east", "west"] }
}
/** Returns a parent of the person. */

View File

@@ -0,0 +1,26 @@
class Location extends @location {
string toString() { none() }
}
class ErbAstNodeParent extends @erb_ast_node_parent {
string toString() { none() }
}
class ErbAstNode extends @erb_ast_node {
string toString() { none() }
ErbAstNodeParent getParent(int index) { erb_ast_node_parent(this, result, index) }
Location getLocation() {
erb_tokeninfo(this, _, _, result) or
erb_comment_directive_def(this, _, result) or
erb_directive_def(this, _, result) or
erb_graphql_directive_def(this, _, result) or
erb_output_directive_def(this, _, result) or
erb_template_def(this, result)
}
}
from ErbAstNode node, ErbAstNodeParent parent, int index, Location loc
where parent = node.getParent(index) and loc = node.getLocation()
select node, parent, index, loc

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,120 @@
class Location extends @location {
string toString() { none() }
}
class RubyAstNodeParent extends @ruby_ast_node_parent {
string toString() { none() }
}
class RubyAstNode extends @ruby_ast_node {
string toString() { none() }
RubyAstNodeParent getParent(int index) { ruby_ast_node_parent(this, result, index) }
Location getLocation() {
ruby_tokeninfo(this, _, _, result) or
ruby_alias_def(this, _, _, result) or
ruby_alternative_pattern_def(this, result) or
ruby_argument_list_def(this, result) or
ruby_array_def(this, result) or
ruby_array_pattern_def(this, result) or
ruby_as_pattern_def(this, _, _, result) or
ruby_assignment_def(this, _, _, result) or
ruby_bare_string_def(this, result) or
ruby_bare_symbol_def(this, result) or
ruby_begin_def(this, result) or
ruby_begin_block_def(this, result) or
ruby_binary_def(this, _, _, _, result) or
ruby_block_def(this, result) or
ruby_block_argument_def(this, result) or
ruby_block_parameter_def(this, result) or
ruby_block_parameters_def(this, result) or
ruby_break_def(this, result) or
ruby_call_def(this, _, result) or
ruby_case_def(this, result) or
ruby_case_match_def(this, _, result) or
ruby_chained_string_def(this, result) or
ruby_class_def(this, _, result) or
ruby_conditional_def(this, _, _, _, result) or
ruby_delimited_symbol_def(this, result) or
ruby_destructured_left_assignment_def(this, result) or
ruby_destructured_parameter_def(this, result) or
ruby_do_def(this, result) or
ruby_do_block_def(this, result) or
ruby_element_reference_def(this, _, result) or
ruby_else_def(this, result) or
ruby_elsif_def(this, _, result) or
ruby_end_block_def(this, result) or
ruby_ensure_def(this, result) or
ruby_exception_variable_def(this, _, result) or
ruby_exceptions_def(this, result) or
ruby_expression_reference_pattern_def(this, _, result) or
ruby_find_pattern_def(this, result) or
ruby_for_def(this, _, _, _, result) or
ruby_hash_def(this, result) or
ruby_hash_pattern_def(this, result) or
ruby_hash_splat_argument_def(this, _, result) or
ruby_hash_splat_parameter_def(this, result) or
ruby_heredoc_body_def(this, result) or
ruby_if_def(this, _, result) or
ruby_if_guard_def(this, _, result) or
ruby_if_modifier_def(this, _, _, result) or
ruby_in_def(this, _, result) or
ruby_in_clause_def(this, _, result) or
ruby_interpolation_def(this, result) or
ruby_keyword_parameter_def(this, _, result) or
ruby_keyword_pattern_def(this, _, result) or
ruby_lambda_def(this, _, result) or
ruby_lambda_parameters_def(this, result) or
ruby_left_assignment_list_def(this, result) or
ruby_method_def(this, _, result) or
ruby_method_parameters_def(this, result) or
ruby_module_def(this, _, result) or
ruby_next_def(this, result) or
ruby_operator_assignment_def(this, _, _, _, result) or
ruby_optional_parameter_def(this, _, _, result) or
ruby_pair_def(this, _, result) or
ruby_parenthesized_pattern_def(this, _, result) or
ruby_parenthesized_statements_def(this, result) or
ruby_pattern_def(this, _, result) or
ruby_program_def(this, result) or
ruby_range_def(this, _, result) or
ruby_rational_def(this, _, result) or
ruby_redo_def(this, result) or
ruby_regex_def(this, result) or
ruby_rescue_def(this, result) or
ruby_rescue_modifier_def(this, _, _, result) or
ruby_rest_assignment_def(this, result) or
ruby_retry_def(this, result) or
ruby_return_def(this, result) or
ruby_right_assignment_list_def(this, result) or
ruby_scope_resolution_def(this, _, result) or
ruby_setter_def(this, _, result) or
ruby_singleton_class_def(this, _, result) or
ruby_singleton_method_def(this, _, _, result) or
ruby_splat_argument_def(this, _, result) or
ruby_splat_parameter_def(this, result) or
ruby_string_def(this, result) or
ruby_string_array_def(this, result) or
ruby_subshell_def(this, result) or
ruby_superclass_def(this, _, result) or
ruby_symbol_array_def(this, result) or
ruby_then_def(this, result) or
ruby_unary_def(this, _, _, result) or
ruby_undef_def(this, result) or
ruby_unless_def(this, _, result) or
ruby_unless_guard_def(this, _, result) or
ruby_unless_modifier_def(this, _, _, result) or
ruby_until_def(this, _, _, result) or
ruby_until_modifier_def(this, _, _, result) or
ruby_variable_reference_pattern_def(this, _, result) or
ruby_when_def(this, result) or
ruby_while_def(this, _, _, result) or
ruby_while_modifier_def(this, _, _, result) or
ruby_yield_def(this, result)
}
}
from RubyAstNode node, RubyAstNodeParent parent, int index, Location loc
where parent = node.getParent(index) and loc = node.getLocation()
select node, parent, index, loc

View File

@@ -0,0 +1,116 @@
description: Move location columns to a single table
compatibility: full
erb_ast_node_info.rel: run erb_ast_node_info.qlo
ruby_ast_node_info.rel: run ruby_ast_node_info.qlo
erb_ast_node_parent.rel: delete
ruby_ast_node_parent.rel: delete
erb_tokeninfo.rel: reorder erb_tokeninfo.rel (int id, int kind, string value, int loc) id kind value
ruby_tokeninfo.rel: reorder ruby_tokeninfo.rel (int id, int kind, string value, int loc) id kind value
erb_comment_directive_def.rel: reorder erb_comment_directive_def.rel (int id, int child, int loc) id child
erb_directive_def.rel: reorder erb_directive_def.rel (int id, int child, int loc) id child
erb_graphql_directive_def.rel: reorder erb_graphql_directive_def.rel (int id, int child, int loc) id child
erb_output_directive_def.rel: reorder erb_output_directive_def.rel (int id, int child, int loc) id child
erb_template_def.rel: reorder erb_template_def.rel (int id, int loc) id
ruby_alias_def.rel: reorder ruby_alias_def.rel (int id, int alias, int name, int loc) id alias name
ruby_alternative_pattern_def.rel: reorder ruby_alternative_pattern_def.rel (int id, int loc) id
ruby_argument_list_def.rel: reorder ruby_argument_list_def.rel (int id, int loc) id
ruby_array_def.rel: reorder ruby_array_def.rel (int id, int loc) id
ruby_array_pattern_def.rel: reorder ruby_array_pattern_def.rel (int id, int loc) id
ruby_as_pattern_def.rel: reorder ruby_as_pattern_def.rel (int id, int name, int value, int loc) id name value
ruby_assignment_def.rel: reorder ruby_assignment_def.rel (int id, int left, int right, int loc) id left right
ruby_bare_string_def.rel: reorder ruby_bare_string_def.rel (int id, int loc) id
ruby_bare_symbol_def.rel: reorder ruby_bare_symbol_def.rel (int id, int loc) id
ruby_begin_def.rel: reorder ruby_begin_def.rel (int id, int loc) id
ruby_begin_block_def.rel: reorder ruby_begin_block_def.rel (int id, int loc) id
ruby_binary_def.rel: reorder ruby_binary_def.rel (int id, int left, int operator, int right, int loc) id left operator right
ruby_block_def.rel: reorder ruby_block_def.rel (int id, int loc) id
ruby_block_argument_def.rel: reorder ruby_block_argument_def.rel (int id, int loc) id
ruby_block_parameter_def.rel: reorder ruby_block_parameter_def.rel (int id, int loc) id
ruby_block_parameters_def.rel: reorder ruby_block_parameters_def.rel (int id, int loc) id
ruby_break_def.rel: reorder ruby_break_def.rel (int id, int loc) id
ruby_call_def.rel: reorder ruby_call_def.rel (int id, int method, int loc) id method
ruby_case_def.rel: reorder ruby_case_def.rel (int id, int loc) id
ruby_case_match_def.rel: reorder ruby_case_match_def.rel (int id, int value, int loc) id value
ruby_chained_string_def.rel: reorder ruby_chained_string_def.rel (int id, int loc) id
ruby_class_def.rel: reorder ruby_class_def.rel (int id, int name, int loc) id name
ruby_conditional_def.rel: reorder ruby_conditional_def.rel (int id, int alternative, int condition, int consequence, int loc) id alternative condition consequence
ruby_delimited_symbol_def.rel: reorder ruby_delimited_symbol_def.rel (int id, int loc) id
ruby_destructured_left_assignment_def.rel: reorder ruby_destructured_left_assignment_def.rel (int id, int loc) id
ruby_destructured_parameter_def.rel: reorder ruby_destructured_parameter_def.rel (int id, int loc) id
ruby_do_def.rel: reorder ruby_do_def.rel (int id, int loc) id
ruby_do_block_def.rel: reorder ruby_do_block_def.rel (int id, int loc) id
ruby_element_reference_def.rel: reorder ruby_element_reference_def.rel (int id, int object, int loc) id object
ruby_else_def.rel: reorder ruby_else_def.rel (int id, int loc) id
ruby_elsif_def.rel: reorder ruby_elsif_def.rel (int id, int condition, int loc) id condition
ruby_end_block_def.rel: reorder ruby_end_block_def.rel (int id, int loc) id
ruby_ensure_def.rel: reorder ruby_ensure_def.rel (int id, int loc) id
ruby_exception_variable_def.rel: reorder ruby_exception_variable_def.rel (int id, int child, int loc) id child
ruby_exceptions_def.rel: reorder ruby_exceptions_def.rel (int id, int loc) id
ruby_expression_reference_pattern_def.rel: reorder ruby_expression_reference_pattern_def.rel (int id, int value, int loc) id value
ruby_find_pattern_def.rel: reorder ruby_find_pattern_def.rel (int id, int loc) id
ruby_for_def.rel: reorder ruby_for_def.rel (int id, int body, int pattern, int value, int loc) id body pattern value
ruby_hash_def.rel: reorder ruby_hash_def.rel (int id, int loc) id
ruby_hash_pattern_def.rel: reorder ruby_hash_pattern_def.rel (int id, int loc) id
ruby_hash_splat_argument_def.rel: reorder ruby_hash_splat_argument_def.rel (int id, int child, int loc) id child
ruby_hash_splat_parameter_def.rel: reorder ruby_hash_splat_parameter_def.rel (int id, int loc) id
ruby_heredoc_body_def.rel: reorder ruby_heredoc_body_def.rel (int id, int loc) id
ruby_if_def.rel: reorder ruby_if_def.rel (int id, int condition, int loc) id condition
ruby_if_guard_def.rel: reorder ruby_if_guard_def.rel (int id, int condition, int loc) id condition
ruby_if_modifier_def.rel: reorder ruby_if_modifier_def.rel (int id, int body, int condition, int loc) id body condition
ruby_in_def.rel: reorder ruby_in_def.rel (int id, int child, int loc) id child
ruby_in_clause_def.rel: reorder ruby_in_clause_def.rel (int id, int pattern, int loc) id pattern
ruby_interpolation_def.rel: reorder ruby_interpolation_def.rel (int id, int loc) id
ruby_keyword_parameter_def.rel: reorder ruby_keyword_parameter_def.rel (int id, int name, int loc) id name
ruby_keyword_pattern_def.rel: reorder ruby_keyword_pattern_def.rel (int id, int key__, int loc) id key__
ruby_lambda_def.rel: reorder ruby_lambda_def.rel (int id, int body, int loc) id body
ruby_lambda_parameters_def.rel: reorder ruby_lambda_parameters_def.rel (int id, int loc) id
ruby_left_assignment_list_def.rel: reorder ruby_left_assignment_list_def.rel (int id, int loc) id
ruby_method_def.rel: reorder ruby_method_def.rel (int id, int name, int loc) id name
ruby_method_parameters_def.rel: reorder ruby_method_parameters_def.rel (int id, int loc) id
ruby_module_def.rel: reorder ruby_module_def.rel (int id, int name, int loc) id name
ruby_next_def.rel: reorder ruby_next_def.rel (int id, int loc) id
ruby_operator_assignment_def.rel: reorder ruby_operator_assignment_def.rel (int id, int left, int operator, int right, int loc) id left operator right
ruby_optional_parameter_def.rel: reorder ruby_optional_parameter_def.rel (int id, int name, int value, int loc) id name value
ruby_pair_def.rel: reorder ruby_pair_def.rel (int id, int key__, int loc) id key__
ruby_parenthesized_pattern_def.rel: reorder ruby_parenthesized_pattern_def.rel (int id, int child, int loc) id child
ruby_parenthesized_statements_def.rel: reorder ruby_parenthesized_statements_def.rel (int id, int loc) id
ruby_pattern_def.rel: reorder ruby_pattern_def.rel (int id, int child, int loc) id child
ruby_program_def.rel: reorder ruby_program_def.rel (int id, int loc) id
ruby_range_def.rel: reorder ruby_range_def.rel (int id, int operator, int loc) id operator
ruby_rational_def.rel: reorder ruby_rational_def.rel (int id, int child, int loc) id child
ruby_redo_def.rel: reorder ruby_redo_def.rel (int id, int loc) id
ruby_regex_def.rel: reorder ruby_regex_def.rel (int id, int loc) id
ruby_rescue_def.rel: reorder ruby_rescue_def.rel (int id, int loc) id
ruby_rescue_modifier_def.rel: reorder ruby_rescue_modifier_def.rel (int id, int body, int handler, int loc) id body handler
ruby_rest_assignment_def.rel: reorder ruby_rest_assignment_def.rel (int id, int loc) id
ruby_retry_def.rel: reorder ruby_retry_def.rel (int id, int loc) id
ruby_return_def.rel: reorder ruby_return_def.rel (int id, int loc) id
ruby_right_assignment_list_def.rel: reorder ruby_right_assignment_list_def.rel (int id, int loc) id
ruby_scope_resolution_def.rel: reorder ruby_scope_resolution_def.rel (int id, int name, int loc) id name
ruby_setter_def.rel: reorder ruby_setter_def.rel (int id, int name, int loc) id name
ruby_singleton_class_def.rel: reorder ruby_singleton_class_def.rel (int id, int value, int loc) id value
ruby_singleton_method_def.rel: reorder ruby_singleton_method_def.rel (int id, int name, int object, int loc) id name object
ruby_splat_argument_def.rel: reorder ruby_splat_argument_def.rel (int id, int child, int loc) id child
ruby_splat_parameter_def.rel: reorder ruby_splat_parameter_def.rel (int id, int loc) id
ruby_string_def.rel: reorder ruby_string_def.rel (int id, int loc) id
ruby_string_array_def.rel: reorder ruby_string_array_def.rel (int id, int loc) id
ruby_subshell_def.rel: reorder ruby_subshell_def.rel (int id, int loc) id
ruby_superclass_def.rel: reorder ruby_superclass_def.rel (int id, int child, int loc) id child
ruby_symbol_array_def.rel: reorder ruby_symbol_array_def.rel (int id, int loc) id
ruby_then_def.rel: reorder ruby_then_def.rel (int id, int loc) id
ruby_unary_def.rel: reorder ruby_unary_def.rel (int id, int operand, int operator, int loc) id operand operator
ruby_undef_def.rel: reorder ruby_undef_def.rel (int id, int loc) id
ruby_unless_def.rel: reorder ruby_unless_def.rel (int id, int condition, int loc) id condition
ruby_unless_guard_def.rel: reorder ruby_unless_guard_def.rel (int id, int condition, int loc) id condition
ruby_unless_modifier_def.rel: reorder ruby_unless_modifier_def.rel (int id, int body, int condition, int loc) id body condition
ruby_until_def.rel: reorder ruby_until_def.rel (int id, int body, int condition, int loc) id body condition
ruby_until_modifier_def.rel: reorder ruby_until_modifier_def.rel (int id, int body, int condition, int loc) id body condition
ruby_variable_reference_pattern_def.rel: reorder ruby_variable_reference_pattern_def.rel (int id, int name, int loc) id name
ruby_when_def.rel: reorder ruby_when_def.rel (int id, int loc) id
ruby_while_def.rel: reorder ruby_while_def.rel (int id, int body, int condition, int loc) id body condition
ruby_while_modifier_def.rel: reorder ruby_while_modifier_def.rel (int id, int body, int condition, int loc) id body condition
ruby_yield_def.rel: reorder ruby_yield_def.rel (int id, int loc) id

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,7 @@
class AstNode extends @ruby_ast_node {
string toString() { none() }
}
from AstNode ruby_block_argument, AstNode child
where ruby_block_argument_def(ruby_block_argument, child, _)
select ruby_block_argument, child

View File

@@ -0,0 +1,7 @@
class AstNode extends @ruby_ast_node {
string toString() { none() }
}
from AstNode ruby_block_parameter, AstNode name
where ruby_block_parameter_def(ruby_block_parameter, name, _)
select ruby_block_parameter, name

View File

@@ -0,0 +1,7 @@
class AstNode extends @ruby_ast_node {
string toString() { none() }
}
from AstNode ruby_pair, AstNode value
where ruby_pair_def(ruby_pair, _, value, _)
select ruby_pair, value

View File

@@ -0,0 +1,8 @@
description: Update grammar
compatibility: full
ruby_block_argument_child.rel: run ruby_block_argument_child.qlo
ruby_block_parameter_name.rel: run ruby_block_parameter_name.qlo
ruby_pair_value.rel: run ruby_pair_value.qlo
ruby_block_argument_def.rel: reorder ruby_block_argument_def.rel ( int id, int child, int loc ) id loc
ruby_block_parameter_def.rel: reorder ruby_block_parameter_def.rel ( int id, int name, int loc ) id loc
ruby_pair_def.rel: reorder ruby_pair_def.rel (int id, int key__, int value, int loc) id key__ loc

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,2 @@
description: Add `unique` annotation
compatibility: full

View File

@@ -1,3 +1,15 @@
## 0.0.9
## 0.0.8
### New Queries
* Added a new query, `rb/weak-cookie-configuration`. The query finds cases where cookie configuration options are set to values that may make an application more vulnerable to certain attacks.
### Minor Analysis Improvements
* The query `rb/csrf-protection-disabled` has been extended to find calls to the Rails method `protect_from_forgery` that may weaken CSRF protection.
## 0.0.7
## 0.0.6

View File

@@ -1,5 +0,0 @@
---
category: newQuery
---
lgtm,codescanning
* Added a new query, `rb/weak-cookie-configuration`. The query finds cases where cookie configuration options are set to values that may make an application more vulnerable to certain attacks.

View File

@@ -1,5 +0,0 @@
---
category: minorAnalysis
---
lgtm,codescanning
* The query `rb/csrf-protection-disabled` has been extended to find calls to the Rails method `protect_from_forgery` that may weaken CSRF protection.

View File

@@ -0,0 +1,9 @@
## 0.0.8
### New Queries
* Added a new query, `rb/weak-cookie-configuration`. The query finds cases where cookie configuration options are set to values that may make an application more vulnerable to certain attacks.
### Minor Analysis Improvements
* The query `rb/csrf-protection-disabled` has been extended to find calls to the Rails method `protect_from_forgery` that may weaken CSRF protection.

View File

@@ -0,0 +1 @@
## 0.0.9

View File

@@ -1,2 +1,2 @@
---
lastReleaseVersion: 0.0.7
lastReleaseVersion: 0.0.9

View File

@@ -1,5 +1,5 @@
name: codeql/ruby-queries
version: 0.0.8-dev
version: 0.0.10-dev
groups:
- ruby
- queries

View File

@@ -93,7 +93,7 @@
private import InlineExpectationsTestPrivate
/**
* Base class for tests with inline expectations. The test extends this class to provide the actual
* The base class for tests with inline expectations. The test extends this class to provide the actual
* results of the query, which are then compared with the expected results in comments to produce a
* list of failure messages that point out where the actual results differ from the expected
* results.
@@ -123,6 +123,15 @@ abstract class InlineExpectationsTest extends string {
*/
abstract predicate hasActualResult(Location location, string element, string tag, string value);
/**
* Like `hasActualResult`, but returns results that do not require a matching annotation.
* A failure will still arise if there is an annotation that does not match any results, but not vice versa.
* Override this predicate to specify optional results.
*/
predicate hasOptionalResult(Location location, string element, string tag, string value) {
none()
}
final predicate hasFailureMessage(FailureLocatable element, string message) {
exists(ActualResult actualResult |
actualResult.getTest() = this and
@@ -134,7 +143,8 @@ abstract class InlineExpectationsTest extends string {
)
or
not exists(ValidExpectation expectation | expectation.matchesActualResult(actualResult)) and
message = "Unexpected result: " + actualResult.getExpectationText()
message = "Unexpected result: " + actualResult.getExpectationText() and
not actualResult.isOptional()
)
)
or
@@ -243,9 +253,13 @@ private string expectationPattern() {
private newtype TFailureLocatable =
TActualResult(
InlineExpectationsTest test, Location location, string element, string tag, string value
InlineExpectationsTest test, Location location, string element, string tag, string value,
boolean optional
) {
test.hasActualResult(location, element, tag, value)
test.hasActualResult(location, element, tag, value) and
optional = false
or
test.hasOptionalResult(location, element, tag, value) and optional = true
} or
TValidExpectation(ExpectationComment comment, string tag, string value, string knownFailure) {
exists(TColumn column, string tags |
@@ -277,8 +291,9 @@ class ActualResult extends FailureLocatable, TActualResult {
string element;
string tag;
string value;
boolean optional;
ActualResult() { this = TActualResult(test, location, element, tag, value) }
ActualResult() { this = TActualResult(test, location, element, tag, value, optional) }
override string toString() { result = element }
@@ -289,6 +304,8 @@ class ActualResult extends FailureLocatable, TActualResult {
override string getTag() { result = tag }
override string getValue() { result = value }
predicate isOptional() { optional = true }
}
abstract private class Expectation extends FailureLocatable {

View File

@@ -457,6 +457,9 @@ calls/calls.rb:
# 267| getArgument: [BlockArgument] &...
# 267| getValue: [MethodCall] call to bar
# 267| getReceiver: [ConstantReadAccess] X
# 268| getStmt: [MethodCall] call to foo
# 268| getReceiver: [Self, SelfVariableAccess] self
# 268| getArgument: [BlockArgument] &...
# 270| getStmt: [MethodCall] call to foo
# 270| getReceiver: [Self, SelfVariableAccess] self
# 270| getArgument: [SplatExpr] * ...
@@ -673,6 +676,29 @@ calls/calls.rb:
# 341| getArgument: [LocalVariableAccess] x
# 341| getArgument: [LocalVariableAccess] y
# 341| getArgument: [LocalVariableAccess] z
# 344| getStmt: [MethodCall] call to foo
# 344| getReceiver: [Self, SelfVariableAccess] self
# 344| getArgument: [Pair] Pair
# 344| getKey: [SymbolLiteral] :x
# 344| getValue: [IntegerLiteral] 42
# 345| getStmt: [MethodCall] call to foo
# 345| getReceiver: [Self, SelfVariableAccess] self
# 345| getArgument: [Pair] Pair
# 345| getKey: [SymbolLiteral] :x
# 345| getValue: [LocalVariableAccess] x
# 345| getArgument: [Pair] Pair
# 345| getKey: [SymbolLiteral] :novar
# 345| getValue: [MethodCall] call to novar
# 346| getStmt: [MethodCall] call to foo
# 346| getReceiver: [Self, SelfVariableAccess] self
# 346| getArgument: [Pair] Pair
# 346| getKey: [SymbolLiteral] :X
# 346| getValue: [IntegerLiteral] 42
# 347| getStmt: [MethodCall] call to foo
# 347| getReceiver: [Self, SelfVariableAccess] self
# 347| getArgument: [Pair] Pair
# 347| getKey: [SymbolLiteral] :X
# 347| getValue: [ConstantReadAccess] X
control/cases.rb:
# 1| [Toplevel] cases.rb
# 2| getStmt: [AssignExpr] ... = ...
@@ -925,8 +951,8 @@ control/cases.rb:
# 87| getBranch: [InClause] in ... then ...
# 87| getPattern: [IntegerLiteral] 5
# 88| getBranch: [InClause] in ... then ...
# 88| getPattern: [VariableReferencePattern] ^...
# 88| getVariableAccess: [LocalVariableAccess] foo
# 88| getPattern: [ReferencePattern] ^...
# 88| getExpr: [LocalVariableAccess] foo
# 89| getBranch: [InClause] in ... then ...
# 89| getPattern: [StringLiteral] "string"
# 89| getComponent: [StringTextComponent] string
@@ -1023,8 +1049,8 @@ control/cases.rb:
# 111| getBranch: [InClause] in ... then ...
# 111| getPattern: [AlternativePattern] ... | ...
# 111| getAlternative: [IntegerLiteral] 5
# 111| getAlternative: [VariableReferencePattern] ^...
# 111| getVariableAccess: [LocalVariableAccess] foo
# 111| getAlternative: [ReferencePattern] ^...
# 111| getExpr: [LocalVariableAccess] foo
# 111| getAlternative: [StringLiteral] "string"
# 111| getComponent: [StringTextComponent] string
# 111| getAlternative: [LocalVariableAccess] var
@@ -1115,6 +1141,35 @@ control/cases.rb:
# 144| getClass: [ConstantReadAccess] Bar
# 144| getKey: [SymbolLiteral] :a
# 144| getValue: [IntegerLiteral] 1
# 147| getStmt: [CaseExpr] case ...
# 147| getValue: [MethodCall] call to expr
# 147| getReceiver: [Self, SelfVariableAccess] self
# 148| getBranch: [InClause] in ... then ...
# 148| getPattern: [ReferencePattern] ^...
# 148| getExpr: [LocalVariableAccess] foo
# 149| getBranch: [InClause] in ... then ...
# 149| getPattern: [ReferencePattern] ^...
# 149| getExpr: [GlobalVariableAccess] $foo
# 150| getBranch: [InClause] in ... then ...
# 150| getPattern: [ReferencePattern] ^...
# 150| getExpr: [InstanceVariableAccess] @foo
# 151| getBranch: [InClause] in ... then ...
# 151| getPattern: [ReferencePattern] ^...
# 151| getExpr: [ClassVariableAccess] @@foo
# 154| getStmt: [CaseExpr] case ...
# 154| getValue: [MethodCall] call to expr
# 154| getReceiver: [Self, SelfVariableAccess] self
# 155| getBranch: [InClause] in ... then ...
# 155| getPattern: [ReferencePattern] ^...
# 155| getExpr: [LocalVariableAccess] foo
# 156| getBranch: [InClause] in ... then ...
# 156| getPattern: [ReferencePattern] ^...
# 156| getExpr: [InstanceVariableAccess] @foo
# 157| getBranch: [InClause] in ... then ...
# 157| getPattern: [ReferencePattern] ^...
# 157| getExpr: [AddExpr] ... + ...
# 157| getAnOperand/getLeftOperand/getReceiver: [IntegerLiteral] 1
# 157| getAnOperand/getArgument/getRightOperand: [IntegerLiteral] 1
modules/classes.rb:
# 2| [Toplevel] classes.rb
# 3| getStmt: [ClassDeclaration] Foo
@@ -1945,6 +2000,23 @@ literals/literals.rb:
# 180| getComponent: [StringTextComponent]
# 180| cat file.txt
# 180|
# 184| getStmt: [AssignExpr] ... = ...
# 184| getAnOperand/getLeftOperand: [LocalVariableAccess] x
# 184| getAnOperand/getRightOperand: [IntegerLiteral] 42
# 185| getStmt: [HashLiteral] {...}
# 185| getElement: [Pair] Pair
# 185| getKey: [SymbolLiteral] :x
# 185| getValue: [LocalVariableAccess] x
# 185| getElement: [Pair] Pair
# 185| getKey: [SymbolLiteral] :y
# 185| getValue: [IntegerLiteral] 5
# 186| getStmt: [HashLiteral] {...}
# 186| getElement: [Pair] Pair
# 186| getKey: [SymbolLiteral] :y
# 186| getValue: [MethodCall] call to y
# 186| getElement: [Pair] Pair
# 186| getKey: [SymbolLiteral] :Z
# 186| getValue: [ConstantReadAccess] Z
control/loops.rb:
# 1| [Toplevel] loops.rb
# 2| getStmt: [AssignExpr] ... = ...
@@ -2679,6 +2751,19 @@ params/params.rb:
# 77| getParameter: [SimpleParameter] val
# 77| getDefiningAccess: [LocalVariableAccess] val
# 77| getParameter: [HashSplatNilParameter] **nil
# 81| getStmt: [Method] anonymous_block_parameter
# 81| getParameter: [SimpleParameter] array
# 81| getDefiningAccess: [LocalVariableAccess] array
# 81| getParameter: [BlockParameter] &
# 81| getDefiningAccess: [LocalVariableAccess] __synth__0
# 82| getStmt: [MethodCall] call to proc
# 82| getReceiver: [Self, SelfVariableAccess] self
# 82| getArgument: [BlockArgument] &...
# 82| getValue: [LocalVariableAccess] __synth__0
# 83| getStmt: [MethodCall] call to each
# 83| getReceiver: [LocalVariableAccess] array
# 83| getArgument: [BlockArgument] &...
# 83| getValue: [LocalVariableAccess] __synth__0
erb/template.html.erb:
# 19| [Toplevel] template.html.erb
# 19| getStmt: [StringLiteral] "hello world"

View File

@@ -477,6 +477,24 @@ literals/literals.rb:
# 118| getArgument: [HashSplatExpr] ** ...
# 118| getAnOperand/getOperand/getReceiver: [MethodCall] call to baz
# 118| getReceiver: [Self, SelfVariableAccess] self
# 185| [HashLiteral] {...}
# 185| getDesugared: [MethodCall] call to []
# 185| getReceiver: [ConstantReadAccess] Hash
# 185| getArgument: [Pair] Pair
# 185| getKey: [SymbolLiteral] :x
# 185| getValue: [LocalVariableAccess] x
# 185| getArgument: [Pair] Pair
# 185| getKey: [SymbolLiteral] :y
# 185| getValue: [IntegerLiteral] 5
# 186| [HashLiteral] {...}
# 186| getDesugared: [MethodCall] call to []
# 186| getReceiver: [ConstantReadAccess] Hash
# 186| getArgument: [Pair] Pair
# 186| getKey: [SymbolLiteral] :y
# 186| getValue: [MethodCall] call to y
# 186| getArgument: [Pair] Pair
# 186| getKey: [SymbolLiteral] :Z
# 186| getValue: [ConstantReadAccess] Z
control/loops.rb:
# 9| [ForExpr] for ... in ...
# 9| getDesugared: [MethodCall] call to each

View File

@@ -65,6 +65,13 @@
| 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 |
| calls/calls.rb:344:5:344:5 | :x | :x |
| calls/calls.rb:344:8:344:9 | 42 | 42 |
| calls/calls.rb:345:5:345:5 | :x | :x |
| calls/calls.rb:345:9:345:13 | :novar | :novar |
| calls/calls.rb:346:5:346:5 | :X | :X |
| calls/calls.rb:346:8:346:9 | 42 | 42 |
| calls/calls.rb:347:5:347:5 | :X | :X |
| 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 |
@@ -236,6 +243,11 @@
| control/cases.rb:142:11:142:11 | :y | :y |
| control/cases.rb:144:11:144:11 | :a | :a |
| control/cases.rb:144:14:144:14 | 1 | 1 |
| control/cases.rb:148:7:148:9 | foo | 42 |
| control/cases.rb:155:8:155:10 | foo | 42 |
| control/cases.rb:157:8:157:8 | 1 | 1 |
| control/cases.rb:157:8:157:12 | ... + ... | 2 |
| control/cases.rb:157:12:157:12 | 1 | 1 |
| control/conditionals.rb:2:5:2:5 | 0 | 0 |
| control/conditionals.rb:3:5:3:5 | 0 | 0 |
| control/conditionals.rb:4:5:4:5 | 0 | 0 |
@@ -571,6 +583,13 @@
| literals/literals.rb:162:11:162:16 | <<-BLA | \nsome text\\nand some more\n |
| literals/literals.rb:167:9:167:19 | <<~SQUIGGLY | \n indented stuff\n |
| literals/literals.rb:180:10:180:19 | <<`SCRIPT` | \n cat file.txt\n |
| literals/literals.rb:184:5:184:6 | 42 | 42 |
| literals/literals.rb:185:2:185:2 | :x | :x |
| literals/literals.rb:185:2:185:2 | x | 42 |
| literals/literals.rb:185:6:185:6 | :y | :y |
| literals/literals.rb:185:8:185:8 | 5 | 5 |
| literals/literals.rb:186:2:186:2 | :y | :y |
| literals/literals.rb:186:7:186:7 | :Z | :Z |
| misc/misc.erb:2:15:2:37 | "main_include_admin.js" | main_include_admin.js |
| misc/misc.rb:1:7:1:11 | "bar" | bar |
| misc/misc.rb:3:7:3:9 | foo | foo |

View File

@@ -15,6 +15,16 @@ keywordArguments
| calls.rb:249:15:249:30 | Pair | calls.rb:249:15:249:20 | call to foo | calls.rb:249:25:249:30 | call to bar |
| calls.rb:278:5:278:13 | Pair | calls.rb:278:5:278:8 | :blah | calls.rb:278:11:278:13 | call to bar |
| calls.rb:279:5:279:16 | Pair | calls.rb:279:5:279:8 | :blah | calls.rb:279:11:279:16 | call to bar |
| calls.rb:344:5:344:9 | Pair | calls.rb:344:5:344:5 | :x | calls.rb:344:8:344:9 | 42 |
| calls.rb:345:5:345:6 | Pair | calls.rb:345:5:345:5 | :x | calls.rb:345:5:345:5 | x |
| calls.rb:345:9:345:14 | Pair | calls.rb:345:9:345:13 | :novar | calls.rb:345:9:345:13 | call to novar |
| calls.rb:346:5:346:9 | Pair | calls.rb:346:5:346:5 | :X | calls.rb:346:8:346:9 | 42 |
| calls.rb:347:5:347:6 | Pair | calls.rb:347:5:347:5 | :X | calls.rb:347:5:347:5 | X |
keywordArgumentsByKeyword
| calls.rb:278:1:278:14 | call to foo | blah | calls.rb:278:11:278:13 | call to bar |
| calls.rb:279:1:279:17 | call to foo | blah | calls.rb:279:11:279:16 | call to bar |
| calls.rb:344:1:344:10 | call to foo | x | calls.rb:344:8:344:9 | 42 |
| calls.rb:345:1:345:15 | call to foo | novar | calls.rb:345:9:345:13 | call to novar |
| calls.rb:345:1:345:15 | call to foo | x | calls.rb:345:5:345:5 | x |
| calls.rb:346:1:346:10 | call to foo | X | calls.rb:346:8:346:9 | 42 |
| calls.rb:347:1:347:7 | call to foo | X | calls.rb:347:5:347:5 | X |

View File

@@ -3,6 +3,7 @@ callsWithNoReceiverArgumentsOrBlock
| calls.rb:286:5:286:9 | call to super | super |
| calls.rb:287:5:287:11 | call to super | super |
| calls.rb:305:5:305:9 | call to super | super |
| calls.rb:345:9:345:13 | call to novar | novar |
callsWithArguments
| calls.rb:14:1:14:11 | call to foo | foo | 0 | calls.rb:14:5:14:5 | 0 |
| calls.rb:14:1:14:11 | call to foo | foo | 1 | calls.rb:14:8:14:8 | 1 |
@@ -26,6 +27,7 @@ callsWithArguments
| calls.rb:249:1:249:32 | call to [] | [] | 1 | calls.rb:249:15:249:30 | Pair |
| calls.rb:266:1:266:9 | call to foo | foo | 0 | calls.rb:266:5:266:8 | &... |
| calls.rb:267:1:267:12 | call to foo | foo | 0 | calls.rb:267:5:267:11 | &... |
| calls.rb:268:1:268:6 | call to foo | foo | 0 | calls.rb:268:5:268:5 | &... |
| calls.rb:270:1:270:9 | call to foo | foo | 0 | calls.rb:270:5:270:8 | * ... |
| calls.rb:271:1:271:12 | call to foo | foo | 0 | calls.rb:271:5:271:11 | * ... |
| calls.rb:274:1:274:10 | call to foo | foo | 0 | calls.rb:274:5:274:9 | ** ... |
@@ -106,6 +108,11 @@ callsWithArguments
| 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 |
| calls.rb:344:1:344:10 | call to foo | foo | 0 | calls.rb:344:5:344:9 | Pair |
| calls.rb:345:1:345:15 | call to foo | foo | 0 | calls.rb:345:5:345:6 | Pair |
| calls.rb:345:1:345:15 | call to foo | foo | 1 | calls.rb:345:9:345:14 | Pair |
| calls.rb:346:1:346:10 | call to foo | foo | 0 | calls.rb:346:5:346:9 | Pair |
| calls.rb:347:1:347:7 | call to foo | foo | 0 | calls.rb:347:5:347:6 | Pair |
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 |
@@ -267,6 +274,7 @@ callsWithReceiver
| calls.rb:266:6:266:8 | call to bar | calls.rb:266:6:266:8 | self |
| calls.rb:267:1:267:12 | call to foo | calls.rb:267:1:267:12 | self |
| calls.rb:267:6:267:11 | call to bar | calls.rb:267:6:267:6 | X |
| calls.rb:268:1:268:6 | call to foo | calls.rb:268:1:268:6 | self |
| calls.rb:270:1:270:9 | call to foo | calls.rb:270:1:270:9 | self |
| calls.rb:270:5:270:8 | * ... | calls.rb:270:6:270:8 | call to bar |
| calls.rb:270:6:270:8 | call to bar | calls.rb:270:6:270:8 | self |
@@ -356,6 +364,10 @@ callsWithReceiver
| 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 |
| calls.rb:344:1:344:10 | call to foo | calls.rb:344:1:344:10 | self |
| calls.rb:345:1:345:15 | call to foo | calls.rb:345:1:345:15 | self |
| calls.rb:346:1:346:10 | call to foo | calls.rb:346:1:346:10 | self |
| calls.rb:347:1:347:7 | call to foo | calls.rb:347:1:347:7 | 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 |

View File

@@ -265,7 +265,7 @@ X::foo rescue X::bar
# block argument
foo(&bar)
foo(&X::bar)
foo(&)
# splat argument
foo(*bar)
foo(*X::bar)
@@ -340,3 +340,8 @@ end
for x, y, z in [[1,2,3], [4,5,6]]
foo x, y, z
end
foo(x: 42)
foo(x:, novar:)
foo(X: 42)
foo(X:)

View File

@@ -8,6 +8,8 @@ caseValues
| cases.rb:116:1:124:3 | case ... | cases.rb:116:6:116:9 | call to expr |
| cases.rb:128:1:133:3 | case ... | cases.rb:128:6:128:9 | call to expr |
| cases.rb:137:1:145:3 | case ... | cases.rb:137:6:137:9 | call to expr |
| cases.rb:147:1:152:3 | case ... | cases.rb:147:6:147:9 | call to expr |
| cases.rb:154:1:158:3 | case ... | cases.rb:154:6:154:9 | call to expr |
caseNoValues
| cases.rb:18:1:22:3 | case ... |
caseElseBranches
@@ -22,6 +24,8 @@ caseNoElseBranches
| cases.rb:116:1:124:3 | case ... |
| cases.rb:128:1:133:3 | case ... |
| cases.rb:137:1:145:3 | case ... |
| cases.rb:147:1:152:3 | case ... |
| cases.rb:154:1:158:3 | case ... |
caseWhenBranches
| cases.rb:8:1:15:3 | case ... | cases.rb:9:1:10:7 | when ... | 0 | cases.rb:9:6:9:6 | b | cases.rb:9:7:10:7 | then ... |
| cases.rb:8:1:15:3 | case ... | cases.rb:11:1:12:7 | when ... | 0 | cases.rb:11:6:11:6 | c | cases.rb:11:10:12:7 | then ... |
@@ -121,3 +125,10 @@ caseAllBranches
| cases.rb:137:1:145:3 | case ... | 4 | cases.rb:142:3:142:14 | in ... then ... |
| cases.rb:137:1:145:3 | case ... | 5 | cases.rb:143:3:143:15 | in ... then ... |
| cases.rb:137:1:145:3 | case ... | 6 | cases.rb:144:3:144:23 | in ... then ... |
| cases.rb:147:1:152:3 | case ... | 0 | cases.rb:148:3:148:10 | in ... then ... |
| cases.rb:147:1:152:3 | case ... | 1 | cases.rb:149:3:149:11 | in ... then ... |
| cases.rb:147:1:152:3 | case ... | 2 | cases.rb:150:3:150:11 | in ... then ... |
| cases.rb:147:1:152:3 | case ... | 3 | cases.rb:151:3:151:12 | in ... then ... |
| cases.rb:154:1:158:3 | case ... | 0 | cases.rb:155:3:155:12 | in ... then ... |
| cases.rb:154:1:158:3 | case ... | 1 | cases.rb:156:3:156:13 | in ... then ... |
| cases.rb:154:1:158:3 | case ... | 2 | cases.rb:157:3:157:14 | in ... then ... |

View File

@@ -8,6 +8,8 @@
| cases.rb:116:1:124:3 | case ... | CaseExpr |
| cases.rb:128:1:133:3 | case ... | CaseExpr |
| cases.rb:137:1:145:3 | case ... | CaseExpr |
| cases.rb:147:1:152:3 | case ... | CaseExpr |
| cases.rb:154:1:158:3 | case ... | CaseExpr |
| conditionals.rb:10:1:12:3 | if ... | IfExpr |
| conditionals.rb:15:1:19:3 | if ... | IfExpr |
| conditionals.rb:22:1:30:3 | if ... | IfExpr |

View File

@@ -144,3 +144,15 @@ case expr
in Bar( a: 1, **nil);
end
case expr
in ^foo;
in ^$foo;
in ^@foo;
in ^@@foo;
end
case expr
in ^(foo);
in ^(@foo);
in ^(1 + 1);
end

View File

@@ -217,6 +217,14 @@ allLiterals
| literals.rb:171:9:171:15 | <<"DOC" | HereDoc | <none> |
| literals.rb:176:9:176:15 | <<'DOC' | HereDoc | <none> |
| literals.rb:180:10:180:19 | <<`SCRIPT` | HereDoc | \n cat file.txt\n |
| literals.rb:184:5:184:6 | 42 | IntegerLiteral | 42 |
| literals.rb:185:1:185:9 | {...} | HashLiteral | <none> |
| literals.rb:185:2:185:2 | :x | SymbolLiteral | :x |
| literals.rb:185:6:185:6 | :y | SymbolLiteral | :y |
| literals.rb:185:8:185:8 | 5 | IntegerLiteral | 5 |
| literals.rb:186:1:186:9 | {...} | HashLiteral | <none> |
| literals.rb:186:2:186:2 | :y | SymbolLiteral | :y |
| literals.rb:186:7:186:7 | :Z | SymbolLiteral | :Z |
stringlikeLiterals
| literals.rb:46:1:46:2 | "" | |
| literals.rb:47:1:47:2 | "" | |
@@ -327,6 +335,10 @@ stringlikeLiterals
| literals.rb:171:9:171:15 | <<"DOC" | <none> |
| literals.rb:176:9:176:15 | <<'DOC' | <none> |
| literals.rb:180:10:180:19 | <<`SCRIPT` | \n cat file.txt\n |
| literals.rb:185:2:185:2 | :x | :x |
| literals.rb:185:6:185:6 | :y | :y |
| literals.rb:186:2:186:2 | :y | :y |
| literals.rb:186:7:186:7 | :Z | :Z |
stringLiterals
| literals.rb:46:1:46:2 | "" | |
| literals.rb:47:1:47:2 | "" | |
@@ -435,6 +447,10 @@ symbolLiterals
| literals.rb:117:3:117:5 | :foo | :foo |
| literals.rb:117:11:117:14 | :bar | :bar |
| literals.rb:118:3:118:5 | :foo | :foo |
| literals.rb:185:2:185:2 | :x | :x |
| literals.rb:185:6:185:6 | :y | :y |
| literals.rb:186:2:186:2 | :y | :y |
| literals.rb:186:7:186:7 | :Z | :Z |
subshellLiterals
| literals.rb:130:1:130:7 | `ls -l` | ls -l |
| literals.rb:131:1:131:9 | `ls -l` | ls -l |
@@ -701,6 +717,8 @@ hashLiterals
| literals.rb:116:1:116:2 | {...} | 0 |
| literals.rb:117:1:117:33 | {...} | 3 |
| literals.rb:118:1:118:17 | {...} | 2 |
| literals.rb:185:1:185:9 | {...} | 2 |
| literals.rb:186:1:186:9 | {...} | 2 |
hashLiteralElements
| literals.rb:88:1:88:14 | {...} | 0 | literals.rb:88:3:88:12 | Pair | Pair |
| literals.rb:117:1:117:33 | {...} | 0 | literals.rb:117:3:117:8 | Pair | Pair |
@@ -708,12 +726,20 @@ hashLiteralElements
| literals.rb:117:1:117:33 | {...} | 2 | literals.rb:117:22:117:31 | Pair | Pair |
| literals.rb:118:1:118:17 | {...} | 0 | literals.rb:118:3:118:8 | Pair | Pair |
| literals.rb:118:1:118:17 | {...} | 1 | literals.rb:118:11:118:15 | ** ... | HashSplatExpr |
| literals.rb:185:1:185:9 | {...} | 0 | literals.rb:185:2:185:3 | Pair | Pair |
| literals.rb:185:1:185:9 | {...} | 1 | literals.rb:185:6:185:8 | Pair | Pair |
| literals.rb:186:1:186:9 | {...} | 0 | literals.rb:186:2:186:3 | Pair | Pair |
| literals.rb:186:1:186:9 | {...} | 1 | literals.rb:186:7:186:8 | Pair | Pair |
hashLiteralKeyValuePairs
| literals.rb:88:1:88:14 | {...} | literals.rb:88:3:88:12 | Pair | literals.rb:88:3:88:5 | :foo | literals.rb:88:8:88:12 | "bar" |
| literals.rb:117:1:117:33 | {...} | literals.rb:117:3:117:8 | Pair | literals.rb:117:3:117:5 | :foo | literals.rb:117:8:117:8 | 1 |
| literals.rb:117:1:117:33 | {...} | literals.rb:117:11:117:19 | Pair | literals.rb:117:11:117:14 | :bar | literals.rb:117:19:117:19 | 2 |
| literals.rb:117:1:117:33 | {...} | literals.rb:117:22:117:31 | Pair | literals.rb:117:22:117:26 | "baz" | literals.rb:117:31:117:31 | 3 |
| literals.rb:118:1:118:17 | {...} | literals.rb:118:3:118:8 | Pair | literals.rb:118:3:118:5 | :foo | literals.rb:118:8:118:8 | 7 |
| literals.rb:185:1:185:9 | {...} | literals.rb:185:2:185:3 | Pair | literals.rb:185:2:185:2 | :x | literals.rb:185:2:185:2 | x |
| literals.rb:185:1:185:9 | {...} | literals.rb:185:6:185:8 | Pair | literals.rb:185:6:185:6 | :y | literals.rb:185:8:185:8 | 5 |
| literals.rb:186:1:186:9 | {...} | literals.rb:186:2:186:3 | Pair | literals.rb:186:2:186:2 | :y | literals.rb:186:2:186:2 | call to y |
| literals.rb:186:1:186:9 | {...} | literals.rb:186:7:186:8 | Pair | literals.rb:186:7:186:7 | :Z | literals.rb:186:7:186:7 | Z |
finiteRangeLiterals
| literals.rb:121:2:121:6 | _ .. _ | literals.rb:121:2:121:2 | 1 | literals.rb:121:5:121:6 | 10 |
| literals.rb:122:2:122:7 | _ ... _ | literals.rb:122:2:122:2 | 1 | literals.rb:122:6:122:7 | 10 |
@@ -802,6 +828,8 @@ numericLiterals
| literals.rb:140:12:140:12 | 1 | IntegerLiteral | 1 |
| literals.rb:146:10:146:10 | 1 | IntegerLiteral | 1 |
| literals.rb:146:14:146:14 | 1 | IntegerLiteral | 1 |
| literals.rb:184:5:184:6 | 42 | IntegerLiteral | 42 |
| literals.rb:185:8:185:8 | 5 | IntegerLiteral | 5 |
integerLiterals
| literals.rb:10:1:10:4 | 1234 | IntegerLiteral | 1234 |
| literals.rb:11:1:11:5 | 5_678 | IntegerLiteral | 5678 |
@@ -865,6 +893,8 @@ integerLiterals
| literals.rb:140:12:140:12 | 1 | IntegerLiteral | 1 |
| literals.rb:146:10:146:10 | 1 | IntegerLiteral | 1 |
| literals.rb:146:14:146:14 | 1 | IntegerLiteral | 1 |
| literals.rb:184:5:184:6 | 42 | IntegerLiteral | 42 |
| literals.rb:185:8:185:8 | 5 | IntegerLiteral | 5 |
floatLiterals
| literals.rb:30:1:30:5 | 12.34 | FloatLiteral | 12.34 |
| literals.rb:31:1:31:7 | 1234e-2 | FloatLiteral | 12.34 |

View File

@@ -181,3 +181,6 @@ output = <<`SCRIPT`
cat file.txt
SCRIPT
x = 42
{x:, y:5}
{y: , Z:}

View File

@@ -35,6 +35,7 @@ idParams
| params.rb:70:48:70:48 | c | c |
| params.rb:73:27:73:32 | wibble | wibble |
| params.rb:77:16:77:18 | val | val |
| params.rb:81:31:81:35 | array | array |
blockParams
| params.rb:46:28:46:33 | &block | block |
| params.rb:62:29:62:34 | &block | block |
@@ -87,6 +88,8 @@ paramsInMethods
| params.rb:62:1:64:3 | use_block_with_optional | 0 | params.rb:62:29:62:34 | &block | BlockParameter |
| params.rb:73:1:74:3 | method_with_nil_splat | 0 | params.rb:73:27:73:32 | wibble | SimpleParameter |
| params.rb:73:1:74:3 | method_with_nil_splat | 1 | params.rb:73:35:73:39 | **nil | HashSplatNilParameter |
| params.rb:81:1:84:3 | anonymous_block_parameter | 0 | params.rb:81:31:81:35 | array | SimpleParameter |
| params.rb:81:1:84:3 | anonymous_block_parameter | 1 | params.rb:81:38:81:38 | & | BlockParameter |
paramsInBlocks
| params.rb:9:11:11:3 | do ... end | 0 | params.rb:9:15:9:17 | key | SimpleParameter |
| params.rb:9:11:11:3 | do ... end | 1 | params.rb:9:20:9:24 | value | SimpleParameter |
@@ -157,3 +160,5 @@ params
| params.rb:73:35:73:39 | **nil | 1 | HashSplatNilParameter |
| params.rb:77:16:77:18 | val | 0 | SimpleParameter |
| params.rb:77:21:77:25 | **nil | 1 | HashSplatNilParameter |
| params.rb:81:31:81:35 | array | 0 | SimpleParameter |
| params.rb:81:38:81:38 | & | 1 | BlockParameter |

View File

@@ -77,3 +77,8 @@ end
array.each do |val, **nil|
end
# Anonymous block parameter
def anonymous_block_parameter(array, &)
proc(&)
array.each(&)
end

View File

@@ -48,9 +48,9 @@ break_ensure.rb:
#-----| -> 0
# 3| ... > ...
#-----| true -> break
#-----| false -> if ...
#-----| raise -> while ...
#-----| false -> if ...
#-----| true -> break
# 3| 0
#-----| -> ... > ...
@@ -160,8 +160,8 @@ break_ensure.rb:
#-----| -> 0
# 16| ... > ...
#-----| true -> break
#-----| false -> if ...
#-----| true -> break
#-----| raise -> [ensure: raise] y
# 16| 0
@@ -364,16 +364,16 @@ break_ensure.rb:
#-----| -> 0
# 35| ... > ...
#-----| true -> break
#-----| false -> if ...
#-----| true -> break
# 35| [ensure: raise] ... > ...
#-----| true -> [ensure: raise] break
#-----| false -> [ensure: raise] if ...
#-----| true -> [ensure: raise] break
# 35| [ensure: return] ... > ...
#-----| true -> [ensure: return] break
#-----| false -> [ensure: return] if ...
#-----| true -> [ensure: return] break
# 35| 0
#-----| -> ... > ...
@@ -443,8 +443,8 @@ break_ensure.rb:
# 47| ... > ...
#-----| false -> if ...
#-----| raise -> [ensure: raise] x
#-----| true -> self
#-----| raise -> [ensure: raise] x
# 47| 1
#-----| -> ... > ...
@@ -535,8 +535,8 @@ case.rb:
#-----| -> 1
# 3| 1
#-----| no-match -> when ...
#-----| match -> self
#-----| no-match -> when ...
# 3| then ...
#-----| -> case ...
@@ -622,8 +622,8 @@ case.rb:
#-----| -> 1
# 11| 1
#-----| no-match -> in ... then ...
#-----| match -> 3
#-----| no-match -> in ... then ...
# 11| then ...
#-----| -> case ...
@@ -635,8 +635,8 @@ case.rb:
#-----| -> 2
# 12| 2
#-----| no-match -> in ... then ...
#-----| match -> 4
#-----| no-match -> in ... then ...
# 12| then ...
#-----| -> case ...
@@ -654,8 +654,8 @@ case.rb:
#-----| -> 5
# 14| ... == ...
#-----| false -> in ... then ...
#-----| true -> 6
#-----| false -> in ... then ...
# 14| 5
#-----| -> ... == ...
@@ -721,8 +721,8 @@ case.rb:
#-----| -> 1
# 22| 1
#-----| match -> case ...
#-----| raise -> exit case_match_no_match (abnormal)
#-----| match -> case ...
# 26| case_match_raise
#-----| -> case_match_array
@@ -751,8 +751,8 @@ case.rb:
#-----| -> -> { ... }
# 28| -> { ... }
#-----| match -> case ...
#-----| raise -> exit case_match_raise (abnormal)
#-----| match -> case ...
# 28| enter -> { ... }
#-----| -> x
@@ -811,8 +811,8 @@ case.rb:
#-----| -> [ ..., * ]
# 35| [ ..., * ]
#-----| no-match -> in ... then ...
#-----| match -> x
#-----| no-match -> in ... then ...
# 35| x
#-----| match -> case ...
@@ -821,8 +821,8 @@ case.rb:
#-----| -> [ ..., * ]
# 36| [ ..., * ]
#-----| no-match -> in ... then ...
#-----| match -> x
#-----| no-match -> in ... then ...
# 36| x
#-----| match -> case ...
@@ -831,12 +831,12 @@ case.rb:
#-----| -> Bar
# 37| Bar
#-----| match -> [ ..., * ]
#-----| raise -> exit case_match_array (abnormal)
#-----| match -> [ ..., * ]
# 37| [ ..., * ]
#-----| match -> a
#-----| raise -> exit case_match_array (abnormal)
#-----| match -> a
# 37| a
#-----| match -> b
@@ -880,19 +880,19 @@ case.rb:
#-----| -> [ *,...,* ]
# 43| [ *,...,* ]
#-----| match -> x
#-----| raise -> exit case_match_find (abnormal)
#-----| match -> x
# 43| x
#-----| -> 1
# 43| 1
#-----| match -> 2
#-----| raise -> exit case_match_find (abnormal)
#-----| match -> 2
# 43| 2
#-----| match -> y
#-----| raise -> exit case_match_find (abnormal)
#-----| match -> y
# 43| y
#-----| -> case ...
@@ -931,12 +931,12 @@ case.rb:
#-----| no-match -> in ... then ...
# 49| { ..., ** }
#-----| no-match -> in ... then ...
#-----| match -> 1
#-----| no-match -> in ... then ...
# 49| 1
#-----| no-match -> in ... then ...
#-----| match -> a
#-----| no-match -> in ... then ...
# 49| a
#-----| match -> rest
@@ -952,8 +952,8 @@ case.rb:
#-----| no-match -> in ... then ...
# 50| { ..., ** }
#-----| no-match -> in ... then ...
#-----| match -> 1
#-----| no-match -> in ... then ...
# 50| 1
#-----| match -> case ...
@@ -963,12 +963,12 @@ case.rb:
#-----| -> Bar
# 51| Bar
#-----| match -> { ..., ** }
#-----| raise -> exit case_match_hash (abnormal)
#-----| match -> { ..., ** }
# 51| { ..., ** }
#-----| match -> case ...
#-----| raise -> exit case_match_hash (abnormal)
#-----| match -> case ...
# 55| case_match_variable
#-----| -> case_match_underscore
@@ -1186,8 +1186,8 @@ case.rb:
#-----| -> ... => ...
# 82| 5
#-----| no-match -> in ... then ...
#-----| match -> x
#-----| no-match -> in ... then ...
# 82| ... => ...
#-----| -> 5
@@ -1360,16 +1360,16 @@ case.rb:
#-----| -> 0
# 91| ""
#-----| no-match -> [ ..., * ]
#-----| match -> case ...
#-----| no-match -> [ ..., * ]
# 91| [ ..., * ]
#-----| no-match -> { ..., ** }
#-----| match -> case ...
#-----| no-match -> { ..., ** }
# 91| { ..., ** }
#-----| match -> case ...
#-----| raise -> exit case_match_various (abnormal)
#-----| match -> case ...
# 95| case_match_guard_no_else
#-----| -> exit case.rb (normal)
@@ -1404,8 +1404,8 @@ case.rb:
#-----| -> 5
# 97| ... == ...
#-----| true -> 6
#-----| raise -> exit case_match_guard_no_else (abnormal)
#-----| true -> 6
# 97| 5
#-----| -> ... == ...
@@ -1820,8 +1820,8 @@ cfg.rb:
#-----| -> 1
# 42| 1
#-----| no-match -> when ...
#-----| match -> self
#-----| no-match -> when ...
# 42| then ...
#-----| -> case ...
@@ -1893,8 +1893,8 @@ cfg.rb:
#-----| -> 1
# 48| ... == ...
#-----| false -> when ...
#-----| true -> self
#-----| false -> when ...
# 48| 1
#-----| -> ... == ...
@@ -2662,8 +2662,8 @@ cfg.rb:
#-----| -> 10
# 113| ... > ...
#-----| false -> ... if ...
#-----| true -> self
#-----| false -> ... if ...
# 113| 10
#-----| -> ... > ...
@@ -3246,8 +3246,8 @@ cfg.rb:
#-----| -> 0
# 174| ... == ...
#-----| true -> ... unless ...
#-----| false -> self
#-----| true -> ... unless ...
# 174| 0
#-----| -> ... == ...
@@ -4310,8 +4310,8 @@ ifs.rb:
#-----| -> 2
# 2| ... > ...
#-----| false -> x
#-----| true -> self
#-----| false -> x
# 2| 2
#-----| -> ... > ...
@@ -4772,8 +4772,8 @@ ifs.rb:
#-----| -> self
# 47| b
#-----| false -> else ...
#-----| true -> self
#-----| false -> else ...
# 47| then ...
#-----| -> if ...
@@ -5125,8 +5125,8 @@ loops.rb:
#-----| -> y
# 31| ... < ...
#-----| true -> do ...
#-----| false -> while ...
#-----| true -> do ...
# 31| y
#-----| -> ... < ...
@@ -5246,8 +5246,8 @@ raise.rb:
#-----| -> self
# 19| ExceptionA
#-----| match -> self
#-----| raise -> exit m2 (abnormal)
#-----| match -> self
# 19| then ...
#-----| -> rescue ...
@@ -5488,8 +5488,8 @@ raise.rb:
#-----| match -> e
# 62| ExceptionB
#-----| match -> e
#-----| raise -> exit m6 (abnormal)
#-----| match -> e
# 62| e
#-----| -> self
@@ -5545,8 +5545,8 @@ raise.rb:
#-----| -> 2
# 69| ... > ...
#-----| false -> x
#-----| true -> self
#-----| false -> x
#-----| raise -> [ensure: raise] self
# 69| 2
@@ -5588,8 +5588,8 @@ raise.rb:
#-----| -> "x < 0"
# 74| call to puts
#-----| -> self
#-----| raise -> [ensure: raise] self
#-----| -> self
# 74| self
#-----| -> 0 <= x <= 2
@@ -5681,8 +5681,8 @@ raise.rb:
#-----| -> 2
# 82| ... > ...
#-----| false -> x
#-----| true -> self
#-----| false -> x
#-----| raise -> [ensure: raise] self
# 82| 2
@@ -5724,8 +5724,8 @@ raise.rb:
#-----| -> "x < 0"
# 87| call to puts
#-----| -> self
#-----| raise -> [ensure: raise] self
#-----| -> self
# 87| self
#-----| -> 0 <= x <= 2
@@ -5836,8 +5836,8 @@ raise.rb:
#-----| -> 2
# 97| ... > ...
#-----| false -> x
#-----| true -> self
#-----| false -> x
#-----| raise -> [ensure: raise] self
# 97| 2
@@ -5879,8 +5879,8 @@ raise.rb:
#-----| -> "x < 0"
# 102| call to puts
#-----| -> self
#-----| raise -> [ensure: raise] self
#-----| -> self
# 102| self
#-----| -> 0 <= x <= 2
@@ -6093,8 +6093,8 @@ raise.rb:
#-----| -> "inner ensure"
# 113| call to puts
#-----| -> self
#-----| raise -> [ensure: raise] self
#-----| -> self
# 113| self
#-----| -> End m9
@@ -6465,8 +6465,8 @@ raise.rb:
#-----| -> call to nil?
# 155| call to nil?
#-----| false -> ... if ...
#-----| true -> self
#-----| false -> ... if ...
# 158| enter m15
#-----| -> self
@@ -6532,8 +6532,8 @@ raise.rb:
#-----| -> call to raise
# 161| x
#-----| true -> ... unless ...
#-----| false -> self
#-----| true -> ... unless ...
# 166| C
#-----| -> self

View File

@@ -0,0 +1,6 @@
classMethodCalls
| test1.rb:58:1:58:8 | Use getMember("M1").getMember("C1").getMethod("m").getReturn() |
| test1.rb:59:1:59:8 | Use getMember("M2").getMember("C3").getMethod("m").getReturn() |
instanceMethodCalls
| test1.rb:61:1:61:12 | Use getMember("M1").getMember("C1").getMethod("new").getReturn().getMethod("m").getReturn() |
| test1.rb:62:1:62:12 | Use getMember("M2").getMember("C3").getMethod("new").getReturn().getMethod("m").getReturn() |

View File

@@ -0,0 +1,13 @@
/**
* Tests of the public API of API Graphs
*/
import codeql.ruby.ApiGraphs
query predicate classMethodCalls(API::Node node) {
node = API::getTopLevelMember("M1").getMember("C1").getReturn("m")
}
query predicate instanceMethodCalls(API::Node node) {
node = API::getTopLevelMember("M1").getMember("C1").getInstance().getReturn("m")
}

View File

@@ -0,0 +1,39 @@
Something.foo.withCallback do |a, b| #$ use=getMember("Something").getMethod("foo").getReturn().getMethod("withCallback").getReturn()
a.something #$ use=getMember("Something").getMethod("foo").getReturn().getMethod("withCallback").getBlock().getParameter(0).getMethod("something").getReturn()
b.somethingElse #$ use=getMember("Something").getMethod("foo").getReturn().getMethod("withCallback").getBlock().getParameter(1).getMethod("somethingElse").getReturn()
end
Something.withNamedArg do |a:, b: nil| #$ use=getMember("Something").getMethod("withNamedArg").getReturn()
a.something #$ use=getMember("Something").getMethod("withNamedArg").getBlock().getKeywordParameter("a").getMethod("something").getReturn()
b.somethingElse #$ use=getMember("Something").getMethod("withNamedArg").getBlock().getKeywordParameter("b").getMethod("somethingElse").getReturn()
end
Something.withLambda ->(a, b) { #$ use=getMember("Something").getMethod("withLambda").getReturn()
a.something #$ use=getMember("Something").getMethod("withLambda").getParameter(0).getParameter(0).getMethod("something").getReturn()
b.something #$ use=getMember("Something").getMethod("withLambda").getParameter(0).getParameter(1).getMethod("something").getReturn()
}
Something.namedCallback( #$ use=getMember("Something").getMethod("namedCallback").getReturn()
onEvent: ->(a, b) {
a.something #$ use=getMember("Something").getMethod("namedCallback").getKeywordParameter("onEvent").getParameter(0).getMethod("something").getReturn()
b.something #$ use=getMember("Something").getMethod("namedCallback").getKeywordParameter("onEvent").getParameter(1).getMethod("something").getReturn()
}
)
Something.nestedCall1 do |a| #$ use=getMember("Something").getMethod("nestedCall1").getReturn()
a.nestedCall2 do |b:| #$ use=getMember("Something").getMethod("nestedCall1").getBlock().getParameter(0).getMethod("nestedCall2").getReturn()
b.something #$ use=getMember("Something").getMethod("nestedCall1").getBlock().getParameter(0).getMethod("nestedCall2").getBlock().getKeywordParameter("b").getMethod("something").getReturn()
end
end
def getCallback()
->(x) {
x.something #$ use=getMember("Something").getMethod("indirectCallback").getParameter(0).getParameter(0).getMethod("something").getReturn()
}
end
Something.indirectCallback(getCallback()) #$ use=getMember("Something").getMethod("indirectCallback").getReturn()
Something.withMixed do |a, *args, b| #$ use=getMember("Something").getMethod("withMixed").getReturn()
a.something #$ use=getMember("Something").getMethod("withMixed").getBlock().getParameter(0).getMethod("something").getReturn()
# b.something # not currently handled correctly
end

View File

@@ -1,20 +1,20 @@
MyModule #$ use=getMember("MyModule")
print MyModule.foo #$ use=getMember("MyModule").getReturn("foo")
Kernel.print(e) #$ use=getMember("Kernel").getReturn("print")
print MyModule.foo #$ use=getMember("MyModule").getMethod("foo").getReturn()
Kernel.print(e) #$ use=getMember("Kernel").getMethod("print").getReturn() def=getMember("Kernel").getMethod("print").getParameter(0)
Object::Kernel #$ use=getMember("Kernel")
Object::Kernel.print(e) #$ use=getMember("Kernel").getReturn("print")
Object::Kernel.print(e) #$ use=getMember("Kernel").getMethod("print").getReturn()
begin
print MyModule.bar #$ use=getMember("MyModule").getReturn("bar")
print MyModule.bar #$ use=getMember("MyModule").getMethod("bar").getReturn()
raise AttributeError #$ use=getMember("AttributeError")
rescue AttributeError => e #$ use=getMember("AttributeError")
Kernel.print(e) #$ use=getMember("Kernel").getReturn("print")
Kernel.print(e) #$ use=getMember("Kernel").getMethod("print").getReturn()
end
Unknown.new.run #$ use=getMember("Unknown").instance.getReturn("run")
Unknown.new.run #$ use=getMember("Unknown").getMethod("new").getReturn().getMethod("run").getReturn()
Foo::Bar::Baz #$ use=getMember("Foo").getMember("Bar").getMember("Baz")
Const = [1, 2, 3] #$ use=getMember("Array").getReturn("[]")
Const.each do |c| #$ use=getMember("Const").getReturn("each")
puts c
Const = [1, 2, 3] #$ use=getMember("Array").getMethod("[]").getReturn()
Const.each do |c| #$ use=getMember("Const").getMethod("each").getReturn() def=getMember("Const").getMethod("each").getBlock()
puts c #$ use=getMember("Const").getMethod("each").getBlock().getParameter(0)
end
foo = Foo #$ use=getMember("Foo")
@@ -28,4 +28,37 @@ module Outer
end
end
Outer::Inner.foo #$ use=getMember("Outer").getMember("Inner").getReturn("foo")
Outer::Inner.foo #$ use=getMember("Outer").getMember("Inner").getMethod("foo").getReturn()
module M1
class C1
def self.m
end
def m
end
end
end
class C2 < M1::C1 #$ use=getMember("M1").getMember("C1")
end
module M2
class C3 < M1::C1 #$ use=getMember("M1").getMember("C1")
end
class C4 < C2 #$ use=getMember("C2")
end
end
C2 #$ use=getMember("C2") use=getMember("M1").getMember("C1").getASubclass()
M2::C3 #$ use=getMember("M2").getMember("C3") use=getMember("M1").getMember("C1").getASubclass()
M2::C4 #$ use=getMember("M2").getMember("C4") use=getMember("C2").getASubclass() use=getMember("M1").getMember("C1").getASubclass().getASubclass()
M1::C1.m #$ use=getMember("M1").getMember("C1").getMethod("m").getReturn()
M2::C3.m #$ use=getMember("M2").getMember("C3").getMethod("m").getReturn() use=getMember("M1").getMember("C1").getASubclass().getMethod("m").getReturn()
M1::C1.new.m #$ use=getMember("M1").getMember("C1").getMethod("new").getReturn().getMethod("m").getReturn()
M2::C3.new.m #$ use=getMember("M2").getMember("C3").getMethod("new").getReturn().getMethod("m").getReturn()
Foo.foo(a,b:c) #$ use=getMember("Foo").getMethod("foo").getReturn() def=getMember("Foo").getMethod("foo").getParameter(0) def=getMember("Foo").getMethod("foo").getKeywordParameter("b")

View File

@@ -6,20 +6,26 @@ import codeql.ruby.ApiGraphs
class ApiUseTest extends InlineExpectationsTest {
ApiUseTest() { this = "ApiUseTest" }
override string getARelevantTag() { result = "use" }
override string getARelevantTag() { result = ["use", "def"] }
private predicate relevantNode(API::Node a, DataFlow::Node n, Location l) {
n = a.getAUse() and
l = n.getLocation()
private predicate relevantNode(API::Node a, DataFlow::Node n, Location l, string tag) {
l = n.getLocation() and
(
tag = "use" and
n = a.getAUse()
or
tag = "def" and
n = a.getARhs()
)
}
override predicate hasActualResult(Location location, string element, string tag, string value) {
exists(API::Node a, DataFlow::Node n | relevantNode(a, n, location) |
tag = "use" and
tag = "use" and // def tags are always optional
exists(API::Node a, DataFlow::Node n | relevantNode(a, n, location, tag) |
// Only report the longest path on this line:
value =
max(API::Node a2, Location l2, DataFlow::Node n2 |
relevantNode(a2, n2, l2) and
relevantNode(a2, n2, l2, tag) and
l2.getFile() = location.getFile() and
l2.getStartLine() = location.getStartLine()
|
@@ -30,6 +36,36 @@ class ApiUseTest extends InlineExpectationsTest {
element = n.toString()
)
}
// We also permit optional annotations for any other path on the line.
// This is used to test subclass paths, which typically have a shorter canonical path.
override predicate hasOptionalResult(Location location, string element, string tag, string value) {
exists(API::Node a, DataFlow::Node n | relevantNode(a, n, location, tag) |
element = n.toString() and
value = getAPath(a, _)
)
}
}
private int size(AstNode n) { not n instanceof StmtSequence and result = count(n.getAChild*()) }
/**
* Gets a path of the given `length` from the root to the given node.
* This is a copy of `API::getAPath()` without the restriction on path length,
* which would otherwise rule out paths involving `getASubclass()`.
*/
string getAPath(API::Node node, int length) {
node instanceof API::Root and
length = 0 and
result = ""
or
exists(API::Node pred, API::Label::ApiLabel lbl, string predpath |
pred.getASuccessor(lbl) = node and
predpath = getAPath(pred, length - 1) and
exists(string dot | if length = 1 then dot = "" else dot = "." |
result = predpath + dot + lbl and
// avoid producing strings longer than 1MB
result.length() < 1000 * 1000
)
)
}

View File

@@ -6,10 +6,6 @@ import ruby
import TestUtilities.InlineFlowTest
import PathGraph
class HasFlowTest extends InlineFlowTest {
override DataFlow::Configuration getTaintFlowConfig() { none() }
}
from DataFlow::PathNode source, DataFlow::PathNode sink, DefaultValueFlowConf conf
where conf.hasFlowPath(source, sink)
select sink, source, sink, "$@", source, source.toString()

File diff suppressed because it is too large Load Diff

View File

@@ -2,15 +2,26 @@ actionControllerControllerClasses
| ActiveRecordInjection.rb:27:1:58:3 | FooController |
| ActiveRecordInjection.rb:60:1:90:3 | BarController |
| ActiveRecordInjection.rb:92:1:96:3 | BazController |
| app/controllers/comments_controller.rb:1:1:7:3 | CommentsController |
| app/controllers/foo/bars_controller.rb:3:1:31:3 | BarsController |
| app/controllers/photos_controller.rb:1:1:4:3 | PhotosController |
| app/controllers/posts_controller.rb:1:1:10:3 | PostsController |
| app/controllers/users/notifications_controller.rb:2:3:5:5 | NotificationsController |
actionControllerActionMethods
| ActiveRecordInjection.rb:32:3:57:5 | some_request_handler |
| ActiveRecordInjection.rb:61:3:69:5 | some_other_request_handler |
| ActiveRecordInjection.rb:71:3:89:5 | safe_paths |
| ActiveRecordInjection.rb:93:3:95:5 | yet_another_handler |
| app/controllers/comments_controller.rb:2:3:3:5 | index |
| app/controllers/comments_controller.rb:5:3:6:5 | show |
| app/controllers/foo/bars_controller.rb:5:3:7:5 | index |
| app/controllers/foo/bars_controller.rb:9:3:18:5 | show_debug |
| app/controllers/foo/bars_controller.rb:20:3:24:5 | show |
| app/controllers/photos_controller.rb:2:3:3:5 | show |
| app/controllers/posts_controller.rb:2:3:3:5 | index |
| app/controllers/posts_controller.rb:5:3:6:5 | show |
| app/controllers/posts_controller.rb:8:3:9:5 | upvote |
| app/controllers/users/notifications_controller.rb:3:5:4:7 | mark_as_read |
paramsCalls
| ActiveRecordInjection.rb:35:30:35:35 | call to params |
| ActiveRecordInjection.rb:39:29:39:34 | call to params |

View File

@@ -0,0 +1,56 @@
actionDispatchRoutes
| app/config/routes.rb:2:3:8:5 | call to resources | get | posts | posts | index |
| app/config/routes.rb:2:3:8:5 | call to resources | get | posts/:id | posts | show |
| app/config/routes.rb:3:5:6:7 | call to resources | delete | posts/:post_id/comments/:id | comments | destroy |
| app/config/routes.rb:3:5:6:7 | call to resources | get | posts/:post_id/comments | comments | index |
| app/config/routes.rb:3:5:6:7 | call to resources | get | posts/:post_id/comments/:id | comments | show |
| app/config/routes.rb:3:5:6:7 | call to resources | get | posts/:post_id/comments/new | comments | new |
| app/config/routes.rb:3:5:6:7 | call to resources | get | posts/:post_id/comments:id/edit | comments | edit |
| app/config/routes.rb:3:5:6:7 | call to resources | patch | posts/:post_id/comments/:id | comments | update |
| app/config/routes.rb:3:5:6:7 | call to resources | post | posts/:post_id/comments | comments | create |
| app/config/routes.rb:3:5:6:7 | call to resources | put | posts/:post_id/comments/:id | comments | update |
| app/config/routes.rb:4:7:4:41 | call to resources | post | posts/:post_id/comments/:comment_id/replies | replies | create |
| app/config/routes.rb:5:7:5:28 | call to post | post | posts/:post_id/comments/:comment_id/flag | comments | flag |
| app/config/routes.rb:7:5:7:37 | call to post | post | posts/:post_id/upvote | posts | upvote |
| app/config/routes.rb:11:5:11:54 | call to post | post | destroy_all_posts | posts | destroy_alll |
| app/config/routes.rb:15:5:15:46 | call to get | get | numbers/:number | numbers | show |
| app/config/routes.rb:19:5:19:44 | call to get | get | admin/jobs | background_jobs | index |
| app/config/routes.rb:23:5:23:64 | call to get | get | admin/secrets | secrets | view_secrets |
| app/config/routes.rb:24:5:24:42 | call to delete | delete | admin/:user_id | users | destroy |
| app/config/routes.rb:27:3:27:48 | call to match | get | photos/:id | photos | show |
| app/config/routes.rb:28:3:28:50 | call to match | get | photos/:id | photos | show |
| app/config/routes.rb:29:3:29:69 | call to match | get | photos/:id | photos | show |
| app/config/routes.rb:30:3:30:50 | call to match | delete | photos/:id | photos | show |
| app/config/routes.rb:30:3:30:50 | call to match | get | photos/:id | photos | show |
| app/config/routes.rb:30:3:30:50 | call to match | patch | photos/:id | photos | show |
| app/config/routes.rb:30:3:30:50 | call to match | post | photos/:id | photos | show |
| app/config/routes.rb:30:3:30:50 | call to match | put | photos/:id | photos | show |
| app/config/routes.rb:33:5:33:43 | call to post | post | upgrade | users | start_upgrade |
| app/config/routes.rb:37:5:37:31 | call to get | get | current_billing_cycle | billing/enterprise | current_billing_cycle |
| app/config/routes.rb:40:3:40:40 | call to resource | get | global_config | global_config | show |
| app/config/routes.rb:43:5:45:7 | call to resources | get | foo/bar | foo/bar | index |
| app/config/routes.rb:43:5:45:7 | call to resources | get | foo/bar/:id | foo/bar | show |
| app/config/routes.rb:44:7:44:39 | call to get | get | foo/bar/:bar_id/show_debug | foo/bar | show_debug |
| app/config/routes.rb:49:5:49:95 | call to delete | delete | users/:user/notifications | users/notifications | destroy |
| app/config/routes.rb:50:5:50:94 | call to post | post | users/:user/notifications/:notification_id/mark_as_read | users/notifications | mark_as_read |
actionDispatchControllerMethods
| app/config/routes.rb:2:3:8:5 | call to resources | app/controllers/posts_controller.rb:2:3:3:5 | index |
| app/config/routes.rb:2:3:8:5 | call to resources | app/controllers/posts_controller.rb:5:3:6:5 | show |
| app/config/routes.rb:3:5:6:7 | call to resources | app/controllers/comments_controller.rb:2:3:3:5 | index |
| app/config/routes.rb:3:5:6:7 | call to resources | app/controllers/comments_controller.rb:5:3:6:5 | show |
| app/config/routes.rb:7:5:7:37 | call to post | app/controllers/posts_controller.rb:8:3:9:5 | upvote |
| app/config/routes.rb:27:3:27:48 | call to match | app/controllers/photos_controller.rb:2:3:3:5 | show |
| app/config/routes.rb:28:3:28:50 | call to match | app/controllers/photos_controller.rb:2:3:3:5 | show |
| app/config/routes.rb:29:3:29:69 | call to match | app/controllers/photos_controller.rb:2:3:3:5 | show |
| app/config/routes.rb:30:3:30:50 | call to match | app/controllers/photos_controller.rb:2:3:3:5 | show |
| app/config/routes.rb:50:5:50:94 | call to post | app/controllers/users/notifications_controller.rb:3:5:4:7 | mark_as_read |
underscore
| Foo | foo |
| Foo::Bar | foo/bar |
| Foo::Bar::Baz | foo/bar/baz |
| Foo::Bar::BazQuux | foo/bar/baz_quux |
| FooBar | foo_bar |
| FooBar::Baz | foo_bar/baz |
| HTTPServerRequest | httpserver_request |
| LotsOfCapitalLetters | lots_of_capital_letters |
| invalid | invalid |

View File

@@ -0,0 +1,26 @@
private import ruby
private import codeql.ruby.frameworks.ActionDispatch
private import codeql.ruby.frameworks.ActionController
query predicate actionDispatchRoutes(
ActionDispatch::Route r, string method, string path, string controller, string action
) {
r.getHttpMethod() = method and
r.getPath() = path and
r.getController() = controller and
r.getAction() = action
}
query predicate actionDispatchControllerMethods(
ActionDispatch::Route r, ActionControllerActionMethod m
) {
m.getARoute() = r
}
query predicate underscore(string input, string output) {
output = ActionDispatch::underscore(input) and
input in [
"Foo", "FooBar", "Foo::Bar", "FooBar::Baz", "Foo::Bar::Baz", "Foo::Bar::BazQuux", "invalid",
"HTTPServerRequest", "LotsOfCapitalLetters"
]
}

View File

@@ -93,4 +93,4 @@ class BazController < BarController
def yet_another_handler
Admin.delete_by(params[:admin_condition])
end
end
end

View File

@@ -0,0 +1,2 @@
| active_support.rb:1:1:1:22 | call to constantize | active_support.rb:1:1:1:10 | "Foo::Bar" |
| active_support.rb:3:1:3:13 | call to constantize | active_support.rb:3:1:3:1 | call to a |

View File

@@ -0,0 +1,5 @@
import codeql.ruby.frameworks.ActiveSupport
query DataFlow::Node constantizeCalls(ActiveSupport::CoreExtensions::String::Constantize c) {
result = c.getCode()
}

View File

@@ -0,0 +1,3 @@
"Foo::Bar".constantize
a.constantize

View File

@@ -0,0 +1,52 @@
Rails.application.routes.draw do
resources :posts, only: [:show, :index] do
resources :comments do
resources :replies, only: [:create]
post "flag", to: :flag
end
post "upvote", to: "posts#upvote"
end
if Rails.env.test?
post "destroy_all_posts", to: "posts#destroy_alll"
end
constraints(number: /[0-9]+/) do
get "/numbers/:number", to: "numbers#show"
end
scope path: "/admin" do
get "/jobs", to: "background_jobs#index"
end
scope "/admin" do
get "secrets", controller: "secrets", action: "view_secrets"
delete ":user_id", to: "users#destroy"
end
match "photos/:id" => "photos#show", via: :get
match "photos/:id", to: "photos#show", via: :get
match "photos/:id", controller: "photos", action: "show", via: :get
match "photos/:id", to: "photos#show", via: :all
scope controller: "users" do
post "upgrade", action: "start_upgrade"
end
scope module: "enterprise", controller: "billing" do
get "current_billing_cycle"
end
resource :global_config, only: [:show]
namespace :foo do
resources :bar, only: [:index, :show] do
get "show_debug", to: :show_debug
end
end
scope "/users/:user" do
delete "/notifications", to: "users/notifications#destroy", as: :user_destroy_notifications
post "notifications/:notification_id/mark_as_read", to: "users/notifications#mark_as_read"
end
end

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