Merge branch 'main' into expand-ruby-ssrf-sinks-faraday-connection-new

This commit is contained in:
thiggy1342
2022-10-20 16:37:57 -04:00
committed by GitHub
1259 changed files with 21429 additions and 57868 deletions

View File

@@ -3,7 +3,7 @@ description: Builds the Ruby CodeQL pack
runs:
using: composite
steps:
- uses: actions/cache@v2
- uses: actions/cache@v3
with:
path: |
~/.cargo/registry

View File

@@ -216,7 +216,7 @@ struct Visitor<'a> {
schema: &'a NodeTypeMap,
/// A stack for gathering information from child nodes. Whenever a node is
/// entered the parent's [Label], child counter, and an empty list is pushed.
/// All children append their data to the the list. When the visitor leaves a
/// All children append their data to the list. When the visitor leaves a
/// node the list containing the child data is popped from the stack and
/// matched against the dbscheme for the node. If the expectations are met
/// the corresponding row definitions are added to the trap_output.

View File

@@ -68,7 +68,6 @@ fn main() -> std::io::Result<()> {
.unwrap_or_else(|_| tracing_subscriber::EnvFilter::new("ruby_extractor=warn")),
)
.init();
tracing::warn!("Support for Ruby is currently in Beta: https://codeql.github.com/docs/codeql-overview/supported-languages-and-frameworks/");
let num_threads = num_codeql_threads();
tracing::info!(
"Using {} {}",

View File

@@ -43,7 +43,7 @@ pub enum FieldTypeInfo {
},
/// The field can be one of several tokens, so the db type will be an `int`
/// with a `case @foo.kind` for each possiblity.
/// with a `case @foo.kind` for each possibility.
ReservedWordInt(BTreeMap<String, (usize, String)>),
}

View File

@@ -0,0 +1,4 @@
---
category: minorAnalysis
---
* `ActiveJob::Serializers.deserialize` is considered to be a code execution sink.

View File

@@ -0,0 +1,5 @@
---
category: minorAnalysis
---
* More sources of remote input arising from methods on `ActionDispatch::Request`
are now recognised.

View File

@@ -0,0 +1,4 @@
---
category: minorAnalysis
---
* The response value returned by the `Faraday#run_request` method is now also considered a source of remote input.

View File

@@ -0,0 +1,4 @@
---
category: minorAnalysis
---
* The hashing algorithms from `Digest` and `OpenSSL::Digest` are now recognized and can be flagged by the `rb/weak-cryptographic-algorithm` query.

View File

@@ -203,7 +203,7 @@ module API {
/**
* Gets a node representing a call to `method` on the receiver represented by this node.
*/
Node getMethod(string method) {
MethodAccessNode getMethod(string method) {
result = this.getASubclass().getASuccessor(Label::method(method))
}
@@ -898,7 +898,7 @@ module API {
/** Gets the `subclass` edge label. */
LabelSubclass subclass() { any() }
/** Gets the label representing the given keword argument/parameter. */
/** Gets the label representing the given keyword argument/parameter. */
LabelKeywordParameter keywordParameter(string name) { result.getName() = name }
/** Gets the label representing the `n`th positional argument/parameter. */

View File

@@ -250,6 +250,11 @@ module Http {
/** Gets a string that identifies the framework used for this route setup. */
string getFramework() { result = super.getFramework() }
/**
* Gets the HTTP method name, in lowercase, that this handler will respond to.
*/
string getHttpMethod() { result = super.getHttpMethod() }
}
/** Provides a class for modeling new HTTP routing APIs. */
@@ -287,9 +292,34 @@ module Http {
/** Gets a string that identifies the framework used for this route setup. */
abstract string getFramework();
/**
* Gets the HTTP method name, in all caps, that this handler will respond to.
*/
abstract string getHttpMethod();
}
}
/** A kind of request input. */
class RequestInputKind extends string {
RequestInputKind() { this = ["parameter", "header", "body", "url", "cookie"] }
}
/** Input from the parameters of a request. */
RequestInputKind parameterInputKind() { result = "parameter" }
/** Input from the headers of a request. */
RequestInputKind headerInputKind() { result = "header" }
/** Input from the body of a request. */
RequestInputKind bodyInputKind() { result = "body" }
/** Input from the URL of a request. */
RequestInputKind urlInputKind() { result = "url" }
/** Input from the cookies of a request. */
RequestInputKind cookieInputKind() { result = "cookie" }
/**
* An access to a user-controlled HTTP request input. For example, the URL or body of a request.
* Instances of this class automatically become `RemoteFlowSource`s.
@@ -304,6 +334,32 @@ module Http {
* This is typically the name of the method that gives rise to this input.
*/
string getSourceType() { result = super.getSourceType() }
/**
* Gets the kind of the accessed input,
* Can be one of "parameter", "header", "body", "url", "cookie".
*/
RequestInputKind getKind() { result = super.getKind() }
/**
* Holds if this part of the request may be controlled by a third party,
* that is, an agent other than the one who sent the request.
*
* This is true for the URL, query parameters, and request body.
* These can be controlled by a malicious third party in the following scenarios:
*
* - The user clicks a malicious link or is otherwise redirected to a malicious URL.
* - The user visits a web site that initiates a form submission or AJAX request on their behalf.
*
* In these cases, the request is technically sent from the user's browser, but
* the user is not in direct control of the URL or POST body.
*
* Headers are never considered third-party controllable by this predicate, although the
* third party does have some control over the the Referer and Origin headers.
*/
predicate isThirdPartyControllable() {
this.getKind() = [parameterInputKind(), urlInputKind(), bodyInputKind()]
}
}
/** Provides a class for modeling new HTTP request inputs. */
@@ -321,6 +377,12 @@ module Http {
* This is typically the name of the method that gives rise to this input.
*/
abstract string getSourceType();
/**
* Gets the kind of the accessed input,
* Can be one of "parameter", "header", "body", "url", "cookie".
*/
abstract RequestInputKind getKind();
}
}
@@ -343,6 +405,12 @@ module Http {
/** Gets a string that identifies the framework used for this route setup. */
string getFramework() { result = super.getFramework() }
/**
* Gets an HTTP method name, in all caps, that this handler will respond to.
* Handlers can potentially respond to multiple HTTP methods.
*/
string getAnHttpMethod() { result = super.getAnHttpMethod() }
}
/** Provides a class for modeling new HTTP request handlers. */
@@ -364,6 +432,12 @@ module Http {
/** Gets a string that identifies the framework used for this request handler. */
abstract string getFramework();
/**
* Gets an HTTP method name, in all caps, that this handler will respond to.
* Handlers can potentially respond to multiple HTTP methods.
*/
abstract string getAnHttpMethod();
}
}
@@ -378,6 +452,8 @@ module Http {
}
override string getFramework() { result = rs.getFramework() }
override string getAnHttpMethod() { result = rs.getHttpMethod() }
}
/** A parameter that will receive parts of the url when handling an incoming request. */
@@ -387,6 +463,39 @@ module Http {
RoutedParameter() { this.getParameter() = handler.getARoutedParameter() }
override string getSourceType() { result = handler.getFramework() + " RoutedParameter" }
override RequestInputKind getKind() { result = parameterInputKind() }
}
/**
* A data flow node that writes data to a header in an HTTP response.
*
* Extend this class to refine existing API models. If you want to model new APIs,
* extend `HeaderWriteAccess::Range` instead.
*/
class HeaderWriteAccess extends DataFlow::Node instanceof HeaderWriteAccess::Range {
/** Gets the (lower case) name of the header that is written to. */
string getName() { result = super.getName().toLowerCase() }
/** Gets the value that is written to the header. */
DataFlow::Node getValue() { result = super.getValue() }
}
/** Provides a class for modeling new HTTP header writes. */
module HeaderWriteAccess {
/**
* A data flow node that writes data to the header in an HTTP response.
*
* Extend this class to model new APIs. If you want to refine existing API models,
* extend `HeaderWriteAccess` instead.
*/
abstract class Range extends DataFlow::Node {
/** Gets the name of the header that is written to. */
abstract string getName();
/** Gets the value that is written to the header. */
abstract DataFlow::Node getValue();
}
}
/**

View File

@@ -5,6 +5,7 @@
private import codeql.ruby.frameworks.Core
private import codeql.ruby.frameworks.ActionCable
private import codeql.ruby.frameworks.ActionController
private import codeql.ruby.frameworks.ActiveJob
private import codeql.ruby.frameworks.ActionMailer
private import codeql.ruby.frameworks.ActiveRecord
private import codeql.ruby.frameworks.ActiveResource

View File

@@ -106,7 +106,7 @@ class MethodCall extends Call instanceof MethodCallImpl {
final Block getBlock() { result = super.getBlockImpl() }
/**
* Holds if the safe nagivation operator (`&.`) is used in this call.
* Holds if the safe navigation operator (`&.`) is used in this call.
* ```rb
* foo&.empty?
* ```

View File

@@ -65,7 +65,7 @@ class ConstantValue extends TConstantValue {
/** Holds if this is the string value `s`. */
predicate isString(string s) { s = this.getString() }
/** Gets the symbol value (exluding the `:` prefix), if this is a symbol. */
/** Gets the symbol value (excluding the `:` prefix), if this is a symbol. */
string getSymbol() { this = TSymbol(result) }
/** Holds if this is the symbol value `:s`. */

View File

@@ -138,9 +138,26 @@ private module Cached {
cached
string resolveConstantWrite(ConstantWriteAccess c) { result = resolveConstantWriteAccess(c) }
/**
* Gets a method named `name` that is available in module `m`. This includes methods
* that are included/prepended into `m` and methods available on base classes of `m`.
*/
cached
Method lookupMethod(Module m, string name) { TMethod(result) = lookupMethodOrConst(m, name) }
/**
* Gets a method named `name` that is available in a sub class of module `m`. This
* includes methods that are included/prepended into any of the sub classes of `m`,
* but not methods inherited from base classes.
*/
cached
Method lookupMethodInSubClasses(Module m, string name) {
exists(Module sub | sub.getSuperClass() = m |
TMethod(result) = lookupMethodOrConst0(sub, name) or
result = lookupMethodInSubClasses(sub, name)
)
}
cached
Expr lookupConst(Module m, string name) {
TExpr(result) = lookupMethodOrConst(m, name)
@@ -394,7 +411,7 @@ private module ResolveImpl {
/**
* The qualified names of the ancestors of a class/module. The ancestors should be an ordered list
* of the ancestores of `prepend`ed modules, the module itself , the ancestors or `include`d modules
* of the ancestors of `prepend`ed modules, the module itself , the ancestors or `include`d modules
* and the ancestors of the super class. The priority value only distinguishes the kind of ancestor,
* it does not order the ancestors within a group of the same kind. This is an over-approximation, however,
* computing the precise order is tricky because it depends on the evaluation/file loading order.

View File

@@ -676,7 +676,9 @@ private class SelfVariableAccessReal extends SelfVariableAccessImpl, TSelfReal {
private SelfVariable var;
SelfVariableAccessReal() {
exists(Ruby::Self self | this = TSelfReal(self) and var = TSelfVariable(scopeOf(self)))
exists(Ruby::Self self |
this = TSelfReal(self) and var = TSelfVariable(scopeOf(self).getEnclosingSelfScope())
)
}
final override SelfVariable getVariableImpl() { result = var }

View File

@@ -687,6 +687,15 @@ module ExprNodes {
final ExprCfgNode getValue() { e.hasCfgChild(e.getValue(), this, result) }
}
/** A control-flow node that wraps a `VariableAccess` AST expression. */
class VariableAccessCfgNode extends ExprCfgNode {
override string getAPrimaryQlClass() { result = "VariableAccessCfgNode" }
override VariableAccess e;
final override VariableAccess getExpr() { result = ExprCfgNode.super.getExpr() }
}
/** A control-flow node that wraps a `VariableReadAccess` AST expression. */
class VariableReadAccessCfgNode extends ExprCfgNode {
override string getAPrimaryQlClass() { result = "VariableReadAccessCfgNode" }

View File

@@ -885,7 +885,7 @@ module TestOutput {
/**
* Gets a string used to resolve ties in node and edge ordering.
*/
string getOrderDisambuigation() { result = "" }
string getOrderDisambiguation() { result = "" }
}
query predicate nodes(RelevantNode n, string attr, string val) {
@@ -900,7 +900,7 @@ module TestOutput {
order by
l.getFile().getBaseName(), l.getFile().getAbsolutePath(), l.getStartLine(),
l.getStartColumn(), l.getEndLine(), l.getEndColumn(), p.toString(),
p.getOrderDisambuigation()
p.getOrderDisambiguation()
)
).toString()
}
@@ -923,7 +923,7 @@ module TestOutput {
order by
l.getFile().getBaseName(), l.getFile().getAbsolutePath(), l.getStartLine(),
l.getStartColumn(), l.getEndLine(), l.getEndColumn(), t.toString(), s.toString(),
s.getOrderDisambuigation()
s.getOrderDisambiguation()
)
).toString()
}

View File

@@ -46,7 +46,7 @@ module SummaryComponent {
/**
* Gets a summary component that represents an element in a collection at a specific
* known index `cv`, or an uknown index.
* known index `cv`, or an unknown index.
*/
SummaryComponent elementKnownOrUnknown(ConstantValue cv) {
result = SC::content(TKnownOrUnknownElementContent(TKnownElementContent(cv)))

View File

@@ -1,5 +1,5 @@
/**
* Provides an extension point for for modeling user-controlled data.
* Provides an extension point for modeling user-controlled data.
* Such data is often used as data-flow sources in security queries.
*/

View File

@@ -151,17 +151,24 @@ private class NormalCall extends DataFlowCall, TNormalCall {
override Location getLocation() { result = c.getLocation() }
}
/** A call for which we want to compute call targets. */
private class RelevantCall extends CfgNodes::ExprNodes::CallCfgNode {
pragma[nomagic]
RelevantCall() {
// Temporarily disable operation resolution (due to bad performance)
not this.getExpr() instanceof Operation
}
}
pragma[nomagic]
private predicate methodCall(
CfgNodes::ExprNodes::CallCfgNode call, DataFlow::Node receiver, string method
) {
private predicate methodCall(RelevantCall call, DataFlow::Node receiver, string method) {
method = call.getExpr().(MethodCall).getMethodName() and
receiver.asExpr() = call.getReceiver()
}
pragma[nomagic]
private predicate flowsToMethodCall(
CfgNodes::ExprNodes::CallCfgNode call, DataFlow::LocalSourceNode sourceNode, string method
private predicate flowsToMethodCallReceiver(
RelevantCall call, DataFlow::LocalSourceNode sourceNode, string method
) {
exists(DataFlow::Node receiver |
methodCall(call, receiver, method) and
@@ -169,7 +176,12 @@ private predicate flowsToMethodCall(
)
}
private Block yieldCall(CfgNodes::ExprNodes::CallCfgNode call) {
pragma[nomagic]
private predicate moduleFlowsToMethodCallReceiver(RelevantCall call, Module m, string method) {
flowsToMethodCallReceiver(call, trackModuleAccess(m), method)
}
private Block yieldCall(RelevantCall call) {
call.getExpr() instanceof YieldCall and
exists(BlockParameterNode node |
node = trackBlock(result) and
@@ -178,7 +190,7 @@ private Block yieldCall(CfgNodes::ExprNodes::CallCfgNode call) {
}
pragma[nomagic]
private predicate superCall(CfgNodes::ExprNodes::CallCfgNode call, Module superClass, string method) {
private predicate superCall(RelevantCall call, Module superClass, string method) {
call.getExpr() instanceof SuperCall and
exists(Module tp |
tp = call.getExpr().getEnclosingModule().getModule() and
@@ -187,20 +199,6 @@ private predicate superCall(CfgNodes::ExprNodes::CallCfgNode call, Module superC
)
}
pragma[nomagic]
private predicate instanceMethodCall(CfgNodes::ExprNodes::CallCfgNode call, Module tp, string method) {
exists(DataFlow::Node receiver, Module m, boolean exact |
methodCall(call, receiver, method) and
receiver = trackInstance(m, exact)
|
tp = m
or
// When we don't know the exact type, it could be any sub class
exact = false and
tp.getSuperClass+() = m
)
}
/** Holds if `self` belongs to module `m`. */
pragma[nomagic]
private predicate selfInModule(SelfVariable self, Module m) {
@@ -318,6 +316,19 @@ private predicate extendCallModule(Module m, Module n) {
)
}
/**
* Gets a method available in module `m`, or in one of `m`'s transitive
* sub classes when `exact = false`.
*/
pragma[nomagic]
private Method lookupMethod(Module m, string name, boolean exact) {
result = lookupMethod(m, name) and
exact in [false, true]
or
result = lookupMethodInSubClasses(m, name) and
exact = false
}
cached
private module Cached {
cached
@@ -332,100 +343,139 @@ private module Cached {
FlowSummaryImpl::Private::summaryCallbackRange(c, receiver)
}
cached
CfgScope getTarget(CfgNodes::ExprNodes::CallCfgNode call) {
// Temporarily disable operation resolution (due to bad performance)
not call.getExpr() instanceof Operation and
(
exists(string method |
exists(Module tp |
instanceMethodCall(call, tp, method) and
result = lookupMethod(tp, method) and
(
if result.(Method).isPrivate()
then
call.getReceiver().getExpr() instanceof SelfVariableAccess and
// For now, we restrict the scope of top-level declarations to their file.
// This may remove some plausible targets, but also removes a lot of
// implausible targets
if result.getEnclosingModule() instanceof Toplevel
then result.getFile() = call.getFile()
else any()
else any()
) and
if result.(Method).isProtected()
then result = lookupMethod(call.getExpr().getEnclosingModule().getModule(), method)
else any()
)
or
// singleton method defined on an instance, e.g.
// ```rb
// c = C.new
// def c.singleton; end # <- result
// c.singleton # <- call
// ```
// or an `extend`ed instance, e.g.
// ```rb
// c = C.new
// module M
// def instance; end # <- result
// end
// c.extend M
// c.instance # <- call
// ```
exists(DataFlow::Node receiver |
methodCall(call, receiver, method) and
receiver = trackSingletonMethodOnInstance(result, method)
)
or
// singleton method defined on a module
// or an `extend`ed module, e.g.
// ```rb
// module M
// def instance; end # <- result
// end
// M.extend(M)
// M.instance # <- call
// ```
exists(DataFlow::Node sourceNode, Module m |
flowsToMethodCall(call, sourceNode, method) and
result = lookupSingletonMethod(m, method)
|
// ```rb
// def C.singleton; end # <- result
// C.singleton # <- call
// ```
sourceNode = trackModuleAccess(m)
or
// ```rb
// class C
// def self.singleton; end # <- result
// self.singleton # <- call
// end
// ```
selfInModule(sourceNode.(SsaSelfDefinitionNode).getVariable(), m)
or
// ```rb
// class C
// def self.singleton; end # <- result
// def self.other
// self.singleton # <- call
// end
// end
// ```
selfInMethod(sourceNode.(SsaSelfDefinitionNode).getVariable(), _, m.getSuperClass*())
)
)
or
exists(Module superClass, string method |
superCall(call, superClass, method) and
result = lookupMethod(superClass, method)
)
or
result = yieldCall(call)
pragma[nomagic]
private Method lookupInstanceMethodCall(RelevantCall call, string method, boolean exact) {
exists(Module tp, DataFlow::Node receiver |
methodCall(call, pragma[only_bind_into](receiver), pragma[only_bind_into](method)) and
receiver = trackInstance(tp, exact) and
result = lookupMethod(tp, pragma[only_bind_into](method), exact)
)
}
pragma[nomagic]
private predicate isToplevelMethodInFile(Method m, File f) {
m.getEnclosingModule() instanceof Toplevel and
f = m.getFile()
}
/** Holds if a `self` access may be the receiver of `call` directly inside module `m`. */
pragma[nomagic]
private predicate selfInModuleFlowsToMethodCallReceiver(RelevantCall call, Module m, string method) {
exists(SsaSelfDefinitionNode self |
flowsToMethodCallReceiver(call, self, method) and
selfInModule(self.getVariable(), m)
)
}
/**
* Holds if a `self` access may be the receiver of `call` inside some singleton method, where
* that method belongs to `m` or one of `m`'s transitive super classes.
*/
pragma[nomagic]
private predicate selfInSingletonMethodFlowsToMethodCallReceiver(
RelevantCall call, Module m, string method
) {
exists(SsaSelfDefinitionNode self, Module target, MethodBase caller |
flowsToMethodCallReceiver(call, self, method) and
target = m.getSuperClass*() and
selfInMethod(self.getVariable(), caller, target) and
singletonMethod(caller, _, _) and
// Singleton methods declared in a block in the top-level may spuriously end up being seen as singleton
// methods on Object, if the block is actually evaluated in the context of another class.
// The 'self' inside such a singleton method could then be any class, leading to self-calls
// being resolved to arbitrary singleton methods.
// To remedy this, we do not allow following super-classes all the way to Object.
not (m != target and target = TResolved("Object"))
)
}
cached
CfgScope getTarget(RelevantCall call) {
exists(string method |
exists(boolean exact |
result = lookupInstanceMethodCall(call, method, exact) and
(
if result.(Method).isPrivate()
then
call.getReceiver().getExpr() instanceof SelfVariableAccess and
// For now, we restrict the scope of top-level declarations to their file.
// This may remove some plausible targets, but also removes a lot of
// implausible targets
(
isToplevelMethodInFile(result, call.getFile()) or
not isToplevelMethodInFile(result, _)
)
else any()
) and
if result.(Method).isProtected()
then result = lookupMethod(call.getExpr().getEnclosingModule().getModule(), method, exact)
else any()
)
or
// singleton method defined on an instance, e.g.
// ```rb
// c = C.new
// def c.singleton; end # <- result
// c.singleton # <- call
// ```
// or an `extend`ed instance, e.g.
// ```rb
// c = C.new
// module M
// def instance; end # <- result
// end
// c.extend M
// c.instance # <- call
// ```
exists(DataFlow::Node receiver |
methodCall(call, receiver, method) and
receiver = trackSingletonMethodOnInstance(result, method)
)
or
// singleton method defined on a module
// or an `extend`ed module, e.g.
// ```rb
// module M
// def instance; end # <- result
// end
// M.extend(M)
// M.instance # <- call
// ```
exists(Module m | result = lookupSingletonMethod(m, method) |
// ```rb
// def C.singleton; end # <- result
// C.singleton # <- call
// ```
moduleFlowsToMethodCallReceiver(call, m, method)
or
// ```rb
// class C
// def self.singleton; end # <- result
// self.singleton # <- call
// end
// ```
selfInModuleFlowsToMethodCallReceiver(call, m, method)
or
// ```rb
// class C
// def self.singleton; end # <- result
// def self.other
// self.singleton # <- call
// end
// end
// ```
selfInSingletonMethodFlowsToMethodCallReceiver(call, m, method)
)
)
or
exists(Module superClass, string method |
superCall(call, superClass, method) and
result = lookupMethod(superClass, method)
)
or
result = yieldCall(call)
}
/** Gets a viable run-time target for the call `call`. */
cached
DataFlowCallable viableCallable(DataFlowCall call) {
@@ -487,9 +537,14 @@ private DataFlow::LocalSourceNode trackModuleAccess(Module m, TypeTracker t) {
)
}
/**
* We exclude steps into `self` parameters, and instead rely on the type of the
* enclosing module.
*/
pragma[nomagic]
private DataFlow::LocalSourceNode trackModuleAccessRec(Module m, TypeTracker t, StepSummary summary) {
StepSummary::step(trackModuleAccess(m, t), result, summary)
StepSummary::step(trackModuleAccess(m, t), result, summary) and
not result instanceof SelfParameterNode
}
pragma[nomagic]
@@ -551,19 +606,24 @@ private predicate isInstance(DataFlow::Node n, Module tp, boolean exact) {
tp = TResolved("Proc") and
exact = true
or
exists(CfgNodes::ExprNodes::CallCfgNode call, DataFlow::LocalSourceNode sourceNode |
flowsToMethodCall(call, sourceNode, "new") and
exact = true and
exists(RelevantCall call, DataFlow::LocalSourceNode sourceNode |
flowsToMethodCallReceiver(call, sourceNode, "new") and
n.asExpr() = call
|
// `C.new`
sourceNode = trackModuleAccess(tp)
sourceNode = trackModuleAccess(tp) and
exact = true
or
// `self.new` inside a module
selfInModule(sourceNode.(SsaSelfDefinitionNode).getVariable(), tp)
selfInModule(sourceNode.(SsaSelfDefinitionNode).getVariable(), tp) and
exact = true
or
// `self.new` inside a singleton method
selfInMethod(sourceNode.(SsaSelfDefinitionNode).getVariable(), any(SingletonMethod sm), tp)
exists(MethodBase target |
selfInMethod(sourceNode.(SsaSelfDefinitionNode).getVariable(), target, tp) and
singletonMethod(target, _, _) and
exact = false
)
)
or
// `self` reference in method or top-level (but not in module or singleton method,
@@ -834,10 +894,7 @@ pragma[nomagic]
private predicate paramReturnFlow(
DataFlow::Node nodeFrom, DataFlow::PostUpdateNode nodeTo, StepSummary summary
) {
exists(
CfgNodes::ExprNodes::CallCfgNode call, DataFlow::Node arg, DataFlow::ParameterNode p,
Expr nodeFromPreExpr
|
exists(RelevantCall call, DataFlow::Node arg, DataFlow::ParameterNode p, Expr nodeFromPreExpr |
TypeTrackerSpecific::callStep(call, arg, p) and
nodeTo.getPreUpdateNode() = arg and
summary.toString() = "return" and
@@ -911,8 +968,7 @@ private predicate isInstanceLocalMustFlow(DataFlow::Node n, Module tp, boolean e
*/
pragma[nomagic]
private predicate mayBenefitFromCallContext0(
CfgNodes::ExprNodes::CallCfgNode ctx, ArgumentNode arg, CfgNodes::ExprNodes::CallCfgNode call,
Callable encl, string name
RelevantCall ctx, ArgumentNode arg, RelevantCall call, Callable encl, string name
) {
exists(
ParameterNodeImpl p, SsaDefinitionNode ssaNode, ParameterPosition ppos, ArgumentPosition apos
@@ -920,7 +976,7 @@ private predicate mayBenefitFromCallContext0(
// the receiver of `call` references `p`
ssaNode = trackInstance(_, _) and
LocalFlow::localFlowSsaParamInput(p, ssaNode) and
flowsToMethodCall(pragma[only_bind_into](call), pragma[only_bind_into](ssaNode),
flowsToMethodCallReceiver(pragma[only_bind_into](call), pragma[only_bind_into](ssaNode),
pragma[only_bind_into](name)) and
// `p` is a parameter of `encl`,
encl = call.getScope() and
@@ -943,8 +999,7 @@ private predicate mayBenefitFromCallContext0(
*/
pragma[nomagic]
private predicate mayBenefitFromCallContext1(
CfgNodes::ExprNodes::CallCfgNode ctx, CfgNodes::ExprNodes::CallCfgNode call, Callable encl,
Module tp, boolean exact, string name
RelevantCall ctx, RelevantCall call, Callable encl, Module tp, boolean exact, string name
) {
exists(ArgumentNode arg |
mayBenefitFromCallContext0(ctx, pragma[only_bind_into](arg), call, encl,
@@ -972,19 +1027,14 @@ predicate mayBenefitFromCallContext(DataFlowCall call, DataFlowCallable c) {
pragma[nomagic]
DataFlowCallable viableImplInCallContext(DataFlowCall call, DataFlowCall ctx) {
// `ctx` can provide a potentially better type bound
exists(CfgNodes::ExprNodes::CallCfgNode call0, Callable res |
exists(RelevantCall call0, Callable res |
call0 = call.asCall() and
res = result.asCallable() and
res = getTarget(call0) and // make sure to not include e.g. private methods
exists(Module tp, Module m, boolean exact, string name |
res = lookupMethod(tp, name) and
exists(Module m, boolean exact, string name |
res = lookupMethod(m, name, exact) and
mayBenefitFromCallContext1(ctx.asCall(), pragma[only_bind_into](call0), _,
pragma[only_bind_into](m), exact, pragma[only_bind_into](name))
|
tp = m
or
exact = false and
tp.getSuperClass+() = m
)
)
or

View File

@@ -838,13 +838,13 @@ private module Stage1 implements StageSig {
* by `revFlow`.
*/
pragma[nomagic]
predicate revFlowIsReadAndStored(Content c, Configuration conf) {
additional predicate revFlowIsReadAndStored(Content c, Configuration conf) {
revFlowConsCand(c, conf) and
revFlowStore(c, _, _, conf)
}
pragma[nomagic]
predicate viableReturnPosOutNodeCandFwd1(
additional predicate viableReturnPosOutNodeCandFwd1(
DataFlowCall call, ReturnPosition pos, NodeEx out, Configuration config
) {
fwdFlowReturnPosition(pos, _, config) and
@@ -860,7 +860,7 @@ private module Stage1 implements StageSig {
}
pragma[nomagic]
predicate viableParamArgNodeCandFwd1(
additional predicate viableParamArgNodeCandFwd1(
DataFlowCall call, ParamNodeEx p, ArgNodeEx arg, Configuration config
) {
viableParamArgEx(call, p, arg) and
@@ -907,7 +907,7 @@ private module Stage1 implements StageSig {
)
}
predicate revFlowState(FlowState state, Configuration config) {
additional predicate revFlowState(FlowState state, Configuration config) {
exists(NodeEx node |
sinkNode(node, state, config) and
revFlow(node, _, pragma[only_bind_into](config)) and
@@ -999,7 +999,7 @@ private module Stage1 implements StageSig {
)
}
predicate stats(
additional predicate stats(
boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config
) {
fwd = true and
@@ -1260,7 +1260,7 @@ private module MkStage<StageSig PrevStage> {
* argument.
*/
pragma[nomagic]
predicate fwdFlow(
additional predicate fwdFlow(
NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config
) {
fwdFlow0(node, state, cc, argAp, ap, config) and
@@ -1484,7 +1484,7 @@ private module MkStage<StageSig PrevStage> {
* the access path of the returned value.
*/
pragma[nomagic]
predicate revFlow(
additional predicate revFlow(
NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
) {
revFlow0(node, state, toReturn, returnAp, ap, config) and
@@ -1662,7 +1662,7 @@ private module MkStage<StageSig PrevStage> {
)
}
predicate revFlow(NodeEx node, FlowState state, Configuration config) {
additional predicate revFlow(NodeEx node, FlowState state, Configuration config) {
revFlow(node, state, _, _, _, config)
}
@@ -1675,11 +1675,13 @@ private module MkStage<StageSig PrevStage> {
// use an alias as a workaround for bad functionality-induced joins
pragma[nomagic]
predicate revFlowAlias(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) }
additional predicate revFlowAlias(NodeEx node, Configuration config) {
revFlow(node, _, _, _, _, config)
}
// use an alias as a workaround for bad functionality-induced joins
pragma[nomagic]
predicate revFlowAlias(NodeEx node, FlowState state, Ap ap, Configuration config) {
additional predicate revFlowAlias(NodeEx node, FlowState state, Ap ap, Configuration config) {
revFlow(node, state, ap, config)
}
@@ -1700,7 +1702,7 @@ private module MkStage<StageSig PrevStage> {
)
}
predicate consCand(TypedContent tc, Ap ap, Configuration config) {
additional predicate consCand(TypedContent tc, Ap ap, Configuration config) {
revConsCand(tc, ap, config) and
validAp(ap, config)
}
@@ -1742,7 +1744,7 @@ private module MkStage<StageSig PrevStage> {
)
}
predicate stats(
additional predicate stats(
boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config
) {
fwd = true and

View File

@@ -838,13 +838,13 @@ private module Stage1 implements StageSig {
* by `revFlow`.
*/
pragma[nomagic]
predicate revFlowIsReadAndStored(Content c, Configuration conf) {
additional predicate revFlowIsReadAndStored(Content c, Configuration conf) {
revFlowConsCand(c, conf) and
revFlowStore(c, _, _, conf)
}
pragma[nomagic]
predicate viableReturnPosOutNodeCandFwd1(
additional predicate viableReturnPosOutNodeCandFwd1(
DataFlowCall call, ReturnPosition pos, NodeEx out, Configuration config
) {
fwdFlowReturnPosition(pos, _, config) and
@@ -860,7 +860,7 @@ private module Stage1 implements StageSig {
}
pragma[nomagic]
predicate viableParamArgNodeCandFwd1(
additional predicate viableParamArgNodeCandFwd1(
DataFlowCall call, ParamNodeEx p, ArgNodeEx arg, Configuration config
) {
viableParamArgEx(call, p, arg) and
@@ -907,7 +907,7 @@ private module Stage1 implements StageSig {
)
}
predicate revFlowState(FlowState state, Configuration config) {
additional predicate revFlowState(FlowState state, Configuration config) {
exists(NodeEx node |
sinkNode(node, state, config) and
revFlow(node, _, pragma[only_bind_into](config)) and
@@ -999,7 +999,7 @@ private module Stage1 implements StageSig {
)
}
predicate stats(
additional predicate stats(
boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config
) {
fwd = true and
@@ -1260,7 +1260,7 @@ private module MkStage<StageSig PrevStage> {
* argument.
*/
pragma[nomagic]
predicate fwdFlow(
additional predicate fwdFlow(
NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config
) {
fwdFlow0(node, state, cc, argAp, ap, config) and
@@ -1484,7 +1484,7 @@ private module MkStage<StageSig PrevStage> {
* the access path of the returned value.
*/
pragma[nomagic]
predicate revFlow(
additional predicate revFlow(
NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
) {
revFlow0(node, state, toReturn, returnAp, ap, config) and
@@ -1662,7 +1662,7 @@ private module MkStage<StageSig PrevStage> {
)
}
predicate revFlow(NodeEx node, FlowState state, Configuration config) {
additional predicate revFlow(NodeEx node, FlowState state, Configuration config) {
revFlow(node, state, _, _, _, config)
}
@@ -1675,11 +1675,13 @@ private module MkStage<StageSig PrevStage> {
// use an alias as a workaround for bad functionality-induced joins
pragma[nomagic]
predicate revFlowAlias(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) }
additional predicate revFlowAlias(NodeEx node, Configuration config) {
revFlow(node, _, _, _, _, config)
}
// use an alias as a workaround for bad functionality-induced joins
pragma[nomagic]
predicate revFlowAlias(NodeEx node, FlowState state, Ap ap, Configuration config) {
additional predicate revFlowAlias(NodeEx node, FlowState state, Ap ap, Configuration config) {
revFlow(node, state, ap, config)
}
@@ -1700,7 +1702,7 @@ private module MkStage<StageSig PrevStage> {
)
}
predicate consCand(TypedContent tc, Ap ap, Configuration config) {
additional predicate consCand(TypedContent tc, Ap ap, Configuration config) {
revConsCand(tc, ap, config) and
validAp(ap, config)
}
@@ -1742,7 +1744,7 @@ private module MkStage<StageSig PrevStage> {
)
}
predicate stats(
additional predicate stats(
boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config
) {
fwd = true and

View File

@@ -838,13 +838,13 @@ private module Stage1 implements StageSig {
* by `revFlow`.
*/
pragma[nomagic]
predicate revFlowIsReadAndStored(Content c, Configuration conf) {
additional predicate revFlowIsReadAndStored(Content c, Configuration conf) {
revFlowConsCand(c, conf) and
revFlowStore(c, _, _, conf)
}
pragma[nomagic]
predicate viableReturnPosOutNodeCandFwd1(
additional predicate viableReturnPosOutNodeCandFwd1(
DataFlowCall call, ReturnPosition pos, NodeEx out, Configuration config
) {
fwdFlowReturnPosition(pos, _, config) and
@@ -860,7 +860,7 @@ private module Stage1 implements StageSig {
}
pragma[nomagic]
predicate viableParamArgNodeCandFwd1(
additional predicate viableParamArgNodeCandFwd1(
DataFlowCall call, ParamNodeEx p, ArgNodeEx arg, Configuration config
) {
viableParamArgEx(call, p, arg) and
@@ -907,7 +907,7 @@ private module Stage1 implements StageSig {
)
}
predicate revFlowState(FlowState state, Configuration config) {
additional predicate revFlowState(FlowState state, Configuration config) {
exists(NodeEx node |
sinkNode(node, state, config) and
revFlow(node, _, pragma[only_bind_into](config)) and
@@ -999,7 +999,7 @@ private module Stage1 implements StageSig {
)
}
predicate stats(
additional predicate stats(
boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config
) {
fwd = true and
@@ -1260,7 +1260,7 @@ private module MkStage<StageSig PrevStage> {
* argument.
*/
pragma[nomagic]
predicate fwdFlow(
additional predicate fwdFlow(
NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config
) {
fwdFlow0(node, state, cc, argAp, ap, config) and
@@ -1484,7 +1484,7 @@ private module MkStage<StageSig PrevStage> {
* the access path of the returned value.
*/
pragma[nomagic]
predicate revFlow(
additional predicate revFlow(
NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
) {
revFlow0(node, state, toReturn, returnAp, ap, config) and
@@ -1662,7 +1662,7 @@ private module MkStage<StageSig PrevStage> {
)
}
predicate revFlow(NodeEx node, FlowState state, Configuration config) {
additional predicate revFlow(NodeEx node, FlowState state, Configuration config) {
revFlow(node, state, _, _, _, config)
}
@@ -1675,11 +1675,13 @@ private module MkStage<StageSig PrevStage> {
// use an alias as a workaround for bad functionality-induced joins
pragma[nomagic]
predicate revFlowAlias(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) }
additional predicate revFlowAlias(NodeEx node, Configuration config) {
revFlow(node, _, _, _, _, config)
}
// use an alias as a workaround for bad functionality-induced joins
pragma[nomagic]
predicate revFlowAlias(NodeEx node, FlowState state, Ap ap, Configuration config) {
additional predicate revFlowAlias(NodeEx node, FlowState state, Ap ap, Configuration config) {
revFlow(node, state, ap, config)
}
@@ -1700,7 +1702,7 @@ private module MkStage<StageSig PrevStage> {
)
}
predicate consCand(TypedContent tc, Ap ap, Configuration config) {
additional predicate consCand(TypedContent tc, Ap ap, Configuration config) {
revConsCand(tc, ap, config) and
validAp(ap, config)
}
@@ -1742,7 +1744,7 @@ private module MkStage<StageSig PrevStage> {
)
}
predicate stats(
additional predicate stats(
boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config
) {
fwd = true and

View File

@@ -838,13 +838,13 @@ private module Stage1 implements StageSig {
* by `revFlow`.
*/
pragma[nomagic]
predicate revFlowIsReadAndStored(Content c, Configuration conf) {
additional predicate revFlowIsReadAndStored(Content c, Configuration conf) {
revFlowConsCand(c, conf) and
revFlowStore(c, _, _, conf)
}
pragma[nomagic]
predicate viableReturnPosOutNodeCandFwd1(
additional predicate viableReturnPosOutNodeCandFwd1(
DataFlowCall call, ReturnPosition pos, NodeEx out, Configuration config
) {
fwdFlowReturnPosition(pos, _, config) and
@@ -860,7 +860,7 @@ private module Stage1 implements StageSig {
}
pragma[nomagic]
predicate viableParamArgNodeCandFwd1(
additional predicate viableParamArgNodeCandFwd1(
DataFlowCall call, ParamNodeEx p, ArgNodeEx arg, Configuration config
) {
viableParamArgEx(call, p, arg) and
@@ -907,7 +907,7 @@ private module Stage1 implements StageSig {
)
}
predicate revFlowState(FlowState state, Configuration config) {
additional predicate revFlowState(FlowState state, Configuration config) {
exists(NodeEx node |
sinkNode(node, state, config) and
revFlow(node, _, pragma[only_bind_into](config)) and
@@ -999,7 +999,7 @@ private module Stage1 implements StageSig {
)
}
predicate stats(
additional predicate stats(
boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config
) {
fwd = true and
@@ -1260,7 +1260,7 @@ private module MkStage<StageSig PrevStage> {
* argument.
*/
pragma[nomagic]
predicate fwdFlow(
additional predicate fwdFlow(
NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config
) {
fwdFlow0(node, state, cc, argAp, ap, config) and
@@ -1484,7 +1484,7 @@ private module MkStage<StageSig PrevStage> {
* the access path of the returned value.
*/
pragma[nomagic]
predicate revFlow(
additional predicate revFlow(
NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
) {
revFlow0(node, state, toReturn, returnAp, ap, config) and
@@ -1662,7 +1662,7 @@ private module MkStage<StageSig PrevStage> {
)
}
predicate revFlow(NodeEx node, FlowState state, Configuration config) {
additional predicate revFlow(NodeEx node, FlowState state, Configuration config) {
revFlow(node, state, _, _, _, config)
}
@@ -1675,11 +1675,13 @@ private module MkStage<StageSig PrevStage> {
// use an alias as a workaround for bad functionality-induced joins
pragma[nomagic]
predicate revFlowAlias(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) }
additional predicate revFlowAlias(NodeEx node, Configuration config) {
revFlow(node, _, _, _, _, config)
}
// use an alias as a workaround for bad functionality-induced joins
pragma[nomagic]
predicate revFlowAlias(NodeEx node, FlowState state, Ap ap, Configuration config) {
additional predicate revFlowAlias(NodeEx node, FlowState state, Ap ap, Configuration config) {
revFlow(node, state, ap, config)
}
@@ -1700,7 +1702,7 @@ private module MkStage<StageSig PrevStage> {
)
}
predicate consCand(TypedContent tc, Ap ap, Configuration config) {
additional predicate consCand(TypedContent tc, Ap ap, Configuration config) {
revConsCand(tc, ap, config) and
validAp(ap, config)
}
@@ -1742,7 +1744,7 @@ private module MkStage<StageSig PrevStage> {
)
}
predicate stats(
additional predicate stats(
boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config
) {
fwd = true and

View File

@@ -838,13 +838,13 @@ private module Stage1 implements StageSig {
* by `revFlow`.
*/
pragma[nomagic]
predicate revFlowIsReadAndStored(Content c, Configuration conf) {
additional predicate revFlowIsReadAndStored(Content c, Configuration conf) {
revFlowConsCand(c, conf) and
revFlowStore(c, _, _, conf)
}
pragma[nomagic]
predicate viableReturnPosOutNodeCandFwd1(
additional predicate viableReturnPosOutNodeCandFwd1(
DataFlowCall call, ReturnPosition pos, NodeEx out, Configuration config
) {
fwdFlowReturnPosition(pos, _, config) and
@@ -860,7 +860,7 @@ private module Stage1 implements StageSig {
}
pragma[nomagic]
predicate viableParamArgNodeCandFwd1(
additional predicate viableParamArgNodeCandFwd1(
DataFlowCall call, ParamNodeEx p, ArgNodeEx arg, Configuration config
) {
viableParamArgEx(call, p, arg) and
@@ -907,7 +907,7 @@ private module Stage1 implements StageSig {
)
}
predicate revFlowState(FlowState state, Configuration config) {
additional predicate revFlowState(FlowState state, Configuration config) {
exists(NodeEx node |
sinkNode(node, state, config) and
revFlow(node, _, pragma[only_bind_into](config)) and
@@ -999,7 +999,7 @@ private module Stage1 implements StageSig {
)
}
predicate stats(
additional predicate stats(
boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config
) {
fwd = true and
@@ -1260,7 +1260,7 @@ private module MkStage<StageSig PrevStage> {
* argument.
*/
pragma[nomagic]
predicate fwdFlow(
additional predicate fwdFlow(
NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config
) {
fwdFlow0(node, state, cc, argAp, ap, config) and
@@ -1484,7 +1484,7 @@ private module MkStage<StageSig PrevStage> {
* the access path of the returned value.
*/
pragma[nomagic]
predicate revFlow(
additional predicate revFlow(
NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
) {
revFlow0(node, state, toReturn, returnAp, ap, config) and
@@ -1662,7 +1662,7 @@ private module MkStage<StageSig PrevStage> {
)
}
predicate revFlow(NodeEx node, FlowState state, Configuration config) {
additional predicate revFlow(NodeEx node, FlowState state, Configuration config) {
revFlow(node, state, _, _, _, config)
}
@@ -1675,11 +1675,13 @@ private module MkStage<StageSig PrevStage> {
// use an alias as a workaround for bad functionality-induced joins
pragma[nomagic]
predicate revFlowAlias(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) }
additional predicate revFlowAlias(NodeEx node, Configuration config) {
revFlow(node, _, _, _, _, config)
}
// use an alias as a workaround for bad functionality-induced joins
pragma[nomagic]
predicate revFlowAlias(NodeEx node, FlowState state, Ap ap, Configuration config) {
additional predicate revFlowAlias(NodeEx node, FlowState state, Ap ap, Configuration config) {
revFlow(node, state, ap, config)
}
@@ -1700,7 +1702,7 @@ private module MkStage<StageSig PrevStage> {
)
}
predicate consCand(TypedContent tc, Ap ap, Configuration config) {
additional predicate consCand(TypedContent tc, Ap ap, Configuration config) {
revConsCand(tc, ap, config) and
validAp(ap, config)
}
@@ -1742,7 +1744,7 @@ private module MkStage<StageSig PrevStage> {
)
}
predicate stats(
additional predicate stats(
boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config
) {
fwd = true and

View File

@@ -1003,6 +1003,8 @@ predicate jumpStep(Node pred, Node succ) {
succ.(SsaDefinitionNode).getDefinition())
or
succ.asExpr().getExpr().(ConstantReadAccess).getValue() = pred.asExpr().getExpr()
or
FlowSummaryImpl::Private::Steps::summaryJumpStep(pred, succ)
}
private ContentSet getKeywordContent(string name) {
@@ -1165,8 +1167,8 @@ private module PostUpdateNodes {
ExprPostUpdateNode() { this = TExprPostUpdateNode(e) }
override ExprNode getPreUpdateNode() {
// For compund arguments, such as `m(if b then x else y)`, we want the leaf nodes
// `[post] x` and `[post] y` to have two pre-update nodes: (1) the compund argument,
// For compound arguments, such as `m(if b then x else y)`, we want the leaf nodes
// `[post] x` and `[post] y` to have two pre-update nodes: (1) the compound argument,
// `if b then x else y`; and the (2) the underlying expressions; `x` and `y`,
// respectively.
//

View File

@@ -440,15 +440,24 @@ signature predicate guardChecksSig(CfgNodes::ExprCfgNode g, CfgNode e, boolean b
* in data flow and taint tracking.
*/
module BarrierGuard<guardChecksSig/3 guardChecks> {
pragma[nomagic]
private predicate guardChecksSsaDef(CfgNodes::ExprCfgNode g, boolean branch, Ssa::Definition def) {
guardChecks(g, def.getARead(), branch)
}
pragma[nomagic]
private predicate guardControlsSsaDef(
CfgNodes::ExprCfgNode g, boolean branch, Ssa::Definition def, Node n
) {
def.getARead() = n.asExpr() and
guardControlsBlock(g, n.asExpr().getBasicBlock(), branch)
}
/** Gets a node that is safely guarded by the given guard check. */
Node getABarrierNode() {
exists(
CfgNodes::ExprCfgNode g, boolean branch, CfgNodes::ExprCfgNode testedNode, Ssa::Definition def
|
def.getARead() = testedNode and
def.getARead() = result.asExpr() and
guardChecks(g, testedNode, branch) and
guardControlsBlock(g, result.asExpr().getBasicBlock(), branch)
exists(CfgNodes::ExprCfgNode g, boolean branch, Ssa::Definition def |
guardChecksSsaDef(g, branch, def) and
guardControlsSsaDef(g, branch, def, result)
)
or
result.asExpr() = getAMaybeGuardedCapturedDef().getARead()

View File

@@ -61,6 +61,20 @@ module Public {
/** Gets a summary component for a return of kind `rk`. */
SummaryComponent return(ReturnKind rk) { result = TReturnSummaryComponent(rk) }
/** Gets a summary component for synthetic global `sg`. */
SummaryComponent syntheticGlobal(SyntheticGlobal sg) {
result = TSyntheticGlobalSummaryComponent(sg)
}
/**
* A synthetic global. This represents some form of global state, which
* summaries can read and write individually.
*/
abstract class SyntheticGlobal extends string {
bindingset[this]
SyntheticGlobal() { any() }
}
}
/**
@@ -256,6 +270,7 @@ module Private {
TParameterSummaryComponent(ArgumentPosition pos) or
TArgumentSummaryComponent(ParameterPosition pos) or
TReturnSummaryComponent(ReturnKind rk) or
TSyntheticGlobalSummaryComponent(SummaryComponent::SyntheticGlobal sg) or
TWithoutContentSummaryComponent(ContentSet c) or
TWithContentSummaryComponent(ContentSet c)
@@ -563,6 +578,11 @@ module Private {
getCallbackReturnType(getNodeType(summaryNodeInputState(pragma[only_bind_out](c),
s.tail())), rk)
)
or
exists(SummaryComponent::SyntheticGlobal sg |
head = TSyntheticGlobalSummaryComponent(sg) and
result = getSyntheticGlobalType(sg)
)
)
or
n = summaryNodeOutputState(c, s) and
@@ -582,6 +602,11 @@ module Private {
getCallbackParameterType(getNodeType(summaryNodeInputState(pragma[only_bind_out](c),
s.tail())), pos)
)
or
exists(SummaryComponent::SyntheticGlobal sg |
head = TSyntheticGlobalSummaryComponent(sg) and
result = getSyntheticGlobalType(sg)
)
)
)
}
@@ -692,6 +717,18 @@ module Private {
)
}
/**
* Holds if there is a jump step from `pred` to `succ`, which is synthesized
* from a flow summary.
*/
predicate summaryJumpStep(Node pred, Node succ) {
exists(SummaryComponentStack s |
s = SummaryComponentStack::singleton(SummaryComponent::syntheticGlobal(_)) and
pred = summaryNodeOutputState(_, s) and
succ = summaryNodeInputState(_, s)
)
}
/**
* Holds if values stored inside content `c` are cleared at `n`. `n` is a
* synthesized summary node, so in order for values to be cleared at calls
@@ -871,18 +908,28 @@ module Private {
AccessPathRange() { relevantSpec(this) }
}
/** Holds if specification component `c` parses as parameter `n`. */
/** Holds if specification component `token` parses as parameter `pos`. */
predicate parseParam(AccessPathToken token, ArgumentPosition pos) {
token.getName() = "Parameter" and
pos = parseParamBody(token.getAnArgument())
}
/** Holds if specification component `c` parses as argument `n`. */
/** Holds if specification component `token` parses as argument `pos`. */
predicate parseArg(AccessPathToken token, ParameterPosition pos) {
token.getName() = "Argument" and
pos = parseArgBody(token.getAnArgument())
}
/** Holds if specification component `token` parses as synthetic global `sg`. */
predicate parseSynthGlobal(AccessPathToken token, string sg) {
token.getName() = "SyntheticGlobal" and
sg = token.getAnArgument()
}
private class SyntheticGlobalFromAccessPath extends SummaryComponent::SyntheticGlobal {
SyntheticGlobalFromAccessPath() { parseSynthGlobal(_, this) }
}
private SummaryComponent interpretComponent(AccessPathToken token) {
exists(ParameterPosition pos |
parseArg(token, pos) and result = SummaryComponent::argument(pos)
@@ -894,6 +941,10 @@ module Private {
or
token = "ReturnValue" and result = SummaryComponent::return(getReturnValueKind())
or
exists(string sg |
parseSynthGlobal(token, sg) and result = SummaryComponent::syntheticGlobal(sg)
)
or
result = interpretComponentSpecific(token)
}

View File

@@ -44,6 +44,9 @@ DataFlowType getCallbackParameterType(DataFlowType t, ArgumentPosition pos) { an
*/
DataFlowType getCallbackReturnType(DataFlowType t, ReturnKind rk) { any() }
/** Gets the type of synthetic global `sg`. */
DataFlowType getSyntheticGlobalType(SummaryComponent::SyntheticGlobal sg) { any() }
/**
* Holds if an external flow summary exists for `c` with input specification
* `input`, output specification `output`, kind `kind`, and a flag `generated`

View File

@@ -64,7 +64,7 @@ predicate uninitializedWrite(Cfg::EntryBasicBlock bb, int i, LocalVariable v) {
i = -1
}
/** Holds if `bb` contains a caputured read of variable `v`. */
/** Holds if `bb` contains a captured read of variable `v`. */
pragma[noinline]
private predicate hasCapturedVariableRead(Cfg::BasicBlock bb, LocalVariable v) {
exists(LocalVariableReadAccess read |
@@ -74,7 +74,7 @@ private predicate hasCapturedVariableRead(Cfg::BasicBlock bb, LocalVariable v) {
)
}
/** Holds if `bb` contains a caputured write to variable `v`. */
/** Holds if `bb` contains a captured write to variable `v`. */
pragma[noinline]
private predicate writesCapturedVariable(Cfg::BasicBlock bb, LocalVariable v) {
exists(LocalVariableWriteAccess write |

View File

@@ -417,7 +417,7 @@ module Rbi {
override ReturnType getReturnType() { result = ReturnsCall.super.getReturnType() }
}
/** A call to `void` that spcifies that a given method does not return a useful value. */
/** A call to `void` that specifies that a given method does not return a useful value. */
class MethodVoidCall extends MethodReturnsTypeCall instanceof VoidCall {
override ReturnType getReturnType() { result = VoidCall.super.getReturnType() }
}
@@ -448,7 +448,7 @@ module Rbi {
}
/**
* A call to `void` that spcifies that a given proc or block does not return
* A call to `void` that specifies that a given proc or block does not return
* a useful value.
*/
class ProcVoidCall extends ProcReturnsTypeCall instanceof VoidCall {

View File

@@ -6,7 +6,7 @@ private import codeql.ruby.ast.internal.TreeSitter
/** A source file that contains generated code. */
abstract class GeneratedCodeFile extends RubyFile { }
/** A file contining comments suggesting it contains generated code. */
/** A file continuing comments suggesting it contains generated code. */
class GeneratedCommentFile extends GeneratedCodeFile {
GeneratedCommentFile() { this = any(GeneratedCodeComment c).getLocation().getFile() }
}

View File

@@ -83,6 +83,8 @@ class ActionControllerActionMethod extends Method, Http::Server::RequestHandler:
override string getFramework() { result = "ActionController" }
override string getAnHttpMethod() { result = this.getARoute().getHttpMethod() }
/** Gets a call to render from within this method. */
Rails::RenderCall getARenderCall() { result.getParent+() = this }
@@ -139,6 +141,8 @@ class ParamsSource extends Http::Server::RequestInputAccess::Range {
ParamsSource() { this.asExpr().getExpr() instanceof Rails::ParamsCall }
override string getSourceType() { result = "ActionController::Metal#params" }
override Http::Server::RequestInputKind getKind() { result = Http::Server::parameterInputKind() }
}
/**
@@ -149,6 +153,8 @@ class CookiesSource extends Http::Server::RequestInputAccess::Range {
CookiesSource() { this.asExpr().getExpr() instanceof Rails::CookiesCall }
override string getSourceType() { result = "ActionController::Metal#cookies" }
override Http::Server::RequestInputKind getKind() { result = Http::Server::cookieInputKind() }
}
/** A call to `cookies` from within a controller. */
@@ -161,6 +167,140 @@ private class ActionControllerParamsCall extends ActionControllerContextCall, Pa
ActionControllerParamsCall() { this.getMethodName() = "params" }
}
/** Modeling for `ActionDispatch::Request`. */
private module Request {
/**
* A call to `request` from within a controller. This is an instance of
* `ActionDispatch::Request`.
*/
private class RequestNode extends DataFlow::CallNode {
RequestNode() {
this.asExpr().getExpr() instanceof ActionControllerContextCall and
this.getMethodName() = "request"
}
}
/**
* A method call on `request`.
*/
private class RequestMethodCall extends DataFlow::CallNode {
RequestMethodCall() {
any(RequestNode r).(DataFlow::LocalSourceNode).flowsTo(this.getReceiver())
}
}
abstract private class RequestInputAccess extends RequestMethodCall,
Http::Server::RequestInputAccess::Range {
override string getSourceType() { result = "ActionDispatch::Request#" + this.getMethodName() }
}
/**
* A method call on `request` which returns request parameters.
*/
private class ParametersCall extends RequestInputAccess {
ParametersCall() {
this.getMethodName() =
[
"parameters", "params", "GET", "POST", "query_parameters", "request_parameters",
"filtered_parameters"
]
}
override Http::Server::RequestInputKind getKind() {
result = Http::Server::parameterInputKind()
}
}
/** A method call on `request` which returns part or all of the request path. */
private class PathCall extends RequestInputAccess {
PathCall() {
this.getMethodName() =
["path", "filtered_path", "fullpath", "original_fullpath", "original_url", "url"]
}
override Http::Server::RequestInputKind getKind() { result = Http::Server::urlInputKind() }
}
/** A method call on `request` which returns a specific request header. */
private class HeadersCall extends RequestInputAccess {
HeadersCall() {
this.getMethodName() =
[
"authorization", "script_name", "path_info", "user_agent", "referer", "referrer",
"host_authority", "content_type", "host", "hostname", "accept_encoding",
"accept_language", "if_none_match", "if_none_match_etags", "content_mime_type"
]
or
// Request headers are prefixed with `HTTP_` to distinguish them from
// "headers" supplied by Rack middleware.
this.getMethodName() = ["get_header", "fetch_header"] and
this.getArgument(0).asExpr().getExpr().getConstantValue().getString().regexpMatch("^HTTP_.+")
}
override Http::Server::RequestInputKind getKind() { result = Http::Server::headerInputKind() }
}
// TODO: each_header
/**
* A method call on `request` which returns part or all of the host.
* This can be influenced by headers such as Host and X-Forwarded-Host.
*/
private class HostCall extends RequestInputAccess {
HostCall() {
this.getMethodName() =
[
"authority", "host", "host_authority", "host_with_port", "hostname", "forwarded_for",
"forwarded_host", "port", "forwarded_port"
]
}
override Http::Server::RequestInputKind getKind() { result = Http::Server::headerInputKind() }
}
/**
* A method call on `request` which is influenced by one or more request
* headers.
*/
private class HeaderTaintedCall extends RequestInputAccess {
HeaderTaintedCall() {
this.getMethodName() = ["media_type", "media_type_params", "content_charset", "base_url"]
}
override Http::Server::RequestInputKind getKind() { result = Http::Server::headerInputKind() }
}
/** A method call on `request` which returns the request body. */
private class BodyCall extends RequestInputAccess {
BodyCall() { this.getMethodName() = ["body", "raw_post"] }
override Http::Server::RequestInputKind getKind() { result = Http::Server::bodyInputKind() }
}
/**
* A method call on `request` which returns the rack env.
* This is a hash containing all the information about the request. Values
* under keys starting with `HTTP_` are user-controlled.
*/
private class EnvCall extends RequestMethodCall {
EnvCall() { this.getMethodName() = ["env", "filtered_env"] }
}
/**
* A read of a user-controlled parameter from the request env.
*/
private class EnvHttpAccess extends DataFlow::CallNode, Http::Server::RequestInputAccess::Range {
EnvHttpAccess() {
any(EnvCall c).(DataFlow::LocalSourceNode).flowsTo(this.getReceiver()) and
this.getMethodName() = "[]" and
this.getArgument(0).asExpr().getExpr().getConstantValue().getString().regexpMatch("^HTTP_.+")
}
override Http::Server::RequestInputKind getKind() { result = Http::Server::headerInputKind() }
override string getSourceType() { result = "ActionDispatch::Request#env[]" }
}
}
/** A call to `render` from within a controller. */
private class ActionControllerRenderCall extends ActionControllerContextCall, RenderCallImpl {
ActionControllerRenderCall() { this.getMethodName() = "render" }
@@ -171,14 +311,6 @@ private class ActionControllerRenderToCall extends ActionControllerContextCall,
ActionControllerRenderToCall() { this.getMethodName() = ["render_to_body", "render_to_string"] }
}
/** A call to `html_safe` from within a controller. */
private class ActionControllerHtmlSafeCall extends HtmlSafeCallImpl {
ActionControllerHtmlSafeCall() {
this.getMethodName() = "html_safe" and
this.getEnclosingModule() instanceof ActionControllerControllerClass
}
}
/** A call to `html_escape` from within a controller. */
private class ActionControllerHtmlEscapeCall extends HtmlEscapeCallImpl {
ActionControllerHtmlEscapeCall() {
@@ -376,8 +508,11 @@ private class ActionControllerProtectFromForgeryCall extends CsrfProtectionSetti
*/
private class SendFile extends FileSystemAccess::Range, DataFlow::CallNode {
SendFile() {
this.asExpr().getExpr() instanceof ActionControllerContextCall and
this.getMethodName() = "send_file"
this.getMethodName() = "send_file" and
(
this.asExpr().getExpr() instanceof ActionControllerContextCall or
this.getReceiver().asExpr().getExpr() instanceof Response::ResponseCall
)
}
override DataFlow::Node getAPathArgument() { result = this.getArgument(0) }
@@ -502,3 +637,94 @@ private module ParamsSummaries {
}
}
}
/**
* Provides modeling for `ActionDispatch::Response`, which represents an HTTP
* response.
*/
private module Response {
class ResponseCall extends ActionControllerContextCall {
ResponseCall() { this.getMethodName() = "response" }
}
class BodyWrite extends DataFlow::CallNode, Http::Server::HttpResponse::Range {
BodyWrite() {
this.getReceiver().asExpr().getExpr() instanceof ResponseCall and
this.getMethodName() = "body="
}
override DataFlow::Node getBody() { result = this.getArgument(0) }
override DataFlow::Node getMimetypeOrContentTypeArg() { none() }
override string getMimetypeDefault() { result = "text/http" }
}
class SendFileCall extends DataFlow::CallNode, Http::Server::HttpResponse::Range {
SendFileCall() {
this.getReceiver().asExpr().getExpr() instanceof ResponseCall and
this.getMethodName() = "send_file"
}
override DataFlow::Node getBody() { result = this.getArgument(0) }
override DataFlow::Node getMimetypeOrContentTypeArg() { none() }
override string getMimetypeDefault() { result = "application/octet-stream" }
}
class HeaderWrite extends DataFlow::CallNode, Http::Server::HeaderWriteAccess::Range {
HeaderWrite() {
// response.header[key] = val
// response.headers[key] = val
exists(MethodCall headerCall |
headerCall.getMethodName() = ["header", "headers"] and
headerCall.getReceiver() instanceof ResponseCall
|
this.getReceiver().asExpr().getExpr() = headerCall and
this.getMethodName() = "[]="
)
or
// response.set_header(key) = val
// response[header] = val
// response.add_header(key, val)
this.getReceiver().asExpr().getExpr() instanceof ResponseCall and
this.getMethodName() = ["set_header", "[]=", "add_header"]
}
override string getName() {
result = this.getArgument(0).asExpr().getConstantValue().getString()
}
override DataFlow::Node getValue() { result = this.getArgument(1) }
}
class SpecificHeaderWrite extends DataFlow::CallNode, Http::Server::HeaderWriteAccess::Range {
SpecificHeaderWrite() {
// response.<method> = val
this.getReceiver().asExpr().getExpr() instanceof ResponseCall and
this.getMethodName() =
[
"location=", "cache_control=", "_cache_control=", "etag=", "charset=", "content_type=",
"date=", "last_modified=", "weak_etag=", "strong_etag="
]
}
override string getName() {
this.getMethodName() = "location=" and result = "location"
or
this.getMethodName() = ["_cache_control=", "cache_control="] and result = "cache-control"
or
this.getMethodName() = ["etag=", "weak_etag=", "strong_etag="] and result = "etag"
or
// sets the charset part of the content-type header
this.getMethodName() = ["charset=", "content_type="] and result = "content-type"
or
this.getMethodName() = "date=" and result = "date"
or
this.getMethodName() = "last_modified=" and result = "last-modified"
}
override DataFlow::Node getValue() { result = this.getArgument(0) }
}
}

View File

@@ -39,11 +39,6 @@ predicate inActionViewContext(AstNode n) {
n.getLocation().getFile() instanceof ErbFile
}
/** A call to `html_safe` from within a template. */
private class ActionViewHtmlSafeCall extends HtmlSafeCallImpl {
ActionViewHtmlSafeCall() { this.getMethodName() = "html_safe" and inActionViewContext(this) }
}
/**
* A call to a Rails method that escapes HTML.
*/

View File

@@ -0,0 +1,30 @@
/**
* Modeling for `ActiveJob`, a framweork for declaring and enqueueing jobs that
* ships with Rails.
* https://rubygems.org/gems/activejob
*/
private import codeql.ruby.ApiGraphs
private import codeql.ruby.Concepts
private import codeql.ruby.DataFlow
/** Modeling for `ActiveJob`. */
module ActiveJob {
/**
* `ActiveJob::Serializers`
*/
module Serializers {
/**
* A call to `ActiveJob::Serializers.deserialize`, which interprets part of
* its argument as a Ruby constant.
*/
class DeserializeCall extends DataFlow::CallNode, CodeExecution::Range {
DeserializeCall() {
this =
API::getTopLevelMember("ActiveJob").getMember("Serializers").getAMethodCall("deserialize")
}
override DataFlow::Node getCode() { result = this.getArgument(0) }
}
}
}

View File

@@ -140,6 +140,25 @@ module ActiveSupport {
}
}
/**
* Type summaries for extensions to the `Pathname` module.
*/
private class PathnameTypeSummary extends ModelInput::TypeModelCsv {
override predicate row(string row) {
// package1;type1;package2;type2;path
// Pathname#existence : Pathname
row = ";Pathname;;Pathname;Method[existence].ReturnValue"
}
}
/** Taint flow summaries for extensions to the `Pathname` module. */
private class PathnameTaintSummary extends ModelInput::SummaryModelCsv {
override predicate row(string row) {
// Pathname#existence
row = ";Pathname;Method[existence];Argument[self];ReturnValue;taint"
}
}
/**
* `ActiveSupport::SafeBuffer` wraps a string, providing HTML-safe methods
* for concatenation.

View File

@@ -14,6 +14,7 @@ import core.Hash
import core.String
import core.Regexp
import core.IO
import core.Digest
/**
* A system command executed via subshell literal syntax.

View File

@@ -84,6 +84,9 @@ private class GraphqlSchemaResolverClass extends ClassDeclaration {
}
}
/** Gets an HTTP method that is supported for querying a GraphQL server. */
private string getASupportedHttpMethod() { result = ["get", "post"] }
/**
* A `ClassDeclaration` for a class that extends `GraphQL::Schema::Object`.
* For example,
@@ -120,7 +123,7 @@ class GraphqlSchemaObjectClass extends ClassDeclaration {
* `GraphQL::Schema::RelayClassicMutation` or
* `GraphQL::Schema::Resolver`.
*
* Both of these classes have an overrideable `resolve` instance
* Both of these classes have an overridable `resolve` instance
* method which can receive user input in order to resolve a query or mutation.
*/
private class GraphqlResolvableClass extends ClassDeclaration {
@@ -144,7 +147,7 @@ private class GraphqlResolvableClass extends ClassDeclaration {
*
* ```rb
* module Mutation
* class NameAnInstrument < BaseMutationn
* class NameAnInstrument < BaseMutation
* argument :instrument_uuid, Types::Uuid,
* required: true,
* loads: ::Instrument,
@@ -172,6 +175,8 @@ class GraphqlResolveMethod extends Method, Http::Server::RequestHandler::Range {
override string getFramework() { result = "GraphQL" }
override string getAnHttpMethod() { result = getASupportedHttpMethod() }
/** Gets the mutation class containing this method. */
GraphqlResolvableClass getMutationClass() { result = resolvableClass }
}
@@ -188,7 +193,7 @@ class GraphqlResolveMethod extends Method, Http::Server::RequestHandler::Range {
*
* ```rb
* module Mutation
* class NameAnInstrument < BaseMutationn
* class NameAnInstrument < BaseMutation
* argument :instrument_uuid, Types::Uuid,
* required: true,
* loads: ::Instrument,
@@ -219,6 +224,8 @@ class GraphqlLoadMethod extends Method, Http::Server::RequestHandler::Range {
override string getFramework() { result = "GraphQL" }
override string getAnHttpMethod() { result = getASupportedHttpMethod() }
/** Gets the mutation class containing this method. */
GraphqlResolvableClass getMutationClass() { result = resolvableClass }
}
@@ -388,6 +395,8 @@ class GraphqlFieldResolutionMethod extends Method, Http::Server::RequestHandler:
override string getFramework() { result = "GraphQL" }
override string getAnHttpMethod() { result = getASupportedHttpMethod() }
/** Gets the class containing this method. */
GraphqlSchemaObjectClass getGraphqlClass() { result = schemaObjectClass }
}

View File

@@ -16,10 +16,13 @@ private import codeql.ruby.security.OpenSSL
*/
module Rails {
/**
* DEPRECATED: Any call to `html_safe` is considered an XSS sink.
* A method call on a string to mark it as HTML safe for Rails. Strings marked
* as such will not be automatically escaped when inserted into HTML.
*/
class HtmlSafeCall extends MethodCall instanceof HtmlSafeCallImpl { }
deprecated class HtmlSafeCall extends MethodCall {
HtmlSafeCall() { this.getMethodName() = "html_safe" }
}
/** A call to a Rails method to escape HTML. */
class HtmlEscapeCall extends MethodCall instanceof HtmlEscapeCallImpl { }
@@ -71,6 +74,21 @@ module Rails {
/** A render call that does not automatically set the HTTP response body. */
class RenderToCall extends MethodCall instanceof RenderToCallImpl { }
/**
* A `render` call seen as a file system access.
*/
private class RenderAsFileSystemAccess extends FileSystemAccess::Range, DataFlow::CallNode {
RenderAsFileSystemAccess() {
exists(MethodCall call | this.asExpr().getExpr() = call |
call instanceof RenderCall
or
call instanceof RenderToCall
)
}
override DataFlow::Node getAPathArgument() { result = this.getKeywordArgument("file") }
}
}
/**

View File

@@ -0,0 +1,34 @@
/**
* Provides modeling for the `Digest` module.
*/
private import codeql.ruby.ApiGraphs
private import codeql.ruby.Concepts
private import codeql.ruby.DataFlow
/** Gets an API node for a Digest class that hashes using `algo`. */
private API::Node digest(Cryptography::HashingAlgorithm algo) {
exists(string name | result = API::getTopLevelMember("Digest").getMember(name) |
name = ["MD5", "SHA1", "SHA2", "RMD160"] and
algo.matchesName(name)
)
}
/** A call that hashes some input using a hashing algorithm from the `Digest` module. */
private class DigestCall extends Cryptography::CryptographicOperation::Range instanceof DataFlow::CallNode {
Cryptography::HashingAlgorithm algo;
DigestCall() {
this = digest(algo).getAMethodCall(["hexdigest", "base64digest", "bubblebabble"])
or
this = digest(algo).getAMethodCall("file") // it's directly hashing the contents of a file, but that's close enough for us.
or
this = digest(algo).getInstance().getAMethodCall(["digest", "update", "<<"])
}
override Cryptography::HashingAlgorithm getAlgorithm() { result = algo }
override DataFlow::Node getAnInput() { result = super.getArgument(0) }
override Cryptography::BlockMode getBlockMode() { none() }
}

View File

@@ -95,7 +95,7 @@ module IO {
* popen([env,] cmd, mode="r" [, opt]) -> io
* popen([env,] cmd, mode="r" [, opt]) {|io| block } -> obj
* ```
* `IO.popen` does different things based on the the value of `cmd`:
* `IO.popen` does different things based on the value of `cmd`:
* ```
* "-" : fork
* commandline : command line string which is passed to a shell

View File

@@ -39,7 +39,8 @@ class FaradayHttpRequest extends Http::Client::Request::Range, DataFlow::CallNod
API::getTopLevelMember("Faraday").getMember("Connection").getInstance()
] and
requestNode =
connectionNode.getReturn(["get", "head", "delete", "post", "put", "patch", "trace"]) and
connectionNode
.getReturn(["get", "head", "delete", "post", "put", "patch", "trace", "run_request"]) and
this = requestNode.asSource() and
connectionUse = connectionNode.asSource()
}

View File

@@ -25,7 +25,7 @@ class HttpClientRequest extends Http::Client::Request::Range, DataFlow::CallNode
[
// One-off requests
API::getTopLevelMember("HTTPClient"),
// Conncection re-use
// Connection re-use
API::getTopLevelMember("HTTPClient").getInstance()
] and
requestNode = connectionNode.getReturn(method) and

View File

@@ -1,7 +1,5 @@
private import codeql.ruby.AST
abstract class HtmlSafeCallImpl extends MethodCall { }
abstract class HtmlEscapeCallImpl extends MethodCall { }
abstract class RenderCallImpl extends MethodCall { }

View File

@@ -241,7 +241,7 @@ abstract class RegExp extends Ast::StringlikeLiteral {
/**
* Helper predicate for `escapingChar`.
* In order to avoid negative recusrion, we return a boolean.
* In order to avoid negative recursion, we return a boolean.
* This way, we can refer to `escaping(pos - 1).booleanNot()`
* rather than to a negated version of `escaping(pos)`.
*/

View File

@@ -1,5 +1,5 @@
/**
* Provides precicates for reasoning about bad tag filter vulnerabilities.
* Provides predicates for reasoning about bad tag filter vulnerabilities.
*/
import regexp.RegexpMatching
@@ -65,7 +65,7 @@ predicate isBadRegexpFilter(HtmlMatchingRegExp regexp, string msg) {
regexp.matches("<!-- foo --!>") and
exists(int a, int b | a != b |
regexp.fillsCaptureGroup("<!-- foo -->", a) and
// <!-- foo --> might be ambigously parsed (matching both capture groups), and that is ok here.
// <!-- foo --> might be ambiguously parsed (matching both capture groups), and that is ok here.
regexp.fillsCaptureGroup("<!-- foo --!>", b) and
not regexp.fillsCaptureGroup("<!-- foo --!>", a) and
msg =

View File

@@ -0,0 +1,36 @@
/**
* Provides utility classes and predicates for reasoning about `Kernel.open` and related methods.
*/
private import codeql.ruby.AST
private import codeql.ruby.DataFlow
private import codeql.ruby.AST
private import codeql.ruby.ApiGraphs
private import codeql.ruby.frameworks.core.Kernel::Kernel
/** A call to a method that might access a file or start a process. */
class AmbiguousPathCall extends DataFlow::CallNode {
string name;
AmbiguousPathCall() {
this.(KernelMethodCall).getMethodName() = "open" and
name = "Kernel.open"
or
this = API::getTopLevelMember("IO").getAMethodCall("read") and
not this = API::getTopLevelMember("File").getAMethodCall("read") and // needed in e.g. opal/opal, where some calls have both paths, but I'm not sure why
name = "IO.read"
}
/** Gets the name for the method being called. */
string getName() { result = name }
/** Gets the name for a safer method that can be used instead. */
string getReplacement() {
result = "File.read" and name = "IO.read"
or
result = "File.open" and name = "Kernel.open"
}
/** Gets the argument that specifies the path to be accessed. */
DataFlow::Node getPathArgument() { result = this.getArgument(0) }
}

View File

@@ -581,3 +581,49 @@ private class CipherOperation extends Cryptography::CryptographicOperation::Rang
result = cipherNode.getCipherMode().getBlockMode()
}
}
/** Predicates and classes modeling the `OpenSSL::Digest` module */
private module Digest {
private import codeql.ruby.ApiGraphs
/** A call that hashes some input using a hashing algorithm from the `OpenSSL::Digest` module. */
private class DigestCall extends Cryptography::CryptographicOperation::Range instanceof DataFlow::CallNode {
Cryptography::HashingAlgorithm algo;
DigestCall() {
exists(API::MethodAccessNode call |
call = API::getTopLevelMember("OpenSSL").getMember("Digest").getMethod("new")
|
this = call.getReturn().getAMethodCall(["digest", "update", "<<"]) and
algo.matchesName(call.getCallNode()
.getArgument(0)
.asExpr()
.getExpr()
.getConstantValue()
.getString())
)
}
override Cryptography::HashingAlgorithm getAlgorithm() { result = algo }
override DataFlow::Node getAnInput() { result = super.getArgument(0) }
override Cryptography::BlockMode getBlockMode() { none() }
}
/** A call to `OpenSSL::Digest.digest` that hashes input directly without constructing a digest instance. */
private class DigestCallDirect extends Cryptography::CryptographicOperation::Range instanceof DataFlow::CallNode {
Cryptography::HashingAlgorithm algo;
DigestCallDirect() {
this = API::getTopLevelMember("OpenSSL").getMember("Digest").getMethod("digest").getCallNode() and
algo.matchesName(this.getArgument(0).asExpr().getExpr().getConstantValue().getString())
}
override Cryptography::HashingAlgorithm getAlgorithm() { result = algo }
override DataFlow::Node getAnInput() { result = super.getArgument(1) }
override Cryptography::BlockMode getBlockMode() { none() }
}
}

View File

@@ -11,6 +11,144 @@
private import codeql.ruby.AST
private import codeql.ruby.DataFlow
import codeql.ruby.security.internal.SensitiveDataHeuristics
private import HeuristicNames
private import codeql.ruby.CFG
/** An expression that might contain sensitive data. */
cached
abstract class SensitiveNode extends DataFlow::Node {
/** Gets a human-readable description of this expression for use in alert messages. */
cached
abstract string describe();
/** Gets a classification of the kind of sensitive data this expression might contain. */
cached
abstract SensitiveDataClassification getClassification();
}
/** A method call that might produce sensitive data. */
class SensitiveCall extends SensitiveNode instanceof DataFlow::CallNode {
SensitiveDataClassification classification;
SensitiveCall() {
classification = this.getMethodName().(SensitiveDataMethodName).getClassification()
or
// This is particularly to pick up methods with an argument like "password", which
// may indicate a lookup.
exists(string s | super.getArgument(_).asExpr().getConstantValue().isStringlikeValue(s) |
nameIndicatesSensitiveData(s, classification)
)
}
override string describe() { result = "a call to " + super.getMethodName() }
override SensitiveDataClassification getClassification() { result = classification }
}
/** An access to a variable or hash value that might contain sensitive data. */
abstract class SensitiveVariableAccess extends SensitiveNode {
string name;
SensitiveVariableAccess() {
this.asExpr().(CfgNodes::ExprNodes::VariableAccessCfgNode).getExpr().getVariable().hasName(name)
or
this.asExpr()
.(CfgNodes::ExprNodes::ElementReferenceCfgNode)
.getAnArgument()
.getConstantValue()
.isStringlikeValue(name)
}
override string describe() { result = "an access to " + name }
}
/** A write to a location that might contain sensitive data. */
abstract class SensitiveWrite extends DataFlow::Node { }
/**
* Holds if `node` is a write to a variable or hash value named `name`.
*
* Helper predicate factored out for performance,
* to filter `name` as much as possible before using it in
* regex matching.
*/
pragma[nomagic]
private predicate writesProperty(DataFlow::Node node, string name) {
exists(VariableWriteAccess vwa | vwa.getVariable().getName() = name |
node.asExpr().getExpr() = vwa
)
or
// hash value assignment
node.(DataFlow::CallNode).getMethodName() = "[]=" and
node.(DataFlow::CallNode).getArgument(0).asExpr().getConstantValue().isStringlikeValue(name)
}
/**
* Instance and class variable names are reported with their respective `@`
* and `@@` prefixes. This predicate strips these prefixes.
*/
bindingset[name]
private string unprefixedVariableName(string name) { result = name.regexpReplaceAll("^@*", "") }
/** A write to a variable or property that might contain sensitive data. */
private class BasicSensitiveWrite extends SensitiveWrite {
SensitiveDataClassification classification;
BasicSensitiveWrite() {
exists(string name |
/*
* PERFORMANCE OPTIMISATION:
* `nameIndicatesSensitiveData` performs a `regexpMatch` on `name`.
* To carry out a regex match, we must first compute the Cartesian product
* of all possible `name`s and regexes, then match.
* To keep this product as small as possible,
* we want to filter `name` as much as possible before the product.
*
* Do this by factoring out a helper predicate containing the filtering
* logic that restricts `name`. This helper predicate will get picked first
* in the join order, since it is the only call here that binds `name`.
*/
writesProperty(this, name) and
nameIndicatesSensitiveData(unprefixedVariableName(name), classification)
)
}
/** Gets a classification of the kind of sensitive data the write might handle. */
SensitiveDataClassification getClassification() { result = classification }
}
/** An access to a variable or hash value that might contain sensitive data. */
private class BasicSensitiveVariableAccess extends SensitiveVariableAccess {
SensitiveDataClassification classification;
BasicSensitiveVariableAccess() {
nameIndicatesSensitiveData(unprefixedVariableName(name), classification)
}
override SensitiveDataClassification getClassification() { result = classification }
}
/** A method name that suggests it may be sensitive. */
abstract class SensitiveMethodName extends string {
SensitiveMethodName() { this = any(MethodBase m).getName() }
}
/** A method name that suggests it may produce sensitive data. */
abstract class SensitiveDataMethodName extends SensitiveMethodName {
/** Gets a classification of the kind of sensitive data this method may produce. */
abstract SensitiveDataClassification getClassification();
}
/** A method name that might return sensitive credential data. */
class CredentialsMethodName extends SensitiveDataMethodName {
SensitiveDataClassification classification;
CredentialsMethodName() { nameIndicatesSensitiveData(this, classification) }
override SensitiveDataClassification getClassification() { result = classification }
}
/**
* A sensitive action, such as transfer of sensitive data.

View File

@@ -0,0 +1,54 @@
/**
* Provides default sources and sinks for reasoning about sensitive data sourced
* from the query string of a GET request, as well as extension points for
* adding your own.
*/
private import codeql.ruby.security.SensitiveActions
private import codeql.ruby.Concepts
private import codeql.ruby.DataFlow
/**
* Provides default sources and sinks for reasoning about sensitive data sourced
* from the query string of a GET request, as well as extension points for
* adding your own.
*/
module SensitiveGetQuery {
/**
* A data flow source representing data sourced from the query string in a
* GET request handler.
*/
abstract class Source extends DataFlow::Node {
/** Gets the request handler corresponding to this data source. */
abstract Http::Server::RequestHandler getHandler();
}
/**
* An access to data from the query string of a GET request as a data flow
* source.
*/
private class RequestInputAccessSource extends Source instanceof Http::Server::RequestInputAccess {
private Http::Server::RequestHandler handler;
RequestInputAccessSource() {
handler = this.asExpr().getExpr().getEnclosingMethod() and
handler.getAnHttpMethod() = "get" and
this.getKind() = "parameter"
}
override Http::Server::RequestHandler getHandler() { result = handler }
}
/**
* A data flow sink suggesting a use of sensitive data.
*/
abstract class Sink extends DataFlow::Node { }
/** A sensitive data node as a data flow sink. */
private class SensitiveNodeSink extends Sink instanceof SensitiveNode {
SensitiveNodeSink() {
// User names and other similar information is not sensitive in this context.
not this.getClassification() = SensitiveDataClassification::id()
}
}
}

View File

@@ -0,0 +1,31 @@
/**
* Provides a taint-tracking configuration for detecting flow of query string
* data to sensitive actions in GET query request handlers.
*
* Note, for performance reasons: only import this file if `Configuration` is
* needed, otherwise `SensitiveGetQueryCustomizations` should be imported
* instead.
*/
private import ruby
private import codeql.ruby.TaintTracking
/**
* Provides a taint-tracking configuration for detecting flow of query string
* data to sensitive actions in GET query request handlers.
*/
module SensitiveGetQuery {
import SensitiveGetQueryCustomizations::SensitiveGetQuery
/**
* A taint-tracking configuration for reasoning about use of sensitive data
* from a GET request query string.
*/
class Configuration extends TaintTracking::Configuration {
Configuration() { this = "SensitiveGetQuery" }
override predicate isSource(DataFlow::Node source) { source instanceof Source }
override predicate isSink(DataFlow::Node sink) { sink instanceof Sink }
}
}

View File

@@ -8,6 +8,8 @@ private import codeql.ruby.ApiGraphs
private import codeql.ruby.CFG
private import codeql.ruby.DataFlow
private import codeql.ruby.dataflow.RemoteFlowSources
private import codeql.ruby.frameworks.ActiveJob
private import codeql.ruby.frameworks.core.Module
module UnsafeDeserialization {
/**
@@ -199,4 +201,27 @@ module UnsafeDeserialization {
toNode = callNode
)
}
/**
* A argument in a call to `Module.const_get`, considered as a sink for unsafe
* deserialization.
*
* Calls to `Module.const_get` can return arbitrary classes which can then be
* instantiated.
*/
class ConstGetCallArgument extends Sink {
ConstGetCallArgument() { this = any(Module::ModuleConstGetCallCodeExecution c).getCode() }
}
/**
* A argument in a call to `ActiveJob::Serializers.deserialize`, considered as
* a sink for unsafe deserialization.
*
* This is roughly equivalent to a call to `Module.const_get`.
*/
class ActiveJobSerializersDeserializeArgument extends Sink {
ActiveJobSerializersDeserializeArgument() {
this = any(ActiveJob::Serializers::DeserializeCall c).getCode()
}
}
}

View File

@@ -50,7 +50,9 @@ module UrlRedirect {
/**
* A source of remote user input, considered as a flow source.
*/
class RemoteFlowSourceAsSource extends Source, RemoteFlowSource { }
class HttpRequestInputAccessAsSource extends Source, Http::Server::RequestInputAccess {
HttpRequestInputAccessAsSource() { this.isThirdPartyControllable() }
}
/**
* A HTTP redirect response, considered as a flow sink.

View File

@@ -62,10 +62,7 @@ private module Shared {
*/
class HtmlSafeCallAsSink extends Sink {
HtmlSafeCallAsSink() {
exists(Rails::HtmlSafeCall c, ErbOutputDirective d |
this.asExpr().getExpr() = c.getReceiver() and
c = d.getTerminalStmt()
)
this = any(DataFlow::CallNode call | call.getMethodName() = "html_safe").getReceiver()
}
}
@@ -105,6 +102,17 @@ private module Shared {
}
}
/** A write to an HTTP response header, considered as a flow sink. */
class HeaderWriteAsSink extends Sink {
HeaderWriteAsSink() {
exists(Http::Server::HeaderWriteAccess a |
a.getName() = ["content-type", "access-control-allow-origin"]
|
this = a.getValue()
)
}
}
/**
* An HTML escaping, considered as a sanitizer.
*/
@@ -312,9 +320,11 @@ module ReflectedXss {
deprecated predicate isAdditionalXSSTaintStep = isAdditionalXssTaintStep/2;
/**
* A source of remote user input, considered as a flow source.
* A HTTP request input, considered as a flow source.
*/
class RemoteFlowSourceAsSource extends Source, RemoteFlowSource { }
class HttpRequestInputAccessAsSource extends Source, Http::Server::RequestInputAccess {
HttpRequestInputAccessAsSource() { this.isThirdPartyControllable() }
}
}
/** DEPRECATED: Alias for ReflectedXss */
@@ -329,17 +339,13 @@ private module OrmTracking {
override predicate isSource(DataFlow2::Node source) { source instanceof OrmInstantiation }
// Select any call node and narrow down later
override predicate isSink(DataFlow2::Node sink) { sink instanceof DataFlow2::CallNode }
// Select any call receiver and narrow down later
override predicate isSink(DataFlow2::Node sink) {
sink = any(DataFlow2::CallNode c).getReceiver()
}
override predicate isAdditionalFlowStep(DataFlow2::Node node1, DataFlow2::Node node2) {
Shared::isAdditionalXssFlowStep(node1, node2)
or
// Propagate flow through arbitrary method calls
node2.(DataFlow2::CallNode).getReceiver() = node1
or
// Propagate flow through "or" expressions `or`/`||`
node2.asExpr().getExpr().(LogicalOrExpr).getAnOperand() = node1.asExpr().getExpr()
}
}
}
@@ -372,10 +378,9 @@ module StoredXss {
private class OrmFieldAsSource extends Source instanceof DataFlow2::CallNode {
OrmFieldAsSource() {
exists(OrmTracking::Configuration subConfig, DataFlow2::CallNode subSrc, MethodCall call |
subConfig.hasFlow(subSrc, this) and
call = this.asExpr().getExpr() and
subSrc.(OrmInstantiation).methodCallMayAccessField(call.getMethodName())
exists(OrmTracking::Configuration subConfig, DataFlow2::CallNode subSrc |
subConfig.hasFlow(subSrc, this.getReceiver()) and
subSrc.(OrmInstantiation).methodCallMayAccessField(this.getMethodName())
)
}
}

View File

@@ -202,7 +202,7 @@ private predicate isFork(State q, InputSymbol s1, InputSymbol s2, State r1, Stat
//
// We additionally require that the there exists another InfiniteRepetitionQuantifier `mid` on the path from `q` to itself.
// This is done to avoid flagging regular expressions such as `/(a?)*b/` - that only has polynomial runtime, and is detected by `js/polynomial-redos`.
// The below code is therefore a heuritic, that only flags regular expressions such as `/(a*)*b/`,
// The below code is therefore a heuristic, that only flags regular expressions such as `/(a*)*b/`,
// and does not flag regular expressions such as `/(a?b?)c/`, but the latter pattern is not used frequently.
r1 = r2 and
q1 = q2 and

View File

@@ -59,8 +59,8 @@ predicate matchesEpsilon(RegExpTerm t) {
/**
* A lookahead/lookbehind that matches the empty string.
*/
class EmptyPositiveSubPatttern extends RegExpSubPattern {
EmptyPositiveSubPatttern() {
class EmptyPositiveSubPattern extends RegExpSubPattern {
EmptyPositiveSubPattern() {
(
this instanceof RegExpPositiveLookahead
or
@@ -70,6 +70,9 @@ class EmptyPositiveSubPatttern extends RegExpSubPattern {
}
}
/** DEPRECATED: Use `EmptyPositiveSubPattern` instead. */
deprecated class EmptyPositiveSubPatttern = EmptyPositiveSubPattern;
/**
* A branch in a disjunction that is the root node in a literal, or a literal
* whose root node is not a disjunction.
@@ -133,7 +136,7 @@ private predicate isCanonicalTerm(RelevantRegExpTerm term, string str) {
}
/**
* Gets a string reperesentation of the flags used with the regular expression.
* Gets a string representation of the flags used with the regular expression.
* Only the flags that are relevant for the canonicalization are included.
*/
string getCanonicalizationFlags(RegExpTerm root) {
@@ -334,7 +337,7 @@ private module CharacterClasses {
)
}
private string lowercaseLetter() { result = "abdcefghijklmnopqrstuvwxyz".charAt(_) }
private string lowercaseLetter() { result = "abcdefghijklmnopqrstuvwxyz".charAt(_) }
private string upperCaseLetter() { result = "ABCDEFGHIJKLMNOPQRSTUVWXYZ".charAt(_) }
@@ -697,9 +700,7 @@ predicate delta(State q1, EdgeLabel lbl, State q2) {
lbl = Epsilon() and q2 = Accept(getRoot(dollar))
)
or
exists(EmptyPositiveSubPatttern empty | q1 = before(empty) |
lbl = Epsilon() and q2 = after(empty)
)
exists(EmptyPositiveSubPattern empty | q1 = before(empty) | lbl = Epsilon() and q2 = after(empty))
}
/**
@@ -1028,7 +1029,7 @@ module ReDoSPruning<isCandidateSig/2 isCandidate> {
* as the suffix "X" will cause both the regular expressions to be rejected.
*
* The string `w` is repeated any number of times because it needs to be
* infinitely repeatedable for the attack to work.
* infinitely repeatable for the attack to work.
* For the regular expression `/((ab)+)*abab/` the accepting state is not reachable from the fork
* using epsilon transitions. But any attempt at repeating `w` will end in a state that accepts all suffixes.
*/

View File

@@ -7,7 +7,7 @@ import codeql.Locations
private import codeql.ruby.ast.Literal as Ast
/**
* Holds if `term` is an ecape class representing e.g. `\d`.
* Holds if `term` is an escape class representing e.g. `\d`.
* `clazz` is which character class it represents, e.g. "d" for `\d`.
*/
predicate isEscapeClass(RegExpTerm term, string clazz) {

View File

@@ -106,6 +106,18 @@ module PolynomialReDoS {
regexp.asExpr() = call.getReceiver() and
this.asExpr() = call.getArgument(0)
)
or
// a case-when statement
exists(CfgNodes::ExprNodes::CaseExprCfgNode caseWhen |
matchNode.asExpr() = caseWhen and
this.asExpr() = caseWhen.getValue()
|
regexp.asExpr() =
caseWhen.getBranch(_).(CfgNodes::ExprNodes::WhenClauseCfgNode).getPattern(_)
or
regexp.asExpr() =
caseWhen.getBranch(_).(CfgNodes::ExprNodes::InClauseCfgNode).getPattern()
)
)
)
}

View File

@@ -1,5 +1,5 @@
/**
* Provides precicates for reasoning about which strings are matched by a regular expression,
* Provides predicates for reasoning about which strings are matched by a regular expression,
* and for testing which capture groups are filled when a particular regexp matches a string.
*/

View File

@@ -76,7 +76,7 @@ class StateTuple extends TStateTuple {
StateTuple() { this = MkStateTuple(q1, q2, q3) }
/**
* Gest a string repesentation of this tuple.
* Gest a string representation of this tuple.
*/
string toString() { result = "(" + q1 + ", " + q2 + ", " + q3 + ")" }

View File

@@ -23,7 +23,7 @@
### New Queries
* Added a new query, `rb/log-inection`, to detect cases where a malicious user may be able to forge log entries.
* Added a new query, `rb/log-injection`, to detect cases where a malicious user may be able to forge log entries.
* Added a new query, `rb/incomplete-multi-character-sanitization`. The query
finds string transformations that do not replace all occurrences of a
multi-character substring.

View File

@@ -0,0 +1,4 @@
---
category: newQuery
---
* Added a new query, `rb/sensitive-get-query`, to detect cases where sensitive data is read from the query parameters of an HTTP `GET` request.

View File

@@ -0,0 +1,4 @@
---
category: newQuery
---
* Added a new query, `rb/non-constant-kernel-open`, to detect uses of Kernel.open and related methods with non-constant values.

View File

@@ -0,0 +1,4 @@
---
category: minorAnalysis
---
* The `rb/path-injection` query now treats the `file:` argument of the Rails `render` method as a sink.

View File

@@ -0,0 +1,5 @@
---
category: minorAnalysis
---
* HTTP response header and body writes via `ActionDispatch::Response` are now
recognized.

View File

@@ -2,7 +2,7 @@
### New Queries
* Added a new query, `rb/log-inection`, to detect cases where a malicious user may be able to forge log entries.
* Added a new query, `rb/log-injection`, to detect cases where a malicious user may be able to forge log entries.
* Added a new query, `rb/incomplete-multi-character-sanitization`. The query
finds string transformations that do not replace all occurrences of a
multi-character substring.

View File

@@ -1,6 +1,6 @@
/**
* @name Manually checking http verb instead of using built in rails routes and protections
* @description Manually checking HTTP verbs is an indication that multiple requests are routed to the same controller action. This could lead to bypassing necessary authorization methods and other protections, like CSRF protection. Prefer using different controller actions for each HTTP method and relying Rails routing to handle mappting resources and verbs to specific methods.
* @description Manually checking HTTP verbs is an indication that multiple requests are routed to the same controller action. This could lead to bypassing necessary authorization methods and other protections, like CSRF protection. Prefer using different controller actions for each HTTP method and relying Rails routing to handle mapping resources and verbs to specific methods.
* @kind path-problem
* @problem.severity error
* @security-severity 5.0
@@ -93,4 +93,4 @@ class HttpVerbConfig extends TaintTracking::Configuration {
from HttpVerbConfig config, DataFlow::PathNode source, DataFlow::PathNode sink
where config.hasFlowPath(source, sink)
select sink.getNode(), source, sink,
"Manually checking HTTP verbs is an indication that multiple requests are routed to the same controller action. This could lead to bypassing necessary authorization methods and other protections, like CSRF protection. Prefer using different controller actions for each HTTP method and relying Rails routing to handle mappting resources and verbs to specific methods."
"Manually checking HTTP verbs is an indication that multiple requests are routed to the same controller action. This could lead to bypassing necessary authorization methods and other protections, like CSRF protection. Prefer using different controller actions for each HTTP method and relying Rails routing to handle mapping resources and verbs to specific methods."

View File

@@ -0,0 +1,46 @@
<!DOCTYPE qhelp PUBLIC
"-//Semmle//qhelp//EN"
"qhelp.dtd">
<qhelp>
<overview>
<p>If <code>Kernel.open</code> is given a file name that starts with a <code>|</code>
character, it will execute the remaining string as a shell command. If a
malicious user can control the file name, they can execute arbitrary code.
The same vulnerability applies to <code>IO.read</code>.
</p>
</overview>
<recommendation>
<p>Use <code>File.open</code> instead of <code>Kernel.open</code>, as the former
does not have this vulnerability. Similarly, use <code>File.read</code> instead
of <code>IO.read</code>.</p>
</recommendation>
<example>
<p>
The following example shows code that calls <code>Kernel.open</code> on a
user-supplied file path.
</p>
<sample src="examples/kernel_open.rb" />
<p>Instead, <code>File.open</code> should be used, as in the following example.</p>
<sample src="examples/file_open.rb" />
</example>
<references>
<li>
OWASP:
<a href="https://www.owasp.org/index.php/Command_Injection">Command Injection</a>.
</li>
<li>
Example CVE: <a href="https://www.ruby-lang.org/en/news/2021/05/02/os-command-injection-in-rdoc/">Command Injection in RDoc</a>.
</li>
</references>
</qhelp>

View File

@@ -1,46 +1,4 @@
<!DOCTYPE qhelp PUBLIC
"-//Semmle//qhelp//EN"
"qhelp.dtd">
<!DOCTYPE qhelp PUBLIC "-//Semmle//qhelp//EN" "qhelp.dtd">
<qhelp>
<overview>
<p>If <code>Kernel.open</code> is given a file name that starts with a <code>|</code>
character, it will execute the remaining string as a shell command. If a
malicious user can control the file name, they can execute arbitrary code.
The same vulnerability applies to <code>IO.read</code>.
</p>
</overview>
<recommendation>
<p>Use <code>File.open</code> instead of <code>Kernel.open</code>, as the former
does not have this vulnerability. Similarly, use <code>File.read</code> instead
of <code>IO.read</code>.</p>
</recommendation>
<example>
<p>
The following example shows code that calls <code>Kernel.open</code> on a
user-supplied file path.
</p>
<sample src="examples/kernel_open.rb" />
<p>Instead, <code>File.open</code> should be used, as in the following example.</p>
<sample src="examples/file_open.rb" />
</example>
<references>
<li>
OWASP:
<a href="https://www.owasp.org/index.php/Command_Injection">Command Injection</a>.
</li>
<li>
Example CVE: <a href="https://www.ruby-lang.org/en/news/2021/05/02/os-command-injection-in-rdoc/">Command Injection in RDoc</a>.
</li>
</references>
</qhelp>
<include src="KernelOpen.inc.qhelp" />
</qhelp>

View File

@@ -1,5 +1,5 @@
/**
* @name Use of `Kernel.open` or `IO.read`
* @name Use of `Kernel.open` or `IO.read` with user-controlled input
* @description Using `Kernel.open` or `IO.read` may allow a malicious
* user to execute arbitrary system commands.
* @kind path-problem
@@ -14,39 +14,12 @@
* external/cwe/cwe-073
*/
import codeql.ruby.AST
import codeql.ruby.ApiGraphs
import codeql.ruby.frameworks.core.Kernel::Kernel
import codeql.ruby.TaintTracking
import codeql.ruby.dataflow.BarrierGuards
import codeql.ruby.dataflow.RemoteFlowSources
import codeql.ruby.DataFlow
import codeql.ruby.TaintTracking
import codeql.ruby.dataflow.RemoteFlowSources
import codeql.ruby.dataflow.BarrierGuards
import DataFlow::PathGraph
/**
* A method call that has a suggested replacement.
*/
abstract class Replacement extends DataFlow::CallNode {
abstract string getFrom();
abstract string getTo();
}
class KernelOpenCall extends KernelMethodCall, Replacement {
KernelOpenCall() { this.getMethodName() = "open" }
override string getFrom() { result = "Kernel.open" }
override string getTo() { result = "File.open" }
}
class IOReadCall extends DataFlow::CallNode, Replacement {
IOReadCall() { this = API::getTopLevelMember("IO").getAMethodCall("read") }
override string getFrom() { result = "IO.read" }
override string getTo() { result = "File.read" }
}
import codeql.ruby.security.KernelOpenQuery
class Configuration extends TaintTracking::Configuration {
Configuration() { this = "KernelOpen" }
@@ -54,9 +27,7 @@ class Configuration extends TaintTracking::Configuration {
override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource }
override predicate isSink(DataFlow::Node sink) {
exists(KernelOpenCall c | c.getArgument(0) = sink)
or
exists(IOReadCall c | c.getArgument(0) = sink)
sink = any(AmbiguousPathCall r).getPathArgument()
}
override predicate isSanitizer(DataFlow::Node node) {
@@ -73,5 +44,6 @@ where
sourceNode = source.getNode() and
call.getArgument(0) = sink.getNode()
select sink.getNode(), source, sink,
"This call to " + call.(Replacement).getFrom() + " depends on a $@. Replace it with " +
call.(Replacement).getTo() + ".", source.getNode(), "user-provided value"
"This call to " + call.(AmbiguousPathCall).getName() +
" depends on a $@. Consider replacing it with " + call.(AmbiguousPathCall).getReplacement() +
".", source.getNode(), "user-provided value"

View File

@@ -0,0 +1,4 @@
<!DOCTYPE qhelp PUBLIC "-//Semmle//qhelp//EN" "qhelp.dtd">
<qhelp>
<include src="KernelOpen.inc.qhelp" />
</qhelp>

View File

@@ -0,0 +1,29 @@
/**
* @name Use of `Kernel.open` or `IO.read` with a non-constant value
* @description Using `Kernel.open` or `IO.read` may allow a malicious
* user to execute arbitrary system commands.
* @kind problem
* @problem.severity warning
* @security-severity 6.5
* @precision high
* @id rb/non-constant-kernel-open
* @tags correctness
* security
* external/cwe/cwe-078
* external/cwe/cwe-088
* external/cwe/cwe-073
*/
import codeql.ruby.security.KernelOpenQuery
import codeql.ruby.ast.Literal
from AmbiguousPathCall call
where
// there is not a constant string argument
not exists(call.getPathArgument().asExpr().getExpr().getConstantValue()) and
// if it's a format string, then the first argument is not a constant string
not call.getPathArgument().getALocalSource().asExpr().getExpr().(StringLiteral).getComponent(0)
instanceof StringTextComponent
select call,
"Call to " + call.getName() + " with a non-constant value. Consider replacing it with " +
call.getReplacement() + "."

View File

@@ -0,0 +1,43 @@
<!DOCTYPE qhelp PUBLIC "-//Semmle//qhelp//EN" "qhelp.dtd">
<qhelp>
<overview>
<p>
Sensitive information such as passwords should not be transmitted within the query string of the requested URL.
Sensitive information within URLs may be logged in various locations, including the user's browser, the web server,
and any proxy servers between the two endpoints. URLs may also be displayed on-screen, bookmarked
or emailed around by users. They may be disclosed to third parties via the Referer header when any off-site links are
followed. Placing sensitive information into the URL therefore increases the risk that it will be captured by an attacker.
</p>
</overview>
<recommendation>
<p>
Use HTTP POST to send sensitive information as part of the request body; for example, as form data.
</p>
</recommendation>
<example>
<p>
The following example shows two route handlers that both receive a username and a password.
The first receives this sensitive information from the query parameters of a GET request, which is
transmitted in the URL. The second receives this sensitive information from the request body of a POST request.
</p>
<sample src="examples/routes.rb" />
<sample src="examples/users_controller.rb" />
</example>
<references>
<li>
CWE:
<a href="https://cwe.mitre.org/data/definitions/598.html">CWE-598: Use of GET Request Method with Sensitive Query Strings</a>
</li>
<li>
PortSwigger (Burp):
<a href="https://portswigger.net/kb/issues/00400300_password-submitted-using-get-method">Password Submitted using GET Method</a>
</li>
<li>
OWASP:
<a href="https://owasp.org/www-community/vulnerabilities/Information_exposure_through_query_strings_in_url">Information Exposure through Query Strings in URL</a>
</li>
</references>
</qhelp>

View File

@@ -0,0 +1,23 @@
/**
* @name Sensitive data read from GET request
* @description Placing sensitive data in a GET request increases the risk of
* the data being exposed to an attacker.
* @kind path-problem
* @problem.severity warning
* @security-severity 6.5
* @precision high
* @id rb/sensitive-get-query
* @tags security
* external/cwe/cwe-598
*/
import ruby
import DataFlow::PathGraph
import codeql.ruby.security.SensitiveGetQueryQuery
import codeql.ruby.security.SensitiveActions
from DataFlow::PathNode source, DataFlow::PathNode sink, SensitiveGetQuery::Configuration config
where config.hasFlowPath(source, sink)
select source.getNode(), source, sink,
"$@ for GET requests uses query parameter as sensitive data.",
source.getNode().(SensitiveGetQuery::Source).getHandler(), "Route handler"

View File

@@ -0,0 +1,4 @@
Rails.application.routes.draw do
get "users/login", to: "#login_get" # BAD: sensitive data transmitted through query parameters
post "users/login", to: "users#login_post" # GOOD: sensitive data transmitted in the request body
end

View File

@@ -0,0 +1,16 @@
class UsersController < ActionController::Base
def login_get
password = params[:password]
authenticate_user(params[:username], password)
end
def login_post
password = params[:password]
authenticate_user(params[:username], password)
end
private
def authenticate_user(username, password)
# ... authenticate the user here
end
end

View File

@@ -3696,7 +3696,7 @@ cfg.rb:
#-----| -> exit filter_nil
# 207| filter_nil
#-----| -> exit cfg.rb (normal)
#-----| -> self
# 207| list
#-----| -> list
@@ -3733,6 +3733,35 @@ cfg.rb:
# 209| call to nil?
#-----| -> exit do ... end (normal)
# 213| call to do_something
#-----| -> exit cfg.rb (normal)
# 213| self
#-----| -> do ... end
# 213| do ... end
#-----| -> call to do_something
# 213| enter do ... end
#-----| -> self
# 213| exit do ... end
# 213| exit do ... end (normal)
#-----| -> exit do ... end
# 214| self
#-----| -> call to something
# 214| call to something
#-----| -> self
# 215| call to something_else
#-----| -> exit do ... end (normal)
# 215| self
#-----| -> call to something_else
desugar.rb:
# 1| enter m1
#-----| -> x

View File

@@ -44,6 +44,9 @@ callsWithNoArguments
| cfg.rb:205:1:205:3 | call to foo |
| cfg.rb:208:3:210:5 | call to reject |
| cfg.rb:209:5:209:13 | call to nil? |
| cfg.rb:213:1:216:3 | call to do_something |
| cfg.rb:214:3:214:16 | call to something |
| cfg.rb:215:3:215:16 | call to something_else |
| desugar.rb:6:3:6:7 | call to foo |
| desugar.rb:10:3:10:7 | call to foo |
| desugar.rb:14:3:14:7 | call to foo |

View File

@@ -210,6 +210,11 @@ def filter_nil list
end
end
do_something do
self.something
something_else
end
__END__
Some ignored nonsense

View File

@@ -85,7 +85,7 @@ else
foo
end
if foos.index(foo)r == nil
if foos.index(foo) == nil
foo
else
foo

View File

@@ -1,17 +1,19 @@
actionControllerControllerClasses
| action_controller/input_access.rb:1:1:50:3 | UsersController |
| action_controller/params_flow.rb:1:1:151:3 | MyController |
| active_record/ActiveRecord.rb:23:1:39:3 | FooController |
| active_record/ActiveRecord.rb:41:1:64:3 | BarController |
| active_record/ActiveRecord.rb:66:1:98:3 | BazController |
| active_record/ActiveRecord.rb:100:1:108:3 | AnnotatedController |
| active_storage/active_storage.rb:39:1:45:3 | PostsController |
| app/controllers/comments_controller.rb:1:1:7:3 | CommentsController |
| app/controllers/comments_controller.rb:1:1:40:3 | CommentsController |
| app/controllers/foo/bars_controller.rb:3:1:46:3 | BarsController |
| app/controllers/photos_controller.rb:1:1:4:3 | PhotosController |
| app/controllers/posts_controller.rb:1:1:10:3 | PostsController |
| app/controllers/tags_controller.rb:1:1:2:3 | TagsController |
| app/controllers/users/notifications_controller.rb:2:3:5:5 | NotificationsController |
actionControllerActionMethods
| action_controller/input_access.rb:2:3:49:5 | index |
| action_controller/params_flow.rb:2:3:4:5 | m1 |
| action_controller/params_flow.rb:6:3:8:5 | m2 |
| action_controller/params_flow.rb:10:3:12:5 | m2 |
@@ -59,8 +61,8 @@ actionControllerActionMethods
| active_record/ActiveRecord.rb:101:3:103:5 | index |
| active_record/ActiveRecord.rb:105:3:107:5 | unsafe_action |
| active_storage/active_storage.rb:40:3:44:5 | create |
| app/controllers/comments_controller.rb:2:3:3:5 | index |
| app/controllers/comments_controller.rb:5:3:6:5 | show |
| app/controllers/comments_controller.rb:2:3:36:5 | index |
| app/controllers/comments_controller.rb:38:3:39: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 |
@@ -222,6 +224,137 @@ paramsSources
| app/controllers/foo/bars_controller.rb:21:21:21:26 | call to params |
| app/controllers/foo/bars_controller.rb:22:10:22:15 | call to params |
| app/views/foo/bars/show.html.erb:5:9:5:14 | call to params |
httpInputAccesses
| action_controller/input_access.rb:3:5:3:18 | call to params | ActionDispatch::Request#params |
| action_controller/input_access.rb:4:5:4:22 | call to parameters | ActionDispatch::Request#parameters |
| action_controller/input_access.rb:5:5:5:15 | call to GET | ActionDispatch::Request#GET |
| action_controller/input_access.rb:6:5:6:16 | call to POST | ActionDispatch::Request#POST |
| action_controller/input_access.rb:7:5:7:28 | call to query_parameters | ActionDispatch::Request#query_parameters |
| action_controller/input_access.rb:8:5:8:30 | call to request_parameters | ActionDispatch::Request#request_parameters |
| action_controller/input_access.rb:9:5:9:31 | call to filtered_parameters | ActionDispatch::Request#filtered_parameters |
| action_controller/input_access.rb:11:5:11:25 | call to authorization | ActionDispatch::Request#authorization |
| action_controller/input_access.rb:12:5:12:23 | call to script_name | ActionDispatch::Request#script_name |
| action_controller/input_access.rb:13:5:13:21 | call to path_info | ActionDispatch::Request#path_info |
| action_controller/input_access.rb:14:5:14:22 | call to user_agent | ActionDispatch::Request#user_agent |
| action_controller/input_access.rb:15:5:15:19 | call to referer | ActionDispatch::Request#referer |
| action_controller/input_access.rb:16:5:16:20 | call to referrer | ActionDispatch::Request#referrer |
| action_controller/input_access.rb:17:5:17:26 | call to host_authority | ActionDispatch::Request#host_authority |
| action_controller/input_access.rb:18:5:18:24 | call to content_type | ActionDispatch::Request#content_type |
| action_controller/input_access.rb:19:5:19:16 | call to host | ActionDispatch::Request#host |
| action_controller/input_access.rb:20:5:20:20 | call to hostname | ActionDispatch::Request#hostname |
| action_controller/input_access.rb:21:5:21:27 | call to accept_encoding | ActionDispatch::Request#accept_encoding |
| action_controller/input_access.rb:22:5:22:27 | call to accept_language | ActionDispatch::Request#accept_language |
| action_controller/input_access.rb:23:5:23:25 | call to if_none_match | ActionDispatch::Request#if_none_match |
| action_controller/input_access.rb:24:5:24:31 | call to if_none_match_etags | ActionDispatch::Request#if_none_match_etags |
| action_controller/input_access.rb:25:5:25:29 | call to content_mime_type | ActionDispatch::Request#content_mime_type |
| action_controller/input_access.rb:27:5:27:21 | call to authority | ActionDispatch::Request#authority |
| action_controller/input_access.rb:28:5:28:16 | call to host | ActionDispatch::Request#host |
| action_controller/input_access.rb:29:5:29:26 | call to host_authority | ActionDispatch::Request#host_authority |
| action_controller/input_access.rb:30:5:30:26 | call to host_with_port | ActionDispatch::Request#host_with_port |
| action_controller/input_access.rb:31:5:31:20 | call to hostname | ActionDispatch::Request#hostname |
| action_controller/input_access.rb:32:5:32:25 | call to forwarded_for | ActionDispatch::Request#forwarded_for |
| action_controller/input_access.rb:33:5:33:26 | call to forwarded_host | ActionDispatch::Request#forwarded_host |
| action_controller/input_access.rb:34:5:34:16 | call to port | ActionDispatch::Request#port |
| action_controller/input_access.rb:35:5:35:26 | call to forwarded_port | ActionDispatch::Request#forwarded_port |
| action_controller/input_access.rb:37:5:37:22 | call to media_type | ActionDispatch::Request#media_type |
| action_controller/input_access.rb:38:5:38:29 | call to media_type_params | ActionDispatch::Request#media_type_params |
| action_controller/input_access.rb:39:5:39:27 | call to content_charset | ActionDispatch::Request#content_charset |
| action_controller/input_access.rb:40:5:40:20 | call to base_url | ActionDispatch::Request#base_url |
| action_controller/input_access.rb:42:5:42:16 | call to body | ActionDispatch::Request#body |
| action_controller/input_access.rb:43:5:43:20 | call to raw_post | ActionDispatch::Request#raw_post |
| action_controller/input_access.rb:45:5:45:30 | ...[...] | ActionDispatch::Request#env[] |
| action_controller/input_access.rb:47:5:47:39 | ...[...] | ActionDispatch::Request#env[] |
| action_controller/params_flow.rb:3:10:3:15 | call to params | ActionController::Metal#params |
| action_controller/params_flow.rb:7:10:7:15 | call to params | ActionController::Metal#params |
| action_controller/params_flow.rb:11:10:11:15 | call to params | ActionController::Metal#params |
| action_controller/params_flow.rb:15:10:15:15 | call to params | ActionController::Metal#params |
| action_controller/params_flow.rb:19:10:19:15 | call to params | ActionController::Metal#params |
| action_controller/params_flow.rb:23:10:23:15 | call to params | ActionController::Metal#params |
| action_controller/params_flow.rb:27:10:27:15 | call to params | ActionController::Metal#params |
| action_controller/params_flow.rb:31:10:31:15 | call to params | ActionController::Metal#params |
| action_controller/params_flow.rb:35:10:35:15 | call to params | ActionController::Metal#params |
| action_controller/params_flow.rb:39:10:39:15 | call to params | ActionController::Metal#params |
| action_controller/params_flow.rb:43:10:43:15 | call to params | ActionController::Metal#params |
| action_controller/params_flow.rb:47:10:47:15 | call to params | ActionController::Metal#params |
| action_controller/params_flow.rb:51:10:51:15 | call to params | ActionController::Metal#params |
| action_controller/params_flow.rb:55:10:55:15 | call to params | ActionController::Metal#params |
| action_controller/params_flow.rb:59:10:59:15 | call to params | ActionController::Metal#params |
| action_controller/params_flow.rb:63:10:63:15 | call to params | ActionController::Metal#params |
| action_controller/params_flow.rb:67:10:67:15 | call to params | ActionController::Metal#params |
| action_controller/params_flow.rb:71:10:71:15 | call to params | ActionController::Metal#params |
| action_controller/params_flow.rb:75:10:75:15 | call to params | ActionController::Metal#params |
| action_controller/params_flow.rb:79:10:79:15 | call to params | ActionController::Metal#params |
| action_controller/params_flow.rb:83:10:83:15 | call to params | ActionController::Metal#params |
| action_controller/params_flow.rb:87:10:87:15 | call to params | ActionController::Metal#params |
| action_controller/params_flow.rb:91:10:91:15 | call to params | ActionController::Metal#params |
| action_controller/params_flow.rb:95:10:95:15 | call to params | ActionController::Metal#params |
| action_controller/params_flow.rb:99:10:99:15 | call to params | ActionController::Metal#params |
| action_controller/params_flow.rb:103:10:103:15 | call to params | ActionController::Metal#params |
| action_controller/params_flow.rb:107:10:107:15 | call to params | ActionController::Metal#params |
| action_controller/params_flow.rb:111:10:111:15 | call to params | ActionController::Metal#params |
| action_controller/params_flow.rb:112:23:112:28 | call to params | ActionController::Metal#params |
| action_controller/params_flow.rb:116:10:116:15 | call to params | ActionController::Metal#params |
| action_controller/params_flow.rb:117:31:117:36 | call to params | ActionController::Metal#params |
| action_controller/params_flow.rb:121:10:121:15 | call to params | ActionController::Metal#params |
| action_controller/params_flow.rb:122:31:122:36 | call to params | ActionController::Metal#params |
| action_controller/params_flow.rb:126:10:126:15 | call to params | ActionController::Metal#params |
| action_controller/params_flow.rb:127:24:127:29 | call to params | ActionController::Metal#params |
| action_controller/params_flow.rb:130:14:130:19 | call to params | ActionController::Metal#params |
| action_controller/params_flow.rb:135:10:135:15 | call to params | ActionController::Metal#params |
| action_controller/params_flow.rb:136:32:136:37 | call to params | ActionController::Metal#params |
| action_controller/params_flow.rb:139:22:139:27 | call to params | ActionController::Metal#params |
| action_controller/params_flow.rb:144:10:144:15 | call to params | ActionController::Metal#params |
| action_controller/params_flow.rb:145:32:145:37 | call to params | ActionController::Metal#params |
| action_controller/params_flow.rb:148:22:148:27 | call to params | ActionController::Metal#params |
| action_mailer/mailer.rb:3:10:3:15 | call to params | ActionController::Metal#params |
| active_record/ActiveRecord.rb:28:30:28:35 | call to params | ActionController::Metal#params |
| active_record/ActiveRecord.rb:29:29:29:34 | call to params | ActionController::Metal#params |
| active_record/ActiveRecord.rb:30:31:30:36 | call to params | ActionController::Metal#params |
| active_record/ActiveRecord.rb:32:21:32:26 | call to params | ActionController::Metal#params |
| active_record/ActiveRecord.rb:34:34:34:39 | call to params | ActionController::Metal#params |
| active_record/ActiveRecord.rb:35:23:35:28 | call to params | ActionController::Metal#params |
| active_record/ActiveRecord.rb:35:38:35:43 | call to params | ActionController::Metal#params |
| active_record/ActiveRecord.rb:43:10:43:15 | call to params | ActionController::Metal#params |
| active_record/ActiveRecord.rb:50:11:50:16 | call to params | ActionController::Metal#params |
| active_record/ActiveRecord.rb:54:12:54:17 | call to params | ActionController::Metal#params |
| active_record/ActiveRecord.rb:59:12:59:17 | call to params | ActionController::Metal#params |
| active_record/ActiveRecord.rb:62:15:62:20 | call to params | ActionController::Metal#params |
| active_record/ActiveRecord.rb:68:21:68:26 | call to params | ActionController::Metal#params |
| active_record/ActiveRecord.rb:72:18:72:23 | call to params | ActionController::Metal#params |
| active_record/ActiveRecord.rb:76:24:76:29 | call to params | ActionController::Metal#params |
| active_record/ActiveRecord.rb:76:49:76:54 | call to params | ActionController::Metal#params |
| active_record/ActiveRecord.rb:80:25:80:30 | call to params | ActionController::Metal#params |
| active_record/ActiveRecord.rb:80:50:80:55 | call to params | ActionController::Metal#params |
| active_record/ActiveRecord.rb:88:21:88:26 | call to params | ActionController::Metal#params |
| active_record/ActiveRecord.rb:92:27:92:32 | call to params | ActionController::Metal#params |
| active_record/ActiveRecord.rb:92:52:92:57 | call to params | ActionController::Metal#params |
| active_record/ActiveRecord.rb:96:28:96:33 | call to params | ActionController::Metal#params |
| active_record/ActiveRecord.rb:96:53:96:58 | call to params | ActionController::Metal#params |
| active_record/ActiveRecord.rb:106:59:106:64 | call to params | ActionController::Metal#params |
| active_storage/active_storage.rb:41:21:41:26 | call to params | ActionController::Metal#params |
| active_storage/active_storage.rb:42:24:42:29 | call to params | ActionController::Metal#params |
| app/controllers/comments_controller.rb:3:5:3:18 | call to params | ActionDispatch::Request#params |
| app/controllers/comments_controller.rb:4:5:4:22 | call to parameters | ActionDispatch::Request#parameters |
| app/controllers/comments_controller.rb:5:5:5:15 | call to GET | ActionDispatch::Request#GET |
| app/controllers/comments_controller.rb:6:5:6:16 | call to POST | ActionDispatch::Request#POST |
| app/controllers/comments_controller.rb:7:5:7:28 | call to query_parameters | ActionDispatch::Request#query_parameters |
| app/controllers/comments_controller.rb:8:5:8:30 | call to request_parameters | ActionDispatch::Request#request_parameters |
| app/controllers/comments_controller.rb:9:5:9:31 | call to filtered_parameters | ActionDispatch::Request#filtered_parameters |
| app/controllers/foo/bars_controller.rb:10:27:10:33 | call to cookies | ActionController::Metal#cookies |
| app/controllers/foo/bars_controller.rb:13:21:13:26 | call to params | ActionController::Metal#params |
| app/controllers/foo/bars_controller.rb:14:10:14:15 | call to params | ActionController::Metal#params |
| app/controllers/foo/bars_controller.rb:21:21:21:26 | call to params | ActionController::Metal#params |
| app/controllers/foo/bars_controller.rb:22:10:22:15 | call to params | ActionController::Metal#params |
| app/graphql/mutations/dummy.rb:5:24:5:25 | id | GraphQL RoutedParameter |
| app/graphql/mutations/dummy.rb:9:17:9:25 | something | GraphQL RoutedParameter |
| app/graphql/resolvers/dummy_resolver.rb:6:24:6:25 | id | GraphQL RoutedParameter |
| app/graphql/resolvers/dummy_resolver.rb:10:17:10:25 | something | GraphQL RoutedParameter |
| app/graphql/types/query_type.rb:10:18:10:23 | number | GraphQL RoutedParameter |
| app/graphql/types/query_type.rb:18:23:18:33 | blah_number | GraphQL RoutedParameter |
| app/graphql/types/query_type.rb:27:20:27:25 | **args | GraphQL RoutedParameter |
| app/graphql/types/query_type.rb:36:34:36:37 | arg1 | GraphQL RoutedParameter |
| app/graphql/types/query_type.rb:36:41:36:46 | **rest | GraphQL RoutedParameter |
| app/views/foo/bars/show.html.erb:5:9:5:14 | call to params | ActionController::Metal#params |
cookiesCalls
| app/controllers/foo/bars_controller.rb:10:27:10:33 | call to cookies |
cookiesSources
@@ -237,3 +370,19 @@ getAssociatedControllerClasses
controllerTemplateFiles
| app/controllers/foo/bars_controller.rb:3:1:46:3 | BarsController | app/views/foo/bars/_widget.html.erb:0:0:0:0 | app/views/foo/bars/_widget.html.erb |
| app/controllers/foo/bars_controller.rb:3:1:46:3 | BarsController | app/views/foo/bars/show.html.erb:0:0:0:0 | app/views/foo/bars/show.html.erb |
headerWriteAccesses
| app/controllers/comments_controller.rb:15:5:15:35 | call to []= | content-type | app/controllers/comments_controller.rb:15:39:15:49 | ... = ... |
| app/controllers/comments_controller.rb:16:5:16:46 | call to set_header | content-length | app/controllers/comments_controller.rb:16:43:16:45 | 100 |
| app/controllers/comments_controller.rb:17:5:17:39 | call to []= | x-custom-header | app/controllers/comments_controller.rb:17:43:17:46 | ... = ... |
| app/controllers/comments_controller.rb:18:5:18:39 | call to []= | x-another-custom-header | app/controllers/comments_controller.rb:18:43:18:47 | ... = ... |
| app/controllers/comments_controller.rb:19:5:19:49 | call to add_header | x-yet-another | app/controllers/comments_controller.rb:19:42:19:49 | "indeed" |
| app/controllers/comments_controller.rb:25:5:25:21 | call to location= | location | app/controllers/comments_controller.rb:25:25:25:36 | ... = ... |
| app/controllers/comments_controller.rb:26:5:26:26 | call to cache_control= | cache-control | app/controllers/comments_controller.rb:26:30:26:36 | ... = ... |
| app/controllers/comments_controller.rb:27:5:27:27 | call to _cache_control= | cache-control | app/controllers/comments_controller.rb:27:31:27:37 | ... = ... |
| app/controllers/comments_controller.rb:28:5:28:17 | call to etag= | etag | app/controllers/comments_controller.rb:28:21:28:27 | ... = ... |
| app/controllers/comments_controller.rb:29:5:29:20 | call to charset= | content-type | app/controllers/comments_controller.rb:29:24:29:30 | ... = ... |
| app/controllers/comments_controller.rb:30:5:30:25 | call to content_type= | content-type | app/controllers/comments_controller.rb:30:29:30:35 | ... = ... |
| app/controllers/comments_controller.rb:32:5:32:17 | call to date= | date | app/controllers/comments_controller.rb:32:21:32:30 | ... = ... |
| app/controllers/comments_controller.rb:33:5:33:26 | call to last_modified= | last-modified | app/controllers/comments_controller.rb:33:30:33:43 | ... = ... |
| app/controllers/comments_controller.rb:34:5:34:22 | call to weak_etag= | etag | app/controllers/comments_controller.rb:34:26:34:32 | ... = ... |
| app/controllers/comments_controller.rb:35:5:35:24 | call to strong_etag= | etag | app/controllers/comments_controller.rb:35:28:35:34 | ... = ... |

View File

@@ -1,6 +1,9 @@
private import codeql.ruby.AST
private import codeql.ruby.frameworks.ActionController
private import codeql.ruby.frameworks.Rails
private import codeql.ruby.frameworks.ActionView
private import codeql.ruby.Concepts
private import codeql.ruby.DataFlow
query predicate actionControllerControllerClasses(ActionControllerControllerClass cls) { any() }
@@ -10,6 +13,10 @@ query predicate paramsCalls(Rails::ParamsCall c) { any() }
query predicate paramsSources(ParamsSource src) { any() }
query predicate httpInputAccesses(Http::Server::RequestInputAccess a, string sourceType) {
sourceType = a.getSourceType()
}
query predicate cookiesCalls(Rails::CookiesCall c) { any() }
query predicate cookiesSources(CookiesSource src) { any() }
@@ -25,3 +32,9 @@ query predicate getAssociatedControllerClasses(ActionControllerControllerClass c
query predicate controllerTemplateFiles(ActionControllerControllerClass cls, ErbFile templateFile) {
controllerTemplateFile(cls, templateFile)
}
query predicate headerWriteAccesses(
Http::Server::HeaderWriteAccess a, string name, DataFlow::Node value
) {
name = a.getName() and value = a.getValue()
}

View File

@@ -36,8 +36,8 @@ actionDispatchRoutes
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:3:5:6:7 | call to resources | app/controllers/comments_controller.rb:2:3:36:5 | index |
| app/config/routes.rb:3:5:6:7 | call to resources | app/controllers/comments_controller.rb:38:3:39: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 |

View File

@@ -1,6 +1,3 @@
htmlSafeCalls
| app/views/foo/bars/show.html.erb:23:3:23:25 | call to html_safe |
| app/views/foo/bars/show.html.erb:27:3:27:25 | call to html_safe |
rawCalls
| app/views/foo/bars/_widget.html.erb:1:5:1:21 | call to raw |
| app/views/foo/bars/_widget.html.erb:2:5:2:20 | call to raw |
@@ -24,6 +21,8 @@ renderToCalls
linkToCalls
| app/views/foo/bars/show.html.erb:33:5:33:41 | call to link_to |
httpResponses
| app/controllers/comments_controller.rb:11:5:11:17 | call to body= | app/controllers/comments_controller.rb:11:21:11:34 | ... = ... | text/http |
| app/controllers/comments_controller.rb:21:5:21:37 | call to send_file | app/controllers/comments_controller.rb:21:24:21:36 | "my-file.ext" | application/octet-stream |
| app/controllers/foo/bars_controller.rb:15:16:15:97 | call to render_to_string | app/controllers/foo/bars_controller.rb:15:33:15:47 | "foo/bars/show" | text/html |
| app/controllers/foo/bars_controller.rb:23:5:23:76 | call to render | app/controllers/foo/bars_controller.rb:23:12:23:26 | "foo/bars/show" | text/html |
| app/controllers/foo/bars_controller.rb:35:5:35:33 | call to render | app/controllers/foo/bars_controller.rb:35:18:35:33 | call to [] | application/json |

View File

@@ -4,8 +4,6 @@ private import codeql.ruby.frameworks.ActionView
private import codeql.ruby.frameworks.Rails
private import codeql.ruby.Concepts
query predicate htmlSafeCalls(Rails::HtmlSafeCall c) { any() }
query predicate rawCalls(RawCall c) { any() }
query predicate renderCalls(Rails::RenderCall c) { any() }

View File

@@ -0,0 +1,50 @@
class UsersController < ActionController::Base
def index
request.params
request.parameters
request.GET
request.POST
request.query_parameters
request.request_parameters
request.filtered_parameters
request.authorization
request.script_name
request.path_info
request.user_agent
request.referer
request.referrer
request.host_authority
request.content_type
request.host
request.hostname
request.accept_encoding
request.accept_language
request.if_none_match
request.if_none_match_etags
request.content_mime_type
request.authority
request.host
request.host_authority
request.host_with_port
request.hostname
request.forwarded_for
request.forwarded_host
request.port
request.forwarded_port
request.media_type
request.media_type_params
request.content_charset
request.base_url
request.body
request.raw_post
request.env["HTTP_ACCEPT"]
request.env["NOT_USER_CONTROLLED"]
request.filtered_env["HTTP_ACCEPT"]
request.filtered_env["NOT_USER_CONTROLLED"]
end
end

View File

@@ -136,6 +136,14 @@ edges
| active_support.rb:191:34:191:34 | a : | active_support.rb:191:7:191:35 | call to new : |
| active_support.rb:192:7:192:7 | x : | active_support.rb:192:7:192:16 | call to to_param : |
| active_support.rb:192:7:192:16 | call to to_param : | active_support.rb:193:8:193:8 | y |
| active_support.rb:197:7:197:16 | call to source : | active_support.rb:198:20:198:20 | a : |
| active_support.rb:198:7:198:21 | call to new : | active_support.rb:199:7:199:7 | x : |
| active_support.rb:198:20:198:20 | a : | active_support.rb:198:7:198:21 | call to new : |
| active_support.rb:199:7:199:7 | x : | active_support.rb:199:7:199:17 | call to existence : |
| active_support.rb:199:7:199:17 | call to existence : | active_support.rb:200:8:200:8 | y |
| active_support.rb:199:7:199:17 | call to existence : | active_support.rb:201:7:201:7 | y : |
| active_support.rb:201:7:201:7 | y : | active_support.rb:201:7:201:17 | call to existence : |
| active_support.rb:201:7:201:17 | call to existence : | active_support.rb:202:8:202:8 | z |
nodes
| active_support.rb:9:9:9:18 | call to source : | semmle.label | call to source : |
| active_support.rb:10:10:10:10 | x : | semmle.label | x : |
@@ -310,6 +318,15 @@ nodes
| active_support.rb:192:7:192:7 | x : | semmle.label | x : |
| active_support.rb:192:7:192:16 | call to to_param : | semmle.label | call to to_param : |
| active_support.rb:193:8:193:8 | y | semmle.label | y |
| active_support.rb:197:7:197:16 | call to source : | semmle.label | call to source : |
| active_support.rb:198:7:198:21 | call to new : | semmle.label | call to new : |
| active_support.rb:198:20:198:20 | a : | semmle.label | a : |
| active_support.rb:199:7:199:7 | x : | semmle.label | x : |
| active_support.rb:199:7:199:17 | call to existence : | semmle.label | call to existence : |
| active_support.rb:200:8:200:8 | y | semmle.label | y |
| active_support.rb:201:7:201:7 | y : | semmle.label | y : |
| active_support.rb:201:7:201:17 | call to existence : | semmle.label | call to existence : |
| active_support.rb:202:8:202:8 | z | semmle.label | z |
subpaths
#select
| active_support.rb:106:10:106:13 | ...[...] | active_support.rb:104:10:104:17 | call to source : | active_support.rb:106:10:106:13 | ...[...] | $@ | active_support.rb:104:10:104:17 | call to source : | call to source : |

View File

@@ -192,3 +192,12 @@ def m_safe_buffer_to_param
y = x.to_param
sink y # $hasTaintFlow=a
end
def m_pathname_existence
a = source "a"
x = Pathname.new(a)
y = x.existence
sink y # $hasTaintFlow=a
z = y.existence
sink z # $hasTaintFlow=a
end

View File

@@ -1,7 +1,40 @@
class CommentsController < ApplicationController
def index
request.params
request.parameters
request.GET
request.POST
request.query_parameters
request.request_parameters
request.filtered_parameters
response.body = "some content"
response.status = 200
response.header["Content-Type"] = "text/html"
response.set_header("Content-Length", 100)
response.headers["X-Custom-Header"] = "hi"
response["X-Another-Custom-Header"] = "yes"
response.add_header "X-Yet-Another", "indeed"
response.send_file("my-file.ext")
response.request
response.location = "http://..." # relevant for url redirect query
response.cache_control = "value"
response._cache_control = "value"
response.etag = "value"
response.charset = "value" # sets the charset part of the content-type header
response.content_type = "value" # sets the main part of the content-type header
response.date = Date.today
response.last_modified = Date.yesterday
response.weak_etag = "value"
response.strong_etag = "value"
end
def show
end
end
end

View File

@@ -89,27 +89,45 @@ calls.rb:
# 377| SingletonOverride1
#-----| super -> Object
# 404| SingletonOverride2
# 412| SingletonOverride2
#-----| super -> SingletonOverride1
# 421| ConditionalInstanceMethods
# 433| ConditionalInstanceMethods
#-----| super -> Object
# 484| ExtendSingletonMethod
# 496| ExtendSingletonMethod
# 494| ExtendSingletonMethod2
# 506| ExtendSingletonMethod2
# 500| ExtendSingletonMethod3
# 512| ExtendSingletonMethod3
# 513| ProtectedMethodInModule
# 525| ProtectedMethodInModule
# 519| ProtectedMethods
# 531| ProtectedMethods
#-----| super -> Object
#-----| include -> ProtectedMethodInModule
# 538| ProtectedMethodsSub
# 550| ProtectedMethodsSub
#-----| super -> ProtectedMethods
# 564| SingletonUpCall_Base
#-----| super -> Object
# 568| SingletonUpCall_Sub
#-----| super -> SingletonUpCall_Base
# 576| SingletonUpCall_SubSub
#-----| super -> SingletonUpCall_Sub
# 583| SingletonA
#-----| super -> Object
# 596| SingletonB
#-----| super -> SingletonA
# 605| SingletonC
#-----| super -> SingletonA
hello.rb:
# 1| EnglishWords
@@ -204,9 +222,6 @@ modules_rec.rb:
# 1| B::A
#-----| super -> Object
# 4| A::B
#-----| super -> Object
private.rb:
# 1| E
#-----| super -> Object
@@ -218,3 +233,9 @@ private.rb:
# 96| PrivateOverride2
#-----| super -> PrivateOverride1
toplevel_self_singleton.rb:
# 2| A::B
#-----| super -> Object
# 24| Good

View File

@@ -8,7 +8,6 @@ getTarget
| calls.rb:17:1:17:8 | call to bar | calls.rb:13:1:15:3 | bar |
| calls.rb:19:1:19:8 | call to foo | calls.rb:1:1:3:3 | foo |
| calls.rb:19:1:19:8 | call to foo | calls.rb:85:1:89:3 | foo |
| calls.rb:23:9:23:19 | call to singleton_m | calls.rb:25:5:27:7 | singleton_m |
| calls.rb:32:5:32:15 | call to singleton_m | calls.rb:25:5:27:7 | singleton_m |
| calls.rb:33:5:33:20 | call to singleton_m | calls.rb:25:5:27:7 | singleton_m |
| calls.rb:37:1:37:13 | call to singleton_m | calls.rb:25:5:27:7 | singleton_m |
@@ -77,7 +76,6 @@ getTarget
| calls.rb:224:9:224:24 | call to singleton_g | calls.rb:236:1:238:3 | singleton_g |
| calls.rb:224:9:224:24 | call to singleton_g | calls.rb:243:1:245:3 | singleton_g |
| calls.rb:224:9:224:24 | call to singleton_g | calls.rb:251:5:253:7 | singleton_g |
| calls.rb:224:9:224:24 | call to singleton_g | calls.rb:267:1:269:3 | singleton_g |
| calls.rb:228:1:228:22 | call to singleton_a | calls.rb:191:5:194:7 | singleton_a |
| calls.rb:229:1:229:22 | call to singleton_f | calls.rb:218:9:220:11 | singleton_f |
| calls.rb:231:6:231:19 | call to new | calls.rb:117:5:117:16 | new |
@@ -148,73 +146,93 @@ getTarget
| calls.rb:375:1:375:11 | call to instance | calls.rb:368:5:370:7 | instance |
| calls.rb:380:13:380:48 | call to puts | calls.rb:102:5:102:30 | puts |
| calls.rb:384:13:384:22 | call to singleton1 | calls.rb:379:9:381:11 | singleton1 |
| calls.rb:384:13:384:22 | call to singleton1 | calls.rb:406:9:408:11 | singleton1 |
| calls.rb:389:9:389:44 | call to puts | calls.rb:102:5:102:30 | puts |
| calls.rb:393:9:393:18 | call to singleton2 | calls.rb:388:5:390:7 | singleton2 |
| calls.rb:393:9:393:18 | call to singleton2 | calls.rb:411:5:413:7 | singleton2 |
| calls.rb:396:5:396:14 | call to singleton2 | calls.rb:388:5:390:7 | singleton2 |
| calls.rb:399:1:399:29 | call to singleton1 | calls.rb:379:9:381:11 | singleton1 |
| calls.rb:400:1:400:29 | call to singleton2 | calls.rb:388:5:390:7 | singleton2 |
| calls.rb:401:1:401:34 | call to call_singleton1 | calls.rb:383:9:385:11 | call_singleton1 |
| calls.rb:402:1:402:34 | call to call_singleton2 | calls.rb:392:5:394:7 | call_singleton2 |
| calls.rb:407:13:407:48 | call to puts | calls.rb:102:5:102:30 | puts |
| calls.rb:412:9:412:44 | call to puts | calls.rb:102:5:102:30 | puts |
| calls.rb:416:1:416:29 | call to singleton1 | calls.rb:406:9:408:11 | singleton1 |
| calls.rb:417:1:417:29 | call to singleton2 | calls.rb:411:5:413:7 | singleton2 |
| calls.rb:418:1:418:34 | call to call_singleton1 | calls.rb:383:9:385:11 | call_singleton1 |
| calls.rb:419:1:419:34 | call to call_singleton2 | calls.rb:392:5:394:7 | call_singleton2 |
| calls.rb:424:13:424:48 | call to puts | calls.rb:102:5:102:30 | puts |
| calls.rb:429:9:429:44 | call to puts | calls.rb:102:5:102:30 | puts |
| calls.rb:432:13:432:48 | call to puts | calls.rb:102:5:102:30 | puts |
| calls.rb:435:17:435:52 | call to puts | calls.rb:102:5:102:30 | puts |
| calls.rb:443:9:447:11 | call to new | calls.rb:117:5:117:16 | new |
| calls.rb:443:9:447:15 | call to new | calls.rb:117:5:117:16 | new |
| calls.rb:445:17:445:40 | call to puts | calls.rb:102:5:102:30 | puts |
| calls.rb:451:1:451:30 | call to new | calls.rb:117:5:117:16 | new |
| calls.rb:451:1:451:33 | call to m1 | calls.rb:423:9:425:11 | m1 |
| calls.rb:452:1:452:30 | call to new | calls.rb:117:5:117:16 | new |
| calls.rb:453:1:453:30 | call to new | calls.rb:117:5:117:16 | new |
| calls.rb:453:1:453:33 | call to m2 | calls.rb:428:5:440:7 | m2 |
| calls.rb:454:1:454:30 | call to new | calls.rb:117:5:117:16 | new |
| calls.rb:455:1:455:30 | call to new | calls.rb:117:5:117:16 | new |
| calls.rb:456:1:456:30 | call to new | calls.rb:117:5:117:16 | new |
| calls.rb:458:27:476:3 | call to new | calls.rb:117:5:117:16 | new |
| calls.rb:461:13:461:22 | call to puts | calls.rb:102:5:102:30 | puts |
| calls.rb:465:5:469:7 | call to new | calls.rb:117:5:117:16 | new |
| calls.rb:465:5:469:11 | call to new | calls.rb:117:5:117:16 | new |
| calls.rb:467:13:467:22 | call to puts | calls.rb:102:5:102:30 | puts |
| calls.rb:473:13:473:27 | call to puts | calls.rb:102:5:102:30 | puts |
| calls.rb:478:1:478:27 | call to new | calls.rb:117:5:117:16 | new |
| calls.rb:479:1:479:27 | call to new | calls.rb:117:5:117:16 | new |
| calls.rb:480:1:480:27 | call to new | calls.rb:117:5:117:16 | new |
| calls.rb:481:1:481:27 | call to new | calls.rb:117:5:117:16 | new |
| calls.rb:482:1:482:27 | call to new | calls.rb:117:5:117:16 | new |
| calls.rb:492:1:492:31 | call to singleton | calls.rb:485:5:487:7 | singleton |
| calls.rb:498:1:498:32 | call to singleton | calls.rb:485:5:487:7 | singleton |
| calls.rb:505:1:505:32 | call to singleton | calls.rb:485:5:487:7 | singleton |
| calls.rb:511:1:511:13 | call to singleton | calls.rb:485:5:487:7 | singleton |
| calls.rb:520:5:520:35 | call to include | calls.rb:108:5:110:7 | include |
| calls.rb:523:9:523:35 | call to puts | calls.rb:102:5:102:30 | puts |
| calls.rb:527:9:527:11 | call to foo | calls.rb:514:15:516:7 | foo |
| calls.rb:528:9:528:11 | call to bar | calls.rb:522:15:524:7 | bar |
| calls.rb:529:9:529:28 | call to new | calls.rb:117:5:117:16 | new |
| calls.rb:529:9:529:32 | call to foo | calls.rb:514:15:516:7 | foo |
| calls.rb:530:9:530:28 | call to new | calls.rb:117:5:117:16 | new |
| calls.rb:530:9:530:32 | call to bar | calls.rb:522:15:524:7 | bar |
| calls.rb:534:1:534:20 | call to new | calls.rb:117:5:117:16 | new |
| calls.rb:535:1:535:20 | call to new | calls.rb:117:5:117:16 | new |
| calls.rb:536:1:536:20 | call to new | calls.rb:117:5:117:16 | new |
| calls.rb:536:1:536:24 | call to baz | calls.rb:526:5:531:7 | baz |
| calls.rb:540:9:540:11 | call to foo | calls.rb:514:15:516:7 | foo |
| calls.rb:541:9:541:31 | call to new | calls.rb:117:5:117:16 | new |
| calls.rb:541:9:541:35 | call to foo | calls.rb:514:15:516:7 | foo |
| calls.rb:545:1:545:23 | call to new | calls.rb:117:5:117:16 | new |
| calls.rb:546:1:546:23 | call to new | calls.rb:117:5:117:16 | new |
| calls.rb:547:1:547:23 | call to new | calls.rb:117:5:117:16 | new |
| calls.rb:547:1:547:27 | call to baz | calls.rb:539:5:542:7 | baz |
| calls.rb:549:2:549:6 | call to new | calls.rb:117:5:117:16 | new |
| calls.rb:549:20:549:24 | call to baz | calls.rb:51:5:57:7 | baz |
| calls.rb:550:26:550:37 | call to capitalize | calls.rb:97:5:97:23 | capitalize |
| calls.rb:384:13:384:22 | call to singleton1 | calls.rb:414:9:416:11 | singleton1 |
| calls.rb:388:13:388:20 | call to new | calls.rb:117:5:117:16 | new |
| calls.rb:388:13:388:30 | call to instance1 | calls.rb:402:5:404:7 | instance1 |
| calls.rb:388:13:388:30 | call to instance1 | calls.rb:423:5:425:7 | instance1 |
| calls.rb:393:9:393:44 | call to puts | calls.rb:102:5:102:30 | puts |
| calls.rb:397:9:397:18 | call to singleton2 | calls.rb:392:5:394:7 | singleton2 |
| calls.rb:397:9:397:18 | call to singleton2 | calls.rb:419:5:421:7 | singleton2 |
| calls.rb:400:5:400:14 | call to singleton2 | calls.rb:392:5:394:7 | singleton2 |
| calls.rb:403:9:403:43 | call to puts | calls.rb:102:5:102:30 | puts |
| calls.rb:407:1:407:29 | call to singleton1 | calls.rb:379:9:381:11 | singleton1 |
| calls.rb:408:1:408:29 | call to singleton2 | calls.rb:392:5:394:7 | singleton2 |
| calls.rb:409:1:409:34 | call to call_singleton1 | calls.rb:383:9:385:11 | call_singleton1 |
| calls.rb:410:1:410:34 | call to call_singleton2 | calls.rb:396:5:398:7 | call_singleton2 |
| calls.rb:415:13:415:48 | call to puts | calls.rb:102:5:102:30 | puts |
| calls.rb:420:9:420:44 | call to puts | calls.rb:102:5:102:30 | puts |
| calls.rb:424:9:424:43 | call to puts | calls.rb:102:5:102:30 | puts |
| calls.rb:428:1:428:29 | call to singleton1 | calls.rb:414:9:416:11 | singleton1 |
| calls.rb:429:1:429:29 | call to singleton2 | calls.rb:419:5:421:7 | singleton2 |
| calls.rb:430:1:430:34 | call to call_singleton1 | calls.rb:383:9:385:11 | call_singleton1 |
| calls.rb:431:1:431:34 | call to call_singleton2 | calls.rb:396:5:398:7 | call_singleton2 |
| calls.rb:436:13:436:48 | call to puts | calls.rb:102:5:102:30 | puts |
| calls.rb:441:9:441:44 | call to puts | calls.rb:102:5:102:30 | puts |
| calls.rb:444:13:444:48 | call to puts | calls.rb:102:5:102:30 | puts |
| calls.rb:447:17:447:52 | call to puts | calls.rb:102:5:102:30 | puts |
| calls.rb:455:9:459:11 | call to new | calls.rb:117:5:117:16 | new |
| calls.rb:455:9:459:15 | call to new | calls.rb:117:5:117:16 | new |
| calls.rb:457:17:457:40 | call to puts | calls.rb:102:5:102:30 | puts |
| calls.rb:463:1:463:30 | call to new | calls.rb:117:5:117:16 | new |
| calls.rb:463:1:463:33 | call to m1 | calls.rb:435:9:437:11 | m1 |
| calls.rb:464:1:464:30 | call to new | calls.rb:117:5:117:16 | new |
| calls.rb:465:1:465:30 | call to new | calls.rb:117:5:117:16 | new |
| calls.rb:465:1:465:33 | call to m2 | calls.rb:440:5:452:7 | m2 |
| calls.rb:466:1:466:30 | call to new | calls.rb:117:5:117:16 | new |
| calls.rb:467:1:467:30 | call to new | calls.rb:117:5:117:16 | new |
| calls.rb:468:1:468:30 | call to new | calls.rb:117:5:117:16 | new |
| calls.rb:470:27:488:3 | call to new | calls.rb:117:5:117:16 | new |
| calls.rb:473:13:473:22 | call to puts | calls.rb:102:5:102:30 | puts |
| calls.rb:477:5:481:7 | call to new | calls.rb:117:5:117:16 | new |
| calls.rb:477:5:481:11 | call to new | calls.rb:117:5:117:16 | new |
| calls.rb:479:13:479:22 | call to puts | calls.rb:102:5:102:30 | puts |
| calls.rb:485:13:485:27 | call to puts | calls.rb:102:5:102:30 | puts |
| calls.rb:490:1:490:27 | call to new | calls.rb:117:5:117:16 | new |
| calls.rb:491:1:491:27 | call to new | calls.rb:117:5:117:16 | new |
| calls.rb:492:1:492:27 | call to new | calls.rb:117:5:117:16 | new |
| calls.rb:493:1:493:27 | call to new | calls.rb:117:5:117:16 | new |
| calls.rb:494:1:494:27 | call to new | calls.rb:117:5:117:16 | new |
| calls.rb:504:1:504:31 | call to singleton | calls.rb:497:5:499:7 | singleton |
| calls.rb:510:1:510:32 | call to singleton | calls.rb:497:5:499:7 | singleton |
| calls.rb:517:1:517:32 | call to singleton | calls.rb:497:5:499:7 | singleton |
| calls.rb:523:1:523:13 | call to singleton | calls.rb:497:5:499:7 | singleton |
| calls.rb:532:5:532:35 | call to include | calls.rb:108:5:110:7 | include |
| calls.rb:535:9:535:35 | call to puts | calls.rb:102:5:102:30 | puts |
| calls.rb:539:9:539:11 | call to foo | calls.rb:526:15:528:7 | foo |
| calls.rb:540:9:540:11 | call to bar | calls.rb:534:15:536:7 | bar |
| calls.rb:541:9:541:28 | call to new | calls.rb:117:5:117:16 | new |
| calls.rb:541:9:541:32 | call to foo | calls.rb:526:15:528:7 | foo |
| calls.rb:542:9:542:28 | call to new | calls.rb:117:5:117:16 | new |
| calls.rb:542:9:542:32 | call to bar | calls.rb:534:15:536:7 | bar |
| calls.rb:546:1:546:20 | call to new | calls.rb:117:5:117:16 | new |
| calls.rb:547:1:547:20 | call to new | calls.rb:117:5:117:16 | new |
| calls.rb:548:1:548:20 | call to new | calls.rb:117:5:117:16 | new |
| calls.rb:548:1:548:24 | call to baz | calls.rb:538:5:543:7 | baz |
| calls.rb:552:9:552:11 | call to foo | calls.rb:526:15:528:7 | foo |
| calls.rb:553:9:553:31 | call to new | calls.rb:117:5:117:16 | new |
| calls.rb:553:9:553:35 | call to foo | calls.rb:526:15:528:7 | foo |
| calls.rb:557:1:557:23 | call to new | calls.rb:117:5:117:16 | new |
| calls.rb:558:1:558:23 | call to new | calls.rb:117:5:117:16 | new |
| calls.rb:559:1:559:23 | call to new | calls.rb:117:5:117:16 | new |
| calls.rb:559:1:559:27 | call to baz | calls.rb:551:5:554:7 | baz |
| calls.rb:561:2:561:6 | call to new | calls.rb:117:5:117:16 | new |
| calls.rb:561:20:561:24 | call to baz | calls.rb:51:5:57:7 | baz |
| calls.rb:562:26:562:37 | call to capitalize | calls.rb:97:5:97:23 | capitalize |
| calls.rb:569:5:569:13 | call to singleton | calls.rb:565:5:566:7 | singleton |
| calls.rb:572:9:572:17 | call to singleton | calls.rb:565:5:566:7 | singleton |
| calls.rb:573:9:573:18 | call to singleton2 | calls.rb:577:5:578:7 | singleton2 |
| calls.rb:580:5:580:14 | call to mid_method | calls.rb:571:5:574:7 | mid_method |
| calls.rb:588:9:588:18 | call to singleton1 | calls.rb:584:5:585:7 | singleton1 |
| calls.rb:588:9:588:18 | call to singleton1 | calls.rb:597:5:598:7 | singleton1 |
| calls.rb:588:9:588:18 | call to singleton1 | calls.rb:606:5:607:7 | singleton1 |
| calls.rb:592:9:592:23 | call to call_singleton1 | calls.rb:587:5:589:7 | call_singleton1 |
| calls.rb:592:9:592:23 | call to call_singleton1 | calls.rb:600:5:602:7 | call_singleton1 |
| calls.rb:592:9:592:23 | call to call_singleton1 | calls.rb:609:5:611:7 | call_singleton1 |
| calls.rb:601:9:601:18 | call to singleton1 | calls.rb:597:5:598:7 | singleton1 |
| calls.rb:610:9:610:18 | call to singleton1 | calls.rb:606:5:607:7 | singleton1 |
| calls.rb:614:1:614:31 | call to call_call_singleton1 | calls.rb:591:5:593:7 | call_call_singleton1 |
| calls.rb:615:1:615:31 | call to call_call_singleton1 | calls.rb:591:5:593:7 | call_call_singleton1 |
| calls.rb:616:1:616:31 | call to call_call_singleton1 | calls.rb:591:5:593:7 | call_call_singleton1 |
| hello.rb:12:5:12:24 | call to include | calls.rb:108:5:110:7 | include |
| hello.rb:14:16:14:20 | call to hello | hello.rb:2:5:4:7 | hello |
| hello.rb:20:16:20:20 | call to super | hello.rb:13:5:15:7 | message |
@@ -263,7 +281,10 @@ getTarget
| private.rb:104:1:104:20 | call to new | calls.rb:117:5:117:16 | new |
| private.rb:104:1:104:28 | call to call_m1 | private.rb:91:3:93:5 | call_m1 |
| private.rb:105:1:105:20 | call to new | calls.rb:117:5:117:16 | new |
| toplevel_self_singleton.rb:30:13:30:19 | call to call_me | toplevel_self_singleton.rb:26:9:27:11 | call_me |
| toplevel_self_singleton.rb:31:13:31:20 | call to call_you | toplevel_self_singleton.rb:29:9:32:11 | call_you |
unresolvedCall
| calls.rb:23:9:23:19 | call to singleton_m |
| calls.rb:26:9:26:18 | call to instance_m |
| calls.rb:29:5:29:14 | call to instance_m |
| calls.rb:30:5:30:19 | call to instance_m |
@@ -296,44 +317,45 @@ unresolvedCall
| calls.rb:274:1:274:14 | call to singleton_g |
| calls.rb:276:1:276:14 | call to singleton_g |
| calls.rb:313:9:313:20 | call to instance |
| calls.rb:422:8:422:13 | call to rand |
| calls.rb:422:8:422:17 | ... > ... |
| calls.rb:439:9:439:10 | call to m3 |
| calls.rb:442:8:442:13 | call to rand |
| calls.rb:442:8:442:17 | ... > ... |
| calls.rb:443:9:447:18 | call to m5 |
| calls.rb:452:1:452:33 | call to m3 |
| calls.rb:454:1:454:33 | call to m3 |
| calls.rb:455:1:455:33 | call to m4 |
| calls.rb:456:1:456:33 | call to m5 |
| calls.rb:459:5:459:11 | call to [] |
| calls.rb:459:5:463:7 | call to each |
| calls.rb:465:5:469:15 | call to bar |
| calls.rb:434:8:434:13 | call to rand |
| calls.rb:434:8:434:17 | ... > ... |
| calls.rb:451:9:451:10 | call to m3 |
| calls.rb:454:8:454:13 | call to rand |
| calls.rb:454:8:454:17 | ... > ... |
| calls.rb:455:9:459:18 | call to m5 |
| calls.rb:464:1:464:33 | call to m3 |
| calls.rb:466:1:466:33 | call to m3 |
| calls.rb:467:1:467:33 | call to m4 |
| calls.rb:468:1:468:33 | call to m5 |
| calls.rb:471:5:471:11 | call to [] |
| calls.rb:471:5:475:7 | call to each |
| calls.rb:472:9:474:11 | call to define_method |
| calls.rb:478:1:478:31 | call to foo |
| calls.rb:479:1:479:31 | call to bar |
| calls.rb:480:1:480:33 | call to baz_0 |
| calls.rb:481:1:481:33 | call to baz_1 |
| calls.rb:482:1:482:33 | call to baz_2 |
| calls.rb:486:9:486:46 | call to puts |
| calls.rb:489:5:489:15 | call to extend |
| calls.rb:495:5:495:32 | call to extend |
| calls.rb:503:1:503:51 | call to extend |
| calls.rb:508:1:508:13 | call to singleton |
| calls.rb:509:1:509:32 | call to extend |
| calls.rb:514:5:516:7 | call to protected |
| calls.rb:515:9:515:42 | call to puts |
| calls.rb:522:5:524:7 | call to protected |
| calls.rb:534:1:534:24 | call to foo |
| calls.rb:535:1:535:24 | call to bar |
| calls.rb:545:1:545:27 | call to foo |
| calls.rb:546:1:546:27 | call to bar |
| calls.rb:549:1:549:7 | call to [] |
| calls.rb:549:1:549:26 | call to each |
| calls.rb:550:1:550:13 | call to [] |
| calls.rb:550:1:550:39 | call to each |
| calls.rb:477:5:481:15 | call to bar |
| calls.rb:483:5:483:11 | call to [] |
| calls.rb:483:5:487:7 | call to each |
| calls.rb:484:9:486:11 | call to define_method |
| calls.rb:490:1:490:31 | call to foo |
| calls.rb:491:1:491:31 | call to bar |
| calls.rb:492:1:492:33 | call to baz_0 |
| calls.rb:493:1:493:33 | call to baz_1 |
| calls.rb:494:1:494:33 | call to baz_2 |
| calls.rb:498:9:498:46 | call to puts |
| calls.rb:501:5:501:15 | call to extend |
| calls.rb:507:5:507:32 | call to extend |
| calls.rb:515:1:515:51 | call to extend |
| calls.rb:520:1:520:13 | call to singleton |
| calls.rb:521:1:521:32 | call to extend |
| calls.rb:526:5:528:7 | call to protected |
| calls.rb:527:9:527:42 | call to puts |
| calls.rb:534:5:536:7 | call to protected |
| calls.rb:546:1:546:24 | call to foo |
| calls.rb:547:1:547:24 | call to bar |
| calls.rb:557:1:557:27 | call to foo |
| calls.rb:558:1:558:27 | call to bar |
| calls.rb:561:1:561:7 | call to [] |
| calls.rb:561:1:561:26 | call to each |
| calls.rb:562:1:562:13 | call to [] |
| calls.rb:562:1:562:39 | call to each |
| calls.rb:570:5:570:14 | call to singleton2 |
| hello.rb:20:16:20:26 | ... + ... |
| hello.rb:20:16:20:34 | ... + ... |
| hello.rb:20:16:20:40 | ... + ... |
@@ -347,6 +369,11 @@ unresolvedCall
| private.rb:57:1:57:14 | call to private4 |
| private.rb:100:7:100:29 | call to m1 |
| private.rb:105:1:105:23 | call to m1 |
| toplevel_self_singleton.rb:8:1:16:3 | call to do_something |
| toplevel_self_singleton.rb:10:9:10:27 | call to ab_singleton_method |
| toplevel_self_singleton.rb:14:9:14:27 | call to ab_singleton_method |
| toplevel_self_singleton.rb:18:12:22:1 | call to new |
| toplevel_self_singleton.rb:20:9:20:27 | call to ab_singleton_method |
privateMethod
| calls.rb:1:1:3:3 | foo |
| calls.rb:39:1:41:3 | call_instance_m |
@@ -359,8 +386,8 @@ privateMethod
| calls.rb:278:1:286:3 | create |
| calls.rb:343:1:359:3 | pattern_dispatch |
| calls.rb:367:1:371:3 | add_singleton |
| calls.rb:460:9:462:11 | foo |
| calls.rb:466:9:468:11 | bar |
| calls.rb:472:9:474:11 | foo |
| calls.rb:478:9:480:11 | bar |
| private.rb:2:11:3:5 | private1 |
| private.rb:8:3:9:5 | private2 |
| private.rb:14:3:15:5 | private3 |
@@ -377,6 +404,7 @@ privateMethod
| private.rb:83:11:85:5 | m1 |
| private.rb:87:11:89:5 | m2 |
| private.rb:97:11:101:5 | m1 |
| toplevel_self_singleton.rb:9:5:11:7 | method_in_block |
publicMethod
| calls.rb:7:1:9:3 | bar |
| calls.rb:13:1:15:3 | bar |
@@ -423,18 +451,31 @@ publicMethod
| calls.rb:368:5:370:7 | instance |
| calls.rb:379:9:381:11 | singleton1 |
| calls.rb:383:9:385:11 | call_singleton1 |
| calls.rb:388:5:390:7 | singleton2 |
| calls.rb:392:5:394:7 | call_singleton2 |
| calls.rb:406:9:408:11 | singleton1 |
| calls.rb:411:5:413:7 | singleton2 |
| calls.rb:423:9:425:11 | m1 |
| calls.rb:428:5:440:7 | m2 |
| calls.rb:431:9:437:11 | m3 |
| calls.rb:434:13:436:15 | m4 |
| calls.rb:444:13:446:15 | m5 |
| calls.rb:485:5:487:7 | singleton |
| calls.rb:526:5:531:7 | baz |
| calls.rb:539:5:542:7 | baz |
| calls.rb:387:9:389:11 | factory |
| calls.rb:392:5:394:7 | singleton2 |
| calls.rb:396:5:398:7 | call_singleton2 |
| calls.rb:402:5:404:7 | instance1 |
| calls.rb:414:9:416:11 | singleton1 |
| calls.rb:419:5:421:7 | singleton2 |
| calls.rb:423:5:425:7 | instance1 |
| calls.rb:435:9:437:11 | m1 |
| calls.rb:440:5:452:7 | m2 |
| calls.rb:443:9:449:11 | m3 |
| calls.rb:446:13:448:15 | m4 |
| calls.rb:456:13:458:15 | m5 |
| calls.rb:497:5:499:7 | singleton |
| calls.rb:538:5:543:7 | baz |
| calls.rb:551:5:554:7 | baz |
| calls.rb:565:5:566:7 | singleton |
| calls.rb:571:5:574:7 | mid_method |
| calls.rb:577:5:578:7 | singleton2 |
| calls.rb:584:5:585:7 | singleton1 |
| calls.rb:587:5:589:7 | call_singleton1 |
| calls.rb:591:5:593:7 | call_call_singleton1 |
| calls.rb:597:5:598:7 | singleton1 |
| calls.rb:600:5:602:7 | call_singleton1 |
| calls.rb:606:5:607:7 | singleton1 |
| calls.rb:609:5:611:7 | call_singleton1 |
| hello.rb:2:5:4:7 | hello |
| hello.rb:5:5:7:7 | world |
| hello.rb:13:5:15:7 | message |
@@ -456,8 +497,13 @@ publicMethod
| private.rb:38:3:39:5 | public3 |
| private.rb:66:3:67:5 | public |
| private.rb:91:3:93:5 | call_m1 |
| toplevel_self_singleton.rb:3:9:4:11 | ab_singleton_method |
| toplevel_self_singleton.rb:13:5:15:7 | method_in_block |
| toplevel_self_singleton.rb:19:5:21:7 | method_in_struct |
| toplevel_self_singleton.rb:26:9:27:11 | call_me |
| toplevel_self_singleton.rb:29:9:32:11 | call_you |
protectedMethod
| calls.rb:514:15:516:7 | foo |
| calls.rb:522:15:524:7 | bar |
| calls.rb:526:15:528:7 | foo |
| calls.rb:534:15:536:7 | bar |
| private.rb:32:3:33:5 | protected1 |
| private.rb:35:3:36:5 | protected2 |

View File

@@ -383,6 +383,10 @@ class SingletonOverride1
def call_singleton1
singleton1
end
def factory
self.new.instance1
end
end
def self.singleton2
@@ -394,6 +398,10 @@ class SingletonOverride1
end
singleton2
def instance1
puts "SingletonOverride1#instance1"
end
end
SingletonOverride1.singleton1
@@ -411,6 +419,10 @@ class SingletonOverride2 < SingletonOverride1
def self.singleton2
puts "SingletonOverride2#singleton2"
end
def instance1
puts "SingletonOverride2#instance1"
end
end
SingletonOverride2.singleton1
@@ -548,3 +560,57 @@ ProtectedMethodsSub.new.baz
[C.new].each { |c| c.baz }
["a","b","c"].each { |s| s.capitalize }
class SingletonUpCall_Base
def self.singleton
end
end
class SingletonUpCall_Sub < SingletonUpCall_Base
singleton
singleton2 # should not resolve
def self.mid_method
singleton
singleton2 # should resolve
end
end
class SingletonUpCall_SubSub < SingletonUpCall_Sub
def self.singleton2
end
mid_method
end
class SingletonA
def self.singleton1
end
def self.call_singleton1
singleton1
end
def self.call_call_singleton1
call_singleton1
end
end
class SingletonB < SingletonA
def self.singleton1
end
def self.call_singleton1
singleton1 # should not be able to target `SingletonA:::singleton1` and `SingletonC:::singleton1`
end
end
class SingletonC < SingletonA
def self.singleton1
end
def self.call_singleton1
singleton1 # should not be able to target `SingletonA:::singleton1` and `SingletonB:::singleton1`
end
end
SingletonA.call_call_singleton1
SingletonB.call_call_singleton1
SingletonC.call_call_singleton1

View File

@@ -36,13 +36,15 @@ getMethod
| calls.rb:325:1:329:3 | C1 | instance | calls.rb:326:5:328:7 | instance |
| calls.rb:331:1:335:3 | C2 | instance | calls.rb:332:5:334:7 | instance |
| calls.rb:337:1:341:3 | C3 | instance | calls.rb:338:5:340:7 | instance |
| calls.rb:421:1:449:3 | ConditionalInstanceMethods | m1 | calls.rb:423:9:425:11 | m1 |
| calls.rb:421:1:449:3 | ConditionalInstanceMethods | m2 | calls.rb:428:5:440:7 | m2 |
| calls.rb:484:1:490:3 | ExtendSingletonMethod | singleton | calls.rb:485:5:487:7 | singleton |
| calls.rb:513:1:517:3 | ProtectedMethodInModule | foo | calls.rb:514:15:516:7 | foo |
| calls.rb:519:1:532:3 | ProtectedMethods | bar | calls.rb:522:15:524:7 | bar |
| calls.rb:519:1:532:3 | ProtectedMethods | baz | calls.rb:526:5:531:7 | baz |
| calls.rb:538:1:543:3 | ProtectedMethodsSub | baz | calls.rb:539:5:542:7 | baz |
| calls.rb:377:1:405:3 | SingletonOverride1 | instance1 | calls.rb:402:5:404:7 | instance1 |
| calls.rb:412:1:426:3 | SingletonOverride2 | instance1 | calls.rb:423:5:425:7 | instance1 |
| calls.rb:433:1:461:3 | ConditionalInstanceMethods | m1 | calls.rb:435:9:437:11 | m1 |
| calls.rb:433:1:461:3 | ConditionalInstanceMethods | m2 | calls.rb:440:5:452:7 | m2 |
| calls.rb:496:1:502:3 | ExtendSingletonMethod | singleton | calls.rb:497:5:499:7 | singleton |
| calls.rb:525:1:529:3 | ProtectedMethodInModule | foo | calls.rb:526:15:528:7 | foo |
| calls.rb:531:1:544:3 | ProtectedMethods | bar | calls.rb:534:15:536:7 | bar |
| calls.rb:531:1:544:3 | ProtectedMethods | baz | calls.rb:538:5:543:7 | baz |
| calls.rb:550:1:555:3 | ProtectedMethodsSub | baz | calls.rb:551:5:554:7 | baz |
| hello.rb:1:1:8:3 | EnglishWords | hello | hello.rb:2:5:4:7 | hello |
| hello.rb:1:1:8:3 | EnglishWords | world | hello.rb:5:5:7:7 | world |
| hello.rb:11:1:16:3 | Greeting | message | hello.rb:13:5:15:7 | message |
@@ -314,82 +316,168 @@ lookupMethod
| calls.rb:337:1:341:3 | C3 | private_on_main | calls.rb:185:1:186:3 | private_on_main |
| calls.rb:337:1:341:3 | C3 | puts | calls.rb:102:5:102:30 | puts |
| calls.rb:337:1:341:3 | C3 | to_s | calls.rb:172:5:173:7 | to_s |
| calls.rb:377:1:397:3 | SingletonOverride1 | add_singleton | calls.rb:367:1:371:3 | add_singleton |
| calls.rb:377:1:397:3 | SingletonOverride1 | call_block | calls.rb:81:1:83:3 | call_block |
| calls.rb:377:1:397:3 | SingletonOverride1 | call_instance_m | calls.rb:39:1:41:3 | call_instance_m |
| calls.rb:377:1:397:3 | SingletonOverride1 | create | calls.rb:278:1:286:3 | create |
| calls.rb:377:1:397:3 | SingletonOverride1 | foo | calls.rb:1:1:3:3 | foo |
| calls.rb:377:1:397:3 | SingletonOverride1 | foo | calls.rb:85:1:89:3 | foo |
| calls.rb:377:1:397:3 | SingletonOverride1 | funny | calls.rb:140:1:142:3 | funny |
| calls.rb:377:1:397:3 | SingletonOverride1 | indirect | calls.rb:158:1:160:3 | indirect |
| calls.rb:377:1:397:3 | SingletonOverride1 | new | calls.rb:117:5:117:16 | new |
| calls.rb:377:1:397:3 | SingletonOverride1 | optional_arg | calls.rb:76:1:79:3 | optional_arg |
| calls.rb:377:1:397:3 | SingletonOverride1 | pattern_dispatch | calls.rb:343:1:359:3 | pattern_dispatch |
| calls.rb:377:1:397:3 | SingletonOverride1 | private_on_main | calls.rb:185:1:186:3 | private_on_main |
| calls.rb:377:1:397:3 | SingletonOverride1 | puts | calls.rb:102:5:102:30 | puts |
| calls.rb:377:1:397:3 | SingletonOverride1 | to_s | calls.rb:172:5:173:7 | to_s |
| calls.rb:404:1:414:3 | SingletonOverride2 | add_singleton | calls.rb:367:1:371:3 | add_singleton |
| calls.rb:404:1:414:3 | SingletonOverride2 | call_block | calls.rb:81:1:83:3 | call_block |
| calls.rb:404:1:414:3 | SingletonOverride2 | call_instance_m | calls.rb:39:1:41:3 | call_instance_m |
| calls.rb:404:1:414:3 | SingletonOverride2 | create | calls.rb:278:1:286:3 | create |
| calls.rb:404:1:414:3 | SingletonOverride2 | foo | calls.rb:1:1:3:3 | foo |
| calls.rb:404:1:414:3 | SingletonOverride2 | foo | calls.rb:85:1:89:3 | foo |
| calls.rb:404:1:414:3 | SingletonOverride2 | funny | calls.rb:140:1:142:3 | funny |
| calls.rb:404:1:414:3 | SingletonOverride2 | indirect | calls.rb:158:1:160:3 | indirect |
| calls.rb:404:1:414:3 | SingletonOverride2 | new | calls.rb:117:5:117:16 | new |
| calls.rb:404:1:414:3 | SingletonOverride2 | optional_arg | calls.rb:76:1:79:3 | optional_arg |
| calls.rb:404:1:414:3 | SingletonOverride2 | pattern_dispatch | calls.rb:343:1:359:3 | pattern_dispatch |
| calls.rb:404:1:414:3 | SingletonOverride2 | private_on_main | calls.rb:185:1:186:3 | private_on_main |
| calls.rb:404:1:414:3 | SingletonOverride2 | puts | calls.rb:102:5:102:30 | puts |
| calls.rb:404:1:414:3 | SingletonOverride2 | to_s | calls.rb:172:5:173:7 | to_s |
| calls.rb:421:1:449:3 | ConditionalInstanceMethods | add_singleton | calls.rb:367:1:371:3 | add_singleton |
| calls.rb:421:1:449:3 | ConditionalInstanceMethods | call_block | calls.rb:81:1:83:3 | call_block |
| calls.rb:421:1:449:3 | ConditionalInstanceMethods | call_instance_m | calls.rb:39:1:41:3 | call_instance_m |
| calls.rb:421:1:449:3 | ConditionalInstanceMethods | create | calls.rb:278:1:286:3 | create |
| calls.rb:421:1:449:3 | ConditionalInstanceMethods | foo | calls.rb:1:1:3:3 | foo |
| calls.rb:421:1:449:3 | ConditionalInstanceMethods | foo | calls.rb:85:1:89:3 | foo |
| calls.rb:421:1:449:3 | ConditionalInstanceMethods | funny | calls.rb:140:1:142:3 | funny |
| calls.rb:421:1:449:3 | ConditionalInstanceMethods | indirect | calls.rb:158:1:160:3 | indirect |
| calls.rb:421:1:449:3 | ConditionalInstanceMethods | m1 | calls.rb:423:9:425:11 | m1 |
| calls.rb:421:1:449:3 | ConditionalInstanceMethods | m2 | calls.rb:428:5:440:7 | m2 |
| calls.rb:421:1:449:3 | ConditionalInstanceMethods | new | calls.rb:117:5:117:16 | new |
| calls.rb:421:1:449:3 | ConditionalInstanceMethods | optional_arg | calls.rb:76:1:79:3 | optional_arg |
| calls.rb:421:1:449:3 | ConditionalInstanceMethods | pattern_dispatch | calls.rb:343:1:359:3 | pattern_dispatch |
| calls.rb:421:1:449:3 | ConditionalInstanceMethods | private_on_main | calls.rb:185:1:186:3 | private_on_main |
| calls.rb:421:1:449:3 | ConditionalInstanceMethods | puts | calls.rb:102:5:102:30 | puts |
| calls.rb:421:1:449:3 | ConditionalInstanceMethods | to_s | calls.rb:172:5:173:7 | to_s |
| calls.rb:484:1:490:3 | ExtendSingletonMethod | singleton | calls.rb:485:5:487:7 | singleton |
| calls.rb:513:1:517:3 | ProtectedMethodInModule | foo | calls.rb:514:15:516:7 | foo |
| calls.rb:519:1:532:3 | ProtectedMethods | add_singleton | calls.rb:367:1:371:3 | add_singleton |
| calls.rb:519:1:532:3 | ProtectedMethods | bar | calls.rb:522:15:524:7 | bar |
| calls.rb:519:1:532:3 | ProtectedMethods | baz | calls.rb:526:5:531:7 | baz |
| calls.rb:519:1:532:3 | ProtectedMethods | call_block | calls.rb:81:1:83:3 | call_block |
| calls.rb:519:1:532:3 | ProtectedMethods | call_instance_m | calls.rb:39:1:41:3 | call_instance_m |
| calls.rb:519:1:532:3 | ProtectedMethods | create | calls.rb:278:1:286:3 | create |
| calls.rb:519:1:532:3 | ProtectedMethods | foo | calls.rb:514:15:516:7 | foo |
| calls.rb:519:1:532:3 | ProtectedMethods | funny | calls.rb:140:1:142:3 | funny |
| calls.rb:519:1:532:3 | ProtectedMethods | indirect | calls.rb:158:1:160:3 | indirect |
| calls.rb:519:1:532:3 | ProtectedMethods | new | calls.rb:117:5:117:16 | new |
| calls.rb:519:1:532:3 | ProtectedMethods | optional_arg | calls.rb:76:1:79:3 | optional_arg |
| calls.rb:519:1:532:3 | ProtectedMethods | pattern_dispatch | calls.rb:343:1:359:3 | pattern_dispatch |
| calls.rb:519:1:532:3 | ProtectedMethods | private_on_main | calls.rb:185:1:186:3 | private_on_main |
| calls.rb:519:1:532:3 | ProtectedMethods | puts | calls.rb:102:5:102:30 | puts |
| calls.rb:519:1:532:3 | ProtectedMethods | to_s | calls.rb:172:5:173:7 | to_s |
| calls.rb:538:1:543:3 | ProtectedMethodsSub | add_singleton | calls.rb:367:1:371:3 | add_singleton |
| calls.rb:538:1:543:3 | ProtectedMethodsSub | bar | calls.rb:522:15:524:7 | bar |
| calls.rb:538:1:543:3 | ProtectedMethodsSub | baz | calls.rb:539:5:542:7 | baz |
| calls.rb:538:1:543:3 | ProtectedMethodsSub | call_block | calls.rb:81:1:83:3 | call_block |
| calls.rb:538:1:543:3 | ProtectedMethodsSub | call_instance_m | calls.rb:39:1:41:3 | call_instance_m |
| calls.rb:538:1:543:3 | ProtectedMethodsSub | create | calls.rb:278:1:286:3 | create |
| calls.rb:538:1:543:3 | ProtectedMethodsSub | foo | calls.rb:514:15:516:7 | foo |
| calls.rb:538:1:543:3 | ProtectedMethodsSub | funny | calls.rb:140:1:142:3 | funny |
| calls.rb:538:1:543:3 | ProtectedMethodsSub | indirect | calls.rb:158:1:160:3 | indirect |
| calls.rb:538:1:543:3 | ProtectedMethodsSub | new | calls.rb:117:5:117:16 | new |
| calls.rb:538:1:543:3 | ProtectedMethodsSub | optional_arg | calls.rb:76:1:79:3 | optional_arg |
| calls.rb:538:1:543:3 | ProtectedMethodsSub | pattern_dispatch | calls.rb:343:1:359:3 | pattern_dispatch |
| calls.rb:538:1:543:3 | ProtectedMethodsSub | private_on_main | calls.rb:185:1:186:3 | private_on_main |
| calls.rb:538:1:543:3 | ProtectedMethodsSub | puts | calls.rb:102:5:102:30 | puts |
| calls.rb:538:1:543:3 | ProtectedMethodsSub | to_s | calls.rb:172:5:173:7 | to_s |
| calls.rb:377:1:405:3 | SingletonOverride1 | add_singleton | calls.rb:367:1:371:3 | add_singleton |
| calls.rb:377:1:405:3 | SingletonOverride1 | call_block | calls.rb:81:1:83:3 | call_block |
| calls.rb:377:1:405:3 | SingletonOverride1 | call_instance_m | calls.rb:39:1:41:3 | call_instance_m |
| calls.rb:377:1:405:3 | SingletonOverride1 | create | calls.rb:278:1:286:3 | create |
| calls.rb:377:1:405:3 | SingletonOverride1 | foo | calls.rb:1:1:3:3 | foo |
| calls.rb:377:1:405:3 | SingletonOverride1 | foo | calls.rb:85:1:89:3 | foo |
| calls.rb:377:1:405:3 | SingletonOverride1 | funny | calls.rb:140:1:142:3 | funny |
| calls.rb:377:1:405:3 | SingletonOverride1 | indirect | calls.rb:158:1:160:3 | indirect |
| calls.rb:377:1:405:3 | SingletonOverride1 | instance1 | calls.rb:402:5:404:7 | instance1 |
| calls.rb:377:1:405:3 | SingletonOverride1 | new | calls.rb:117:5:117:16 | new |
| calls.rb:377:1:405:3 | SingletonOverride1 | optional_arg | calls.rb:76:1:79:3 | optional_arg |
| calls.rb:377:1:405:3 | SingletonOverride1 | pattern_dispatch | calls.rb:343:1:359:3 | pattern_dispatch |
| calls.rb:377:1:405:3 | SingletonOverride1 | private_on_main | calls.rb:185:1:186:3 | private_on_main |
| calls.rb:377:1:405:3 | SingletonOverride1 | puts | calls.rb:102:5:102:30 | puts |
| calls.rb:377:1:405:3 | SingletonOverride1 | to_s | calls.rb:172:5:173:7 | to_s |
| calls.rb:412:1:426:3 | SingletonOverride2 | add_singleton | calls.rb:367:1:371:3 | add_singleton |
| calls.rb:412:1:426:3 | SingletonOverride2 | call_block | calls.rb:81:1:83:3 | call_block |
| calls.rb:412:1:426:3 | SingletonOverride2 | call_instance_m | calls.rb:39:1:41:3 | call_instance_m |
| calls.rb:412:1:426:3 | SingletonOverride2 | create | calls.rb:278:1:286:3 | create |
| calls.rb:412:1:426:3 | SingletonOverride2 | foo | calls.rb:1:1:3:3 | foo |
| calls.rb:412:1:426:3 | SingletonOverride2 | foo | calls.rb:85:1:89:3 | foo |
| calls.rb:412:1:426:3 | SingletonOverride2 | funny | calls.rb:140:1:142:3 | funny |
| calls.rb:412:1:426:3 | SingletonOverride2 | indirect | calls.rb:158:1:160:3 | indirect |
| calls.rb:412:1:426:3 | SingletonOverride2 | instance1 | calls.rb:423:5:425:7 | instance1 |
| calls.rb:412:1:426:3 | SingletonOverride2 | new | calls.rb:117:5:117:16 | new |
| calls.rb:412:1:426:3 | SingletonOverride2 | optional_arg | calls.rb:76:1:79:3 | optional_arg |
| calls.rb:412:1:426:3 | SingletonOverride2 | pattern_dispatch | calls.rb:343:1:359:3 | pattern_dispatch |
| calls.rb:412:1:426:3 | SingletonOverride2 | private_on_main | calls.rb:185:1:186:3 | private_on_main |
| calls.rb:412:1:426:3 | SingletonOverride2 | puts | calls.rb:102:5:102:30 | puts |
| calls.rb:412:1:426:3 | SingletonOverride2 | to_s | calls.rb:172:5:173:7 | to_s |
| calls.rb:433:1:461:3 | ConditionalInstanceMethods | add_singleton | calls.rb:367:1:371:3 | add_singleton |
| calls.rb:433:1:461:3 | ConditionalInstanceMethods | call_block | calls.rb:81:1:83:3 | call_block |
| calls.rb:433:1:461:3 | ConditionalInstanceMethods | call_instance_m | calls.rb:39:1:41:3 | call_instance_m |
| calls.rb:433:1:461:3 | ConditionalInstanceMethods | create | calls.rb:278:1:286:3 | create |
| calls.rb:433:1:461:3 | ConditionalInstanceMethods | foo | calls.rb:1:1:3:3 | foo |
| calls.rb:433:1:461:3 | ConditionalInstanceMethods | foo | calls.rb:85:1:89:3 | foo |
| calls.rb:433:1:461:3 | ConditionalInstanceMethods | funny | calls.rb:140:1:142:3 | funny |
| calls.rb:433:1:461:3 | ConditionalInstanceMethods | indirect | calls.rb:158:1:160:3 | indirect |
| calls.rb:433:1:461:3 | ConditionalInstanceMethods | m1 | calls.rb:435:9:437:11 | m1 |
| calls.rb:433:1:461:3 | ConditionalInstanceMethods | m2 | calls.rb:440:5:452:7 | m2 |
| calls.rb:433:1:461:3 | ConditionalInstanceMethods | new | calls.rb:117:5:117:16 | new |
| calls.rb:433:1:461:3 | ConditionalInstanceMethods | optional_arg | calls.rb:76:1:79:3 | optional_arg |
| calls.rb:433:1:461:3 | ConditionalInstanceMethods | pattern_dispatch | calls.rb:343:1:359:3 | pattern_dispatch |
| calls.rb:433:1:461:3 | ConditionalInstanceMethods | private_on_main | calls.rb:185:1:186:3 | private_on_main |
| calls.rb:433:1:461:3 | ConditionalInstanceMethods | puts | calls.rb:102:5:102:30 | puts |
| calls.rb:433:1:461:3 | ConditionalInstanceMethods | to_s | calls.rb:172:5:173:7 | to_s |
| calls.rb:496:1:502:3 | ExtendSingletonMethod | singleton | calls.rb:497:5:499:7 | singleton |
| calls.rb:525:1:529:3 | ProtectedMethodInModule | foo | calls.rb:526:15:528:7 | foo |
| calls.rb:531:1:544:3 | ProtectedMethods | add_singleton | calls.rb:367:1:371:3 | add_singleton |
| calls.rb:531:1:544:3 | ProtectedMethods | bar | calls.rb:534:15:536:7 | bar |
| calls.rb:531:1:544:3 | ProtectedMethods | baz | calls.rb:538:5:543:7 | baz |
| calls.rb:531:1:544:3 | ProtectedMethods | call_block | calls.rb:81:1:83:3 | call_block |
| calls.rb:531:1:544:3 | ProtectedMethods | call_instance_m | calls.rb:39:1:41:3 | call_instance_m |
| calls.rb:531:1:544:3 | ProtectedMethods | create | calls.rb:278:1:286:3 | create |
| calls.rb:531:1:544:3 | ProtectedMethods | foo | calls.rb:526:15:528:7 | foo |
| calls.rb:531:1:544:3 | ProtectedMethods | funny | calls.rb:140:1:142:3 | funny |
| calls.rb:531:1:544:3 | ProtectedMethods | indirect | calls.rb:158:1:160:3 | indirect |
| calls.rb:531:1:544:3 | ProtectedMethods | new | calls.rb:117:5:117:16 | new |
| calls.rb:531:1:544:3 | ProtectedMethods | optional_arg | calls.rb:76:1:79:3 | optional_arg |
| calls.rb:531:1:544:3 | ProtectedMethods | pattern_dispatch | calls.rb:343:1:359:3 | pattern_dispatch |
| calls.rb:531:1:544:3 | ProtectedMethods | private_on_main | calls.rb:185:1:186:3 | private_on_main |
| calls.rb:531:1:544:3 | ProtectedMethods | puts | calls.rb:102:5:102:30 | puts |
| calls.rb:531:1:544:3 | ProtectedMethods | to_s | calls.rb:172:5:173:7 | to_s |
| calls.rb:550:1:555:3 | ProtectedMethodsSub | add_singleton | calls.rb:367:1:371:3 | add_singleton |
| calls.rb:550:1:555:3 | ProtectedMethodsSub | bar | calls.rb:534:15:536:7 | bar |
| calls.rb:550:1:555:3 | ProtectedMethodsSub | baz | calls.rb:551:5:554:7 | baz |
| calls.rb:550:1:555:3 | ProtectedMethodsSub | call_block | calls.rb:81:1:83:3 | call_block |
| calls.rb:550:1:555:3 | ProtectedMethodsSub | call_instance_m | calls.rb:39:1:41:3 | call_instance_m |
| calls.rb:550:1:555:3 | ProtectedMethodsSub | create | calls.rb:278:1:286:3 | create |
| calls.rb:550:1:555:3 | ProtectedMethodsSub | foo | calls.rb:526:15:528:7 | foo |
| calls.rb:550:1:555:3 | ProtectedMethodsSub | funny | calls.rb:140:1:142:3 | funny |
| calls.rb:550:1:555:3 | ProtectedMethodsSub | indirect | calls.rb:158:1:160:3 | indirect |
| calls.rb:550:1:555:3 | ProtectedMethodsSub | new | calls.rb:117:5:117:16 | new |
| calls.rb:550:1:555:3 | ProtectedMethodsSub | optional_arg | calls.rb:76:1:79:3 | optional_arg |
| calls.rb:550:1:555:3 | ProtectedMethodsSub | pattern_dispatch | calls.rb:343:1:359:3 | pattern_dispatch |
| calls.rb:550:1:555:3 | ProtectedMethodsSub | private_on_main | calls.rb:185:1:186:3 | private_on_main |
| calls.rb:550:1:555:3 | ProtectedMethodsSub | puts | calls.rb:102:5:102:30 | puts |
| calls.rb:550:1:555:3 | ProtectedMethodsSub | to_s | calls.rb:172:5:173:7 | to_s |
| calls.rb:564:1:567:3 | SingletonUpCall_Base | add_singleton | calls.rb:367:1:371:3 | add_singleton |
| calls.rb:564:1:567:3 | SingletonUpCall_Base | call_block | calls.rb:81:1:83:3 | call_block |
| calls.rb:564:1:567:3 | SingletonUpCall_Base | call_instance_m | calls.rb:39:1:41:3 | call_instance_m |
| calls.rb:564:1:567:3 | SingletonUpCall_Base | create | calls.rb:278:1:286:3 | create |
| calls.rb:564:1:567:3 | SingletonUpCall_Base | foo | calls.rb:1:1:3:3 | foo |
| calls.rb:564:1:567:3 | SingletonUpCall_Base | foo | calls.rb:85:1:89:3 | foo |
| calls.rb:564:1:567:3 | SingletonUpCall_Base | funny | calls.rb:140:1:142:3 | funny |
| calls.rb:564:1:567:3 | SingletonUpCall_Base | indirect | calls.rb:158:1:160:3 | indirect |
| calls.rb:564:1:567:3 | SingletonUpCall_Base | new | calls.rb:117:5:117:16 | new |
| calls.rb:564:1:567:3 | SingletonUpCall_Base | optional_arg | calls.rb:76:1:79:3 | optional_arg |
| calls.rb:564:1:567:3 | SingletonUpCall_Base | pattern_dispatch | calls.rb:343:1:359:3 | pattern_dispatch |
| calls.rb:564:1:567:3 | SingletonUpCall_Base | private_on_main | calls.rb:185:1:186:3 | private_on_main |
| calls.rb:564:1:567:3 | SingletonUpCall_Base | puts | calls.rb:102:5:102:30 | puts |
| calls.rb:564:1:567:3 | SingletonUpCall_Base | to_s | calls.rb:172:5:173:7 | to_s |
| calls.rb:568:1:575:3 | SingletonUpCall_Sub | add_singleton | calls.rb:367:1:371:3 | add_singleton |
| calls.rb:568:1:575:3 | SingletonUpCall_Sub | call_block | calls.rb:81:1:83:3 | call_block |
| calls.rb:568:1:575:3 | SingletonUpCall_Sub | call_instance_m | calls.rb:39:1:41:3 | call_instance_m |
| calls.rb:568:1:575:3 | SingletonUpCall_Sub | create | calls.rb:278:1:286:3 | create |
| calls.rb:568:1:575:3 | SingletonUpCall_Sub | foo | calls.rb:1:1:3:3 | foo |
| calls.rb:568:1:575:3 | SingletonUpCall_Sub | foo | calls.rb:85:1:89:3 | foo |
| calls.rb:568:1:575:3 | SingletonUpCall_Sub | funny | calls.rb:140:1:142:3 | funny |
| calls.rb:568:1:575:3 | SingletonUpCall_Sub | indirect | calls.rb:158:1:160:3 | indirect |
| calls.rb:568:1:575:3 | SingletonUpCall_Sub | new | calls.rb:117:5:117:16 | new |
| calls.rb:568:1:575:3 | SingletonUpCall_Sub | optional_arg | calls.rb:76:1:79:3 | optional_arg |
| calls.rb:568:1:575:3 | SingletonUpCall_Sub | pattern_dispatch | calls.rb:343:1:359:3 | pattern_dispatch |
| calls.rb:568:1:575:3 | SingletonUpCall_Sub | private_on_main | calls.rb:185:1:186:3 | private_on_main |
| calls.rb:568:1:575:3 | SingletonUpCall_Sub | puts | calls.rb:102:5:102:30 | puts |
| calls.rb:568:1:575:3 | SingletonUpCall_Sub | to_s | calls.rb:172:5:173:7 | to_s |
| calls.rb:576:1:581:3 | SingletonUpCall_SubSub | add_singleton | calls.rb:367:1:371:3 | add_singleton |
| calls.rb:576:1:581:3 | SingletonUpCall_SubSub | call_block | calls.rb:81:1:83:3 | call_block |
| calls.rb:576:1:581:3 | SingletonUpCall_SubSub | call_instance_m | calls.rb:39:1:41:3 | call_instance_m |
| calls.rb:576:1:581:3 | SingletonUpCall_SubSub | create | calls.rb:278:1:286:3 | create |
| calls.rb:576:1:581:3 | SingletonUpCall_SubSub | foo | calls.rb:1:1:3:3 | foo |
| calls.rb:576:1:581:3 | SingletonUpCall_SubSub | foo | calls.rb:85:1:89:3 | foo |
| calls.rb:576:1:581:3 | SingletonUpCall_SubSub | funny | calls.rb:140:1:142:3 | funny |
| calls.rb:576:1:581:3 | SingletonUpCall_SubSub | indirect | calls.rb:158:1:160:3 | indirect |
| calls.rb:576:1:581:3 | SingletonUpCall_SubSub | new | calls.rb:117:5:117:16 | new |
| calls.rb:576:1:581:3 | SingletonUpCall_SubSub | optional_arg | calls.rb:76:1:79:3 | optional_arg |
| calls.rb:576:1:581:3 | SingletonUpCall_SubSub | pattern_dispatch | calls.rb:343:1:359:3 | pattern_dispatch |
| calls.rb:576:1:581:3 | SingletonUpCall_SubSub | private_on_main | calls.rb:185:1:186:3 | private_on_main |
| calls.rb:576:1:581:3 | SingletonUpCall_SubSub | puts | calls.rb:102:5:102:30 | puts |
| calls.rb:576:1:581:3 | SingletonUpCall_SubSub | to_s | calls.rb:172:5:173:7 | to_s |
| calls.rb:583:1:594:3 | SingletonA | add_singleton | calls.rb:367:1:371:3 | add_singleton |
| calls.rb:583:1:594:3 | SingletonA | call_block | calls.rb:81:1:83:3 | call_block |
| calls.rb:583:1:594:3 | SingletonA | call_instance_m | calls.rb:39:1:41:3 | call_instance_m |
| calls.rb:583:1:594:3 | SingletonA | create | calls.rb:278:1:286:3 | create |
| calls.rb:583:1:594:3 | SingletonA | foo | calls.rb:1:1:3:3 | foo |
| calls.rb:583:1:594:3 | SingletonA | foo | calls.rb:85:1:89:3 | foo |
| calls.rb:583:1:594:3 | SingletonA | funny | calls.rb:140:1:142:3 | funny |
| calls.rb:583:1:594:3 | SingletonA | indirect | calls.rb:158:1:160:3 | indirect |
| calls.rb:583:1:594:3 | SingletonA | new | calls.rb:117:5:117:16 | new |
| calls.rb:583:1:594:3 | SingletonA | optional_arg | calls.rb:76:1:79:3 | optional_arg |
| calls.rb:583:1:594:3 | SingletonA | pattern_dispatch | calls.rb:343:1:359:3 | pattern_dispatch |
| calls.rb:583:1:594:3 | SingletonA | private_on_main | calls.rb:185:1:186:3 | private_on_main |
| calls.rb:583:1:594:3 | SingletonA | puts | calls.rb:102:5:102:30 | puts |
| calls.rb:583:1:594:3 | SingletonA | to_s | calls.rb:172:5:173:7 | to_s |
| calls.rb:596:1:603:3 | SingletonB | add_singleton | calls.rb:367:1:371:3 | add_singleton |
| calls.rb:596:1:603:3 | SingletonB | call_block | calls.rb:81:1:83:3 | call_block |
| calls.rb:596:1:603:3 | SingletonB | call_instance_m | calls.rb:39:1:41:3 | call_instance_m |
| calls.rb:596:1:603:3 | SingletonB | create | calls.rb:278:1:286:3 | create |
| calls.rb:596:1:603:3 | SingletonB | foo | calls.rb:1:1:3:3 | foo |
| calls.rb:596:1:603:3 | SingletonB | foo | calls.rb:85:1:89:3 | foo |
| calls.rb:596:1:603:3 | SingletonB | funny | calls.rb:140:1:142:3 | funny |
| calls.rb:596:1:603:3 | SingletonB | indirect | calls.rb:158:1:160:3 | indirect |
| calls.rb:596:1:603:3 | SingletonB | new | calls.rb:117:5:117:16 | new |
| calls.rb:596:1:603:3 | SingletonB | optional_arg | calls.rb:76:1:79:3 | optional_arg |
| calls.rb:596:1:603:3 | SingletonB | pattern_dispatch | calls.rb:343:1:359:3 | pattern_dispatch |
| calls.rb:596:1:603:3 | SingletonB | private_on_main | calls.rb:185:1:186:3 | private_on_main |
| calls.rb:596:1:603:3 | SingletonB | puts | calls.rb:102:5:102:30 | puts |
| calls.rb:596:1:603:3 | SingletonB | to_s | calls.rb:172:5:173:7 | to_s |
| calls.rb:605:1:612:3 | SingletonC | add_singleton | calls.rb:367:1:371:3 | add_singleton |
| calls.rb:605:1:612:3 | SingletonC | call_block | calls.rb:81:1:83:3 | call_block |
| calls.rb:605:1:612:3 | SingletonC | call_instance_m | calls.rb:39:1:41:3 | call_instance_m |
| calls.rb:605:1:612:3 | SingletonC | create | calls.rb:278:1:286:3 | create |
| calls.rb:605:1:612:3 | SingletonC | foo | calls.rb:1:1:3:3 | foo |
| calls.rb:605:1:612:3 | SingletonC | foo | calls.rb:85:1:89:3 | foo |
| calls.rb:605:1:612:3 | SingletonC | funny | calls.rb:140:1:142:3 | funny |
| calls.rb:605:1:612:3 | SingletonC | indirect | calls.rb:158:1:160:3 | indirect |
| calls.rb:605:1:612:3 | SingletonC | new | calls.rb:117:5:117:16 | new |
| calls.rb:605:1:612:3 | SingletonC | optional_arg | calls.rb:76:1:79:3 | optional_arg |
| calls.rb:605:1:612:3 | SingletonC | pattern_dispatch | calls.rb:343:1:359:3 | pattern_dispatch |
| calls.rb:605:1:612:3 | SingletonC | private_on_main | calls.rb:185:1:186:3 | private_on_main |
| calls.rb:605:1:612:3 | SingletonC | puts | calls.rb:102:5:102:30 | puts |
| calls.rb:605:1:612:3 | SingletonC | to_s | calls.rb:172:5:173:7 | to_s |
| file://:0:0:0:0 | Class | include | calls.rb:108:5:110:7 | include |
| file://:0:0:0:0 | Class | module_eval | calls.rb:107:5:107:24 | module_eval |
| file://:0:0:0:0 | Class | new | calls.rb:117:5:117:16 | new |
@@ -476,9 +564,6 @@ lookupMethod
| modules_rec.rb:1:1:2:3 | B::A | new | calls.rb:117:5:117:16 | new |
| modules_rec.rb:1:1:2:3 | B::A | puts | calls.rb:102:5:102:30 | puts |
| modules_rec.rb:1:1:2:3 | B::A | to_s | calls.rb:172:5:173:7 | to_s |
| modules_rec.rb:4:1:5:3 | A::B | new | calls.rb:117:5:117:16 | new |
| modules_rec.rb:4:1:5:3 | A::B | puts | calls.rb:102:5:102:30 | puts |
| modules_rec.rb:4:1:5:3 | A::B | to_s | calls.rb:172:5:173:7 | to_s |
| private.rb:1:1:49:3 | E | new | calls.rb:117:5:117:16 | new |
| private.rb:1:1:49:3 | E | private1 | private.rb:2:11:3:5 | private1 |
| private.rb:1:1:49:3 | E | private2 | private.rb:8:3:9:5 | private2 |
@@ -511,6 +596,9 @@ lookupMethod
| private.rb:96:1:102:3 | PrivateOverride2 | private_on_main | private.rb:51:1:52:3 | private_on_main |
| private.rb:96:1:102:3 | PrivateOverride2 | puts | calls.rb:102:5:102:30 | puts |
| private.rb:96:1:102:3 | PrivateOverride2 | to_s | calls.rb:172:5:173:7 | to_s |
| toplevel_self_singleton.rb:2:5:5:7 | A::B | new | calls.rb:117:5:117:16 | new |
| toplevel_self_singleton.rb:2:5:5:7 | A::B | puts | calls.rb:102:5:102:30 | puts |
| toplevel_self_singleton.rb:2:5:5:7 | A::B | to_s | calls.rb:172:5:173:7 | to_s |
enclosingMethod
| calls.rb:2:5:2:14 | call to puts | calls.rb:1:1:3:3 | foo |
| calls.rb:2:5:2:14 | self | calls.rb:1:1:3:3 | foo |
@@ -769,79 +857,102 @@ enclosingMethod
| calls.rb:380:19:380:47 | SingletonOverride1#singleton1 | calls.rb:379:9:381:11 | singleton1 |
| calls.rb:384:13:384:22 | call to singleton1 | calls.rb:383:9:385:11 | call_singleton1 |
| calls.rb:384:13:384:22 | self | calls.rb:383:9:385:11 | call_singleton1 |
| calls.rb:389:9:389:44 | call to puts | calls.rb:388:5:390:7 | singleton2 |
| calls.rb:389:9:389:44 | self | calls.rb:388:5:390:7 | singleton2 |
| calls.rb:389:14:389:44 | "SingletonOverride1#singleton2" | calls.rb:388:5:390:7 | singleton2 |
| calls.rb:389:15:389:43 | SingletonOverride1#singleton2 | calls.rb:388:5:390:7 | singleton2 |
| calls.rb:393:9:393:18 | call to singleton2 | calls.rb:392:5:394:7 | call_singleton2 |
| calls.rb:393:9:393:18 | self | calls.rb:392:5:394:7 | call_singleton2 |
| calls.rb:407:13:407:48 | call to puts | calls.rb:406:9:408:11 | singleton1 |
| calls.rb:407:13:407:48 | self | calls.rb:406:9:408:11 | singleton1 |
| calls.rb:407:18:407:48 | "SingletonOverride2#singleton1" | calls.rb:406:9:408:11 | singleton1 |
| calls.rb:407:19:407:47 | SingletonOverride2#singleton1 | calls.rb:406:9:408:11 | singleton1 |
| calls.rb:412:9:412:44 | call to puts | calls.rb:411:5:413:7 | singleton2 |
| calls.rb:412:9:412:44 | self | calls.rb:411:5:413:7 | singleton2 |
| calls.rb:412:14:412:44 | "SingletonOverride2#singleton2" | calls.rb:411:5:413:7 | singleton2 |
| calls.rb:412:15:412:43 | SingletonOverride2#singleton2 | calls.rb:411:5:413:7 | singleton2 |
| calls.rb:424:13:424:48 | call to puts | calls.rb:423:9:425:11 | m1 |
| calls.rb:424:13:424:48 | self | calls.rb:423:9:425:11 | m1 |
| calls.rb:424:18:424:48 | "ConditionalInstanceMethods#m1" | calls.rb:423:9:425:11 | m1 |
| calls.rb:424:19:424:47 | ConditionalInstanceMethods#m1 | calls.rb:423:9:425:11 | m1 |
| calls.rb:429:9:429:44 | call to puts | calls.rb:428:5:440:7 | m2 |
| calls.rb:429:9:429:44 | self | calls.rb:428:5:440:7 | m2 |
| calls.rb:429:14:429:44 | "ConditionalInstanceMethods#m2" | calls.rb:428:5:440:7 | m2 |
| calls.rb:429:15:429:43 | ConditionalInstanceMethods#m2 | calls.rb:428:5:440:7 | m2 |
| calls.rb:431:9:437:11 | m3 | calls.rb:428:5:440:7 | m2 |
| calls.rb:432:13:432:48 | call to puts | calls.rb:431:9:437:11 | m3 |
| calls.rb:432:13:432:48 | self | calls.rb:431:9:437:11 | m3 |
| calls.rb:432:18:432:48 | "ConditionalInstanceMethods#m3" | calls.rb:431:9:437:11 | m3 |
| calls.rb:432:19:432:47 | ConditionalInstanceMethods#m3 | calls.rb:431:9:437:11 | m3 |
| calls.rb:434:13:436:15 | m4 | calls.rb:431:9:437:11 | m3 |
| calls.rb:435:17:435:52 | call to puts | calls.rb:434:13:436:15 | m4 |
| calls.rb:435:17:435:52 | self | calls.rb:434:13:436:15 | m4 |
| calls.rb:435:22:435:52 | "ConditionalInstanceMethods#m4" | calls.rb:434:13:436:15 | m4 |
| calls.rb:435:23:435:51 | ConditionalInstanceMethods#m4 | calls.rb:434:13:436:15 | m4 |
| calls.rb:439:9:439:10 | call to m3 | calls.rb:428:5:440:7 | m2 |
| calls.rb:439:9:439:10 | self | calls.rb:428:5:440:7 | m2 |
| calls.rb:445:17:445:40 | call to puts | calls.rb:444:13:446:15 | m5 |
| calls.rb:445:17:445:40 | self | calls.rb:444:13:446:15 | m5 |
| calls.rb:445:22:445:40 | "AnonymousClass#m5" | calls.rb:444:13:446:15 | m5 |
| calls.rb:445:23:445:39 | AnonymousClass#m5 | calls.rb:444:13:446:15 | m5 |
| calls.rb:461:13:461:22 | call to puts | calls.rb:460:9:462:11 | foo |
| calls.rb:461:13:461:22 | self | calls.rb:460:9:462:11 | foo |
| calls.rb:461:18:461:22 | "foo" | calls.rb:460:9:462:11 | foo |
| calls.rb:461:19:461:21 | foo | calls.rb:460:9:462:11 | foo |
| calls.rb:467:13:467:22 | call to puts | calls.rb:466:9:468:11 | bar |
| calls.rb:467:13:467:22 | self | calls.rb:466:9:468:11 | bar |
| calls.rb:467:18:467:22 | "bar" | calls.rb:466:9:468:11 | bar |
| calls.rb:467:19:467:21 | bar | calls.rb:466:9:468:11 | bar |
| calls.rb:486:9:486:46 | call to puts | calls.rb:485:5:487:7 | singleton |
| calls.rb:486:9:486:46 | self | calls.rb:485:5:487:7 | singleton |
| calls.rb:486:14:486:46 | "ExtendSingletonMethod#singleton" | calls.rb:485:5:487:7 | singleton |
| calls.rb:486:15:486:45 | ExtendSingletonMethod#singleton | calls.rb:485:5:487:7 | singleton |
| calls.rb:515:9:515:42 | call to puts | calls.rb:514:15:516:7 | foo |
| calls.rb:515:9:515:42 | self | calls.rb:514:15:516:7 | foo |
| calls.rb:515:14:515:42 | "ProtectedMethodInModule#foo" | calls.rb:514:15:516:7 | foo |
| calls.rb:515:15:515:41 | ProtectedMethodInModule#foo | calls.rb:514:15:516:7 | foo |
| calls.rb:523:9:523:35 | call to puts | calls.rb:522:15:524:7 | bar |
| calls.rb:523:9:523:35 | self | calls.rb:522:15:524:7 | bar |
| calls.rb:523:14:523:35 | "ProtectedMethods#bar" | calls.rb:522:15:524:7 | bar |
| calls.rb:523:15:523:34 | ProtectedMethods#bar | calls.rb:522:15:524:7 | bar |
| calls.rb:527:9:527:11 | call to foo | calls.rb:526:5:531:7 | baz |
| calls.rb:527:9:527:11 | self | calls.rb:526:5:531:7 | baz |
| calls.rb:528:9:528:11 | call to bar | calls.rb:526:5:531:7 | baz |
| calls.rb:528:9:528:11 | self | calls.rb:526:5:531:7 | baz |
| calls.rb:529:9:529:24 | ProtectedMethods | calls.rb:526:5:531:7 | baz |
| calls.rb:529:9:529:28 | call to new | calls.rb:526:5:531:7 | baz |
| calls.rb:529:9:529:32 | call to foo | calls.rb:526:5:531:7 | baz |
| calls.rb:530:9:530:24 | ProtectedMethods | calls.rb:526:5:531:7 | baz |
| calls.rb:530:9:530:28 | call to new | calls.rb:526:5:531:7 | baz |
| calls.rb:530:9:530:32 | call to bar | calls.rb:526:5:531:7 | baz |
| calls.rb:540:9:540:11 | call to foo | calls.rb:539:5:542:7 | baz |
| calls.rb:540:9:540:11 | self | calls.rb:539:5:542:7 | baz |
| calls.rb:541:9:541:27 | ProtectedMethodsSub | calls.rb:539:5:542:7 | baz |
| calls.rb:541:9:541:31 | call to new | calls.rb:539:5:542:7 | baz |
| calls.rb:541:9:541:35 | call to foo | calls.rb:539:5:542:7 | baz |
| calls.rb:388:13:388:16 | self | calls.rb:387:9:389:11 | factory |
| calls.rb:388:13:388:20 | call to new | calls.rb:387:9:389:11 | factory |
| calls.rb:388:13:388:30 | call to instance1 | calls.rb:387:9:389:11 | factory |
| calls.rb:393:9:393:44 | call to puts | calls.rb:392:5:394:7 | singleton2 |
| calls.rb:393:9:393:44 | self | calls.rb:392:5:394:7 | singleton2 |
| calls.rb:393:14:393:44 | "SingletonOverride1#singleton2" | calls.rb:392:5:394:7 | singleton2 |
| calls.rb:393:15:393:43 | SingletonOverride1#singleton2 | calls.rb:392:5:394:7 | singleton2 |
| calls.rb:397:9:397:18 | call to singleton2 | calls.rb:396:5:398:7 | call_singleton2 |
| calls.rb:397:9:397:18 | self | calls.rb:396:5:398:7 | call_singleton2 |
| calls.rb:403:9:403:43 | call to puts | calls.rb:402:5:404:7 | instance1 |
| calls.rb:403:9:403:43 | self | calls.rb:402:5:404:7 | instance1 |
| calls.rb:403:14:403:43 | "SingletonOverride1#instance1" | calls.rb:402:5:404:7 | instance1 |
| calls.rb:403:15:403:42 | SingletonOverride1#instance1 | calls.rb:402:5:404:7 | instance1 |
| calls.rb:415:13:415:48 | call to puts | calls.rb:414:9:416:11 | singleton1 |
| calls.rb:415:13:415:48 | self | calls.rb:414:9:416:11 | singleton1 |
| calls.rb:415:18:415:48 | "SingletonOverride2#singleton1" | calls.rb:414:9:416:11 | singleton1 |
| calls.rb:415:19:415:47 | SingletonOverride2#singleton1 | calls.rb:414:9:416:11 | singleton1 |
| calls.rb:420:9:420:44 | call to puts | calls.rb:419:5:421:7 | singleton2 |
| calls.rb:420:9:420:44 | self | calls.rb:419:5:421:7 | singleton2 |
| calls.rb:420:14:420:44 | "SingletonOverride2#singleton2" | calls.rb:419:5:421:7 | singleton2 |
| calls.rb:420:15:420:43 | SingletonOverride2#singleton2 | calls.rb:419:5:421:7 | singleton2 |
| calls.rb:424:9:424:43 | call to puts | calls.rb:423:5:425:7 | instance1 |
| calls.rb:424:9:424:43 | self | calls.rb:423:5:425:7 | instance1 |
| calls.rb:424:14:424:43 | "SingletonOverride2#instance1" | calls.rb:423:5:425:7 | instance1 |
| calls.rb:424:15:424:42 | SingletonOverride2#instance1 | calls.rb:423:5:425:7 | instance1 |
| calls.rb:436:13:436:48 | call to puts | calls.rb:435:9:437:11 | m1 |
| calls.rb:436:13:436:48 | self | calls.rb:435:9:437:11 | m1 |
| calls.rb:436:18:436:48 | "ConditionalInstanceMethods#m1" | calls.rb:435:9:437:11 | m1 |
| calls.rb:436:19:436:47 | ConditionalInstanceMethods#m1 | calls.rb:435:9:437:11 | m1 |
| calls.rb:441:9:441:44 | call to puts | calls.rb:440:5:452:7 | m2 |
| calls.rb:441:9:441:44 | self | calls.rb:440:5:452:7 | m2 |
| calls.rb:441:14:441:44 | "ConditionalInstanceMethods#m2" | calls.rb:440:5:452:7 | m2 |
| calls.rb:441:15:441:43 | ConditionalInstanceMethods#m2 | calls.rb:440:5:452:7 | m2 |
| calls.rb:443:9:449:11 | m3 | calls.rb:440:5:452:7 | m2 |
| calls.rb:444:13:444:48 | call to puts | calls.rb:443:9:449:11 | m3 |
| calls.rb:444:13:444:48 | self | calls.rb:443:9:449:11 | m3 |
| calls.rb:444:18:444:48 | "ConditionalInstanceMethods#m3" | calls.rb:443:9:449:11 | m3 |
| calls.rb:444:19:444:47 | ConditionalInstanceMethods#m3 | calls.rb:443:9:449:11 | m3 |
| calls.rb:446:13:448:15 | m4 | calls.rb:443:9:449:11 | m3 |
| calls.rb:447:17:447:52 | call to puts | calls.rb:446:13:448:15 | m4 |
| calls.rb:447:17:447:52 | self | calls.rb:446:13:448:15 | m4 |
| calls.rb:447:22:447:52 | "ConditionalInstanceMethods#m4" | calls.rb:446:13:448:15 | m4 |
| calls.rb:447:23:447:51 | ConditionalInstanceMethods#m4 | calls.rb:446:13:448:15 | m4 |
| calls.rb:451:9:451:10 | call to m3 | calls.rb:440:5:452:7 | m2 |
| calls.rb:451:9:451:10 | self | calls.rb:440:5:452:7 | m2 |
| calls.rb:457:17:457:40 | call to puts | calls.rb:456:13:458:15 | m5 |
| calls.rb:457:17:457:40 | self | calls.rb:456:13:458:15 | m5 |
| calls.rb:457:22:457:40 | "AnonymousClass#m5" | calls.rb:456:13:458:15 | m5 |
| calls.rb:457:23:457:39 | AnonymousClass#m5 | calls.rb:456:13:458:15 | m5 |
| calls.rb:473:13:473:22 | call to puts | calls.rb:472:9:474:11 | foo |
| calls.rb:473:13:473:22 | self | calls.rb:472:9:474:11 | foo |
| calls.rb:473:18:473:22 | "foo" | calls.rb:472:9:474:11 | foo |
| calls.rb:473:19:473:21 | foo | calls.rb:472:9:474:11 | foo |
| calls.rb:479:13:479:22 | call to puts | calls.rb:478:9:480:11 | bar |
| calls.rb:479:13:479:22 | self | calls.rb:478:9:480:11 | bar |
| calls.rb:479:18:479:22 | "bar" | calls.rb:478:9:480:11 | bar |
| calls.rb:479:19:479:21 | bar | calls.rb:478:9:480:11 | bar |
| calls.rb:498:9:498:46 | call to puts | calls.rb:497:5:499:7 | singleton |
| calls.rb:498:9:498:46 | self | calls.rb:497:5:499:7 | singleton |
| calls.rb:498:14:498:46 | "ExtendSingletonMethod#singleton" | calls.rb:497:5:499:7 | singleton |
| calls.rb:498:15:498:45 | ExtendSingletonMethod#singleton | calls.rb:497:5:499:7 | singleton |
| calls.rb:527:9:527:42 | call to puts | calls.rb:526:15:528:7 | foo |
| calls.rb:527:9:527:42 | self | calls.rb:526:15:528:7 | foo |
| calls.rb:527:14:527:42 | "ProtectedMethodInModule#foo" | calls.rb:526:15:528:7 | foo |
| calls.rb:527:15:527:41 | ProtectedMethodInModule#foo | calls.rb:526:15:528:7 | foo |
| calls.rb:535:9:535:35 | call to puts | calls.rb:534:15:536:7 | bar |
| calls.rb:535:9:535:35 | self | calls.rb:534:15:536:7 | bar |
| calls.rb:535:14:535:35 | "ProtectedMethods#bar" | calls.rb:534:15:536:7 | bar |
| calls.rb:535:15:535:34 | ProtectedMethods#bar | calls.rb:534:15:536:7 | bar |
| calls.rb:539:9:539:11 | call to foo | calls.rb:538:5:543:7 | baz |
| calls.rb:539:9:539:11 | self | calls.rb:538:5:543:7 | baz |
| calls.rb:540:9:540:11 | call to bar | calls.rb:538:5:543:7 | baz |
| calls.rb:540:9:540:11 | self | calls.rb:538:5:543:7 | baz |
| calls.rb:541:9:541:24 | ProtectedMethods | calls.rb:538:5:543:7 | baz |
| calls.rb:541:9:541:28 | call to new | calls.rb:538:5:543:7 | baz |
| calls.rb:541:9:541:32 | call to foo | calls.rb:538:5:543:7 | baz |
| calls.rb:542:9:542:24 | ProtectedMethods | calls.rb:538:5:543:7 | baz |
| calls.rb:542:9:542:28 | call to new | calls.rb:538:5:543:7 | baz |
| calls.rb:542:9:542:32 | call to bar | calls.rb:538:5:543:7 | baz |
| calls.rb:552:9:552:11 | call to foo | calls.rb:551:5:554:7 | baz |
| calls.rb:552:9:552:11 | self | calls.rb:551:5:554:7 | baz |
| calls.rb:553:9:553:27 | ProtectedMethodsSub | calls.rb:551:5:554:7 | baz |
| calls.rb:553:9:553:31 | call to new | calls.rb:551:5:554:7 | baz |
| calls.rb:553:9:553:35 | call to foo | calls.rb:551:5:554:7 | baz |
| calls.rb:572:9:572:17 | call to singleton | calls.rb:571:5:574:7 | mid_method |
| calls.rb:572:9:572:17 | self | calls.rb:571:5:574:7 | mid_method |
| calls.rb:573:9:573:18 | call to singleton2 | calls.rb:571:5:574:7 | mid_method |
| calls.rb:573:9:573:18 | self | calls.rb:571:5:574:7 | mid_method |
| calls.rb:588:9:588:18 | call to singleton1 | calls.rb:587:5:589:7 | call_singleton1 |
| calls.rb:588:9:588:18 | self | calls.rb:587:5:589:7 | call_singleton1 |
| calls.rb:592:9:592:23 | call to call_singleton1 | calls.rb:591:5:593:7 | call_call_singleton1 |
| calls.rb:592:9:592:23 | self | calls.rb:591:5:593:7 | call_call_singleton1 |
| calls.rb:601:9:601:18 | call to singleton1 | calls.rb:600:5:602:7 | call_singleton1 |
| calls.rb:601:9:601:18 | self | calls.rb:600:5:602:7 | call_singleton1 |
| calls.rb:610:9:610:18 | call to singleton1 | calls.rb:609:5:611:7 | call_singleton1 |
| calls.rb:610:9:610:18 | self | calls.rb:609:5:611:7 | call_singleton1 |
| hello.rb:3:9:3:22 | return | hello.rb:2:5:4:7 | hello |
| hello.rb:3:16:3:22 | "hello" | hello.rb:2:5:4:7 | hello |
| hello.rb:3:17:3:21 | hello | hello.rb:2:5:4:7 | hello |
@@ -897,3 +1008,13 @@ enclosingMethod
| private.rb:100:7:100:22 | PrivateOverride1 | private.rb:97:11:101:5 | m1 |
| private.rb:100:7:100:26 | call to new | private.rb:97:11:101:5 | m1 |
| private.rb:100:7:100:29 | call to m1 | private.rb:97:11:101:5 | m1 |
| toplevel_self_singleton.rb:10:9:10:27 | call to ab_singleton_method | toplevel_self_singleton.rb:9:5:11:7 | method_in_block |
| toplevel_self_singleton.rb:10:9:10:27 | self | toplevel_self_singleton.rb:9:5:11:7 | method_in_block |
| toplevel_self_singleton.rb:14:9:14:27 | call to ab_singleton_method | toplevel_self_singleton.rb:13:5:15:7 | method_in_block |
| toplevel_self_singleton.rb:14:9:14:27 | self | toplevel_self_singleton.rb:13:5:15:7 | method_in_block |
| toplevel_self_singleton.rb:20:9:20:27 | call to ab_singleton_method | toplevel_self_singleton.rb:19:5:21:7 | method_in_struct |
| toplevel_self_singleton.rb:20:9:20:27 | self | toplevel_self_singleton.rb:19:5:21:7 | method_in_struct |
| toplevel_self_singleton.rb:30:13:30:19 | call to call_me | toplevel_self_singleton.rb:29:9:32:11 | call_you |
| toplevel_self_singleton.rb:30:13:30:19 | self | toplevel_self_singleton.rb:29:9:32:11 | call_you |
| toplevel_self_singleton.rb:31:13:31:20 | call to call_you | toplevel_self_singleton.rb:29:9:32:11 | call_you |
| toplevel_self_singleton.rb:31:13:31:20 | self | toplevel_self_singleton.rb:29:9:32:11 | call_you |

File diff suppressed because it is too large Load Diff

View File

@@ -85,26 +85,44 @@ calls.rb:
# 377| SingletonOverride1
#-----| -> Object
# 404| SingletonOverride2
# 412| SingletonOverride2
#-----| -> SingletonOverride1
# 421| ConditionalInstanceMethods
# 433| ConditionalInstanceMethods
#-----| -> Object
# 484| ExtendSingletonMethod
# 496| ExtendSingletonMethod
# 494| ExtendSingletonMethod2
# 506| ExtendSingletonMethod2
# 500| ExtendSingletonMethod3
# 512| ExtendSingletonMethod3
# 513| ProtectedMethodInModule
# 525| ProtectedMethodInModule
# 519| ProtectedMethods
# 531| ProtectedMethods
#-----| -> Object
# 538| ProtectedMethodsSub
# 550| ProtectedMethodsSub
#-----| -> ProtectedMethods
# 564| SingletonUpCall_Base
#-----| -> Object
# 568| SingletonUpCall_Sub
#-----| -> SingletonUpCall_Base
# 576| SingletonUpCall_SubSub
#-----| -> SingletonUpCall_Sub
# 583| SingletonA
#-----| -> Object
# 596| SingletonB
#-----| -> SingletonA
# 605| SingletonC
#-----| -> SingletonA
hello.rb:
# 1| EnglishWords
@@ -195,9 +213,6 @@ modules_rec.rb:
# 1| B::A
#-----| -> Object
# 4| A::B
#-----| -> Object
private.rb:
# 1| E
#-----| -> Object
@@ -209,3 +224,9 @@ private.rb:
# 96| PrivateOverride2
#-----| -> PrivateOverride1
toplevel_self_singleton.rb:
# 2| A::B
#-----| -> Object
# 24| Good

View File

@@ -0,0 +1,34 @@
module A
class B
def self.ab_singleton_method # should not be called
end
end
end
do_something do
def method_in_block
ab_singleton_method # should not resolve to anything
end
obj=self
def obj.method_in_block
ab_singleton_method # should not resolve to anything
end
end
MyStruct = Struct.new(:foo, :bar) {
def self.method_in_struct
ab_singleton_method # should not resolve to anything
end
}
module Good
class << self
def call_me
end
def call_you
call_me
call_you
end
end
end

View File

@@ -23,10 +23,10 @@ nodes
| ManuallyCheckHttpVerb.rb:59:10:59:38 | ...[...] | semmle.label | ...[...] |
subpaths
#select
| ManuallyCheckHttpVerb.rb:4:8:4:19 | call to get? | ManuallyCheckHttpVerb.rb:4:8:4:19 | call to get? | ManuallyCheckHttpVerb.rb:4:8:4:19 | call to get? | Manually checking HTTP verbs is an indication that multiple requests are routed to the same controller action. This could lead to bypassing necessary authorization methods and other protections, like CSRF protection. Prefer using different controller actions for each HTTP method and relying Rails routing to handle mappting resources and verbs to specific methods. |
| ManuallyCheckHttpVerb.rb:12:8:12:22 | ... == ... | ManuallyCheckHttpVerb.rb:11:14:11:24 | call to env : | ManuallyCheckHttpVerb.rb:12:8:12:22 | ... == ... | Manually checking HTTP verbs is an indication that multiple requests are routed to the same controller action. This could lead to bypassing necessary authorization methods and other protections, like CSRF protection. Prefer using different controller actions for each HTTP method and relying Rails routing to handle mappting resources and verbs to specific methods. |
| ManuallyCheckHttpVerb.rb:20:8:20:22 | ... == ... | ManuallyCheckHttpVerb.rb:19:14:19:35 | call to request_method : | ManuallyCheckHttpVerb.rb:20:8:20:22 | ... == ... | Manually checking HTTP verbs is an indication that multiple requests are routed to the same controller action. This could lead to bypassing necessary authorization methods and other protections, like CSRF protection. Prefer using different controller actions for each HTTP method and relying Rails routing to handle mappting resources and verbs to specific methods. |
| ManuallyCheckHttpVerb.rb:28:8:28:22 | ... == ... | ManuallyCheckHttpVerb.rb:27:14:27:27 | call to method : | ManuallyCheckHttpVerb.rb:28:8:28:22 | ... == ... | Manually checking HTTP verbs is an indication that multiple requests are routed to the same controller action. This could lead to bypassing necessary authorization methods and other protections, like CSRF protection. Prefer using different controller actions for each HTTP method and relying Rails routing to handle mappting resources and verbs to specific methods. |
| ManuallyCheckHttpVerb.rb:36:8:36:22 | ... == ... | ManuallyCheckHttpVerb.rb:35:14:35:39 | call to raw_request_method : | ManuallyCheckHttpVerb.rb:36:8:36:22 | ... == ... | Manually checking HTTP verbs is an indication that multiple requests are routed to the same controller action. This could lead to bypassing necessary authorization methods and other protections, like CSRF protection. Prefer using different controller actions for each HTTP method and relying Rails routing to handle mappting resources and verbs to specific methods. |
| ManuallyCheckHttpVerb.rb:52:10:52:23 | ... == ... | ManuallyCheckHttpVerb.rb:51:16:51:44 | call to request_method_symbol : | ManuallyCheckHttpVerb.rb:52:10:52:23 | ... == ... | Manually checking HTTP verbs is an indication that multiple requests are routed to the same controller action. This could lead to bypassing necessary authorization methods and other protections, like CSRF protection. Prefer using different controller actions for each HTTP method and relying Rails routing to handle mappting resources and verbs to specific methods. |
| ManuallyCheckHttpVerb.rb:59:10:59:38 | ...[...] | ManuallyCheckHttpVerb.rb:59:10:59:20 | call to env : | ManuallyCheckHttpVerb.rb:59:10:59:38 | ...[...] | Manually checking HTTP verbs is an indication that multiple requests are routed to the same controller action. This could lead to bypassing necessary authorization methods and other protections, like CSRF protection. Prefer using different controller actions for each HTTP method and relying Rails routing to handle mappting resources and verbs to specific methods. |
| ManuallyCheckHttpVerb.rb:4:8:4:19 | call to get? | ManuallyCheckHttpVerb.rb:4:8:4:19 | call to get? | ManuallyCheckHttpVerb.rb:4:8:4:19 | call to get? | Manually checking HTTP verbs is an indication that multiple requests are routed to the same controller action. This could lead to bypassing necessary authorization methods and other protections, like CSRF protection. Prefer using different controller actions for each HTTP method and relying Rails routing to handle mapping resources and verbs to specific methods. |
| ManuallyCheckHttpVerb.rb:12:8:12:22 | ... == ... | ManuallyCheckHttpVerb.rb:11:14:11:24 | call to env : | ManuallyCheckHttpVerb.rb:12:8:12:22 | ... == ... | Manually checking HTTP verbs is an indication that multiple requests are routed to the same controller action. This could lead to bypassing necessary authorization methods and other protections, like CSRF protection. Prefer using different controller actions for each HTTP method and relying Rails routing to handle mapping resources and verbs to specific methods. |
| ManuallyCheckHttpVerb.rb:20:8:20:22 | ... == ... | ManuallyCheckHttpVerb.rb:19:14:19:35 | call to request_method : | ManuallyCheckHttpVerb.rb:20:8:20:22 | ... == ... | Manually checking HTTP verbs is an indication that multiple requests are routed to the same controller action. This could lead to bypassing necessary authorization methods and other protections, like CSRF protection. Prefer using different controller actions for each HTTP method and relying Rails routing to handle mapping resources and verbs to specific methods. |
| ManuallyCheckHttpVerb.rb:28:8:28:22 | ... == ... | ManuallyCheckHttpVerb.rb:27:14:27:27 | call to method : | ManuallyCheckHttpVerb.rb:28:8:28:22 | ... == ... | Manually checking HTTP verbs is an indication that multiple requests are routed to the same controller action. This could lead to bypassing necessary authorization methods and other protections, like CSRF protection. Prefer using different controller actions for each HTTP method and relying Rails routing to handle mapping resources and verbs to specific methods. |
| ManuallyCheckHttpVerb.rb:36:8:36:22 | ... == ... | ManuallyCheckHttpVerb.rb:35:14:35:39 | call to raw_request_method : | ManuallyCheckHttpVerb.rb:36:8:36:22 | ... == ... | Manually checking HTTP verbs is an indication that multiple requests are routed to the same controller action. This could lead to bypassing necessary authorization methods and other protections, like CSRF protection. Prefer using different controller actions for each HTTP method and relying Rails routing to handle mapping resources and verbs to specific methods. |
| ManuallyCheckHttpVerb.rb:52:10:52:23 | ... == ... | ManuallyCheckHttpVerb.rb:51:16:51:44 | call to request_method_symbol : | ManuallyCheckHttpVerb.rb:52:10:52:23 | ... == ... | Manually checking HTTP verbs is an indication that multiple requests are routed to the same controller action. This could lead to bypassing necessary authorization methods and other protections, like CSRF protection. Prefer using different controller actions for each HTTP method and relying Rails routing to handle mapping resources and verbs to specific methods. |
| ManuallyCheckHttpVerb.rb:59:10:59:38 | ...[...] | ManuallyCheckHttpVerb.rb:59:10:59:20 | call to env : | ManuallyCheckHttpVerb.rb:59:10:59:38 | ...[...] | Manually checking HTTP verbs is an indication that multiple requests are routed to the same controller action. This could lead to bypassing necessary authorization methods and other protections, like CSRF protection. Prefer using different controller actions for each HTTP method and relying Rails routing to handle mapping resources and verbs to specific methods. |

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