Merge branch 'main' into maikypedia/sqli-sink-2

This commit is contained in:
Alex Ford
2023-06-01 13:04:54 +01:00
committed by GitHub
1397 changed files with 119809 additions and 83388 deletions

View File

@@ -10,7 +10,7 @@ runs:
uses: actions/cache@v3
with:
path: ruby/extractor-pack
key: ${{ runner.os }}-${{ steps.os_version.outputs.version }}-extractor-${{ hashFiles('ruby/extractor/rust-toolchain.toml', 'ruby/scripts/create-extractor-pack.sh', 'ruby/extractor/**/Cargo.lock', 'ruby/actions/create-extractor-pack/action.yml') }}-${{ hashFiles('ruby/extractor/**/*.rs') }}-${{ hashFiles('ruby/codeql-extractor.yml', 'ruby/downgrades', 'ruby/tools', 'ruby/ql/lib/ruby.dbscheme', 'ruby/ql/lib/ruby.dbscheme.stats') }}
key: ${{ runner.os }}-${{ steps.os_version.outputs.version }}-extractor-${{ hashFiles('ruby/extractor/rust-toolchain.toml', 'ruby/scripts/create-extractor-pack.sh', 'ruby/extractor/**/Cargo.lock', 'ruby/actions/create-extractor-pack/action.yml') }}-${{ hashFiles('shared/tree-sitter-extractor') }}-${{ hashFiles('ruby/extractor/**/*.rs') }}-${{ hashFiles('ruby/codeql-extractor.yml', 'ruby/downgrades', 'ruby/tools', 'ruby/ql/lib/ruby.dbscheme', 'ruby/ql/lib/ruby.dbscheme.stats') }}
- name: Cache cargo
uses: actions/cache@v3
if: steps.cache-extractor.outputs.cache-hit != 'true'

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,2 @@
description: Sync dbscheme fragments
compatibility: full

View File

@@ -1,45 +1,22 @@
use clap::Args;
use std::env;
use std::path::PathBuf;
use std::process::Command;
use clap::Args;
use codeql_extractor::autobuilder;
#[derive(Args)]
// The autobuilder takes no command-line options, but this may change in the future.
pub struct Options {}
pub fn run(_: Options) -> std::io::Result<()> {
let dist = env::var("CODEQL_DIST").expect("CODEQL_DIST not set");
let db = env::var("CODEQL_EXTRACTOR_RUBY_WIP_DATABASE")
let database = env::var("CODEQL_EXTRACTOR_RUBY_WIP_DATABASE")
.expect("CODEQL_EXTRACTOR_RUBY_WIP_DATABASE not set");
let codeql = if env::consts::OS == "windows" {
"codeql.exe"
} else {
"codeql"
};
let codeql: PathBuf = [&dist, codeql].iter().collect();
let mut cmd = Command::new(codeql);
cmd.arg("database")
.arg("index-files")
.arg("--include-extension=.rb")
.arg("--include-extension=.erb")
.arg("--include-extension=.gemspec")
.arg("--include=**/Gemfile")
.arg("--exclude=**/.git")
.arg("--size-limit=5m")
.arg("--language=ruby")
.arg("--working-dir=.")
.arg(db);
for line in env::var("LGTM_INDEX_FILTERS")
.unwrap_or_default()
.split('\n')
{
if let Some(stripped) = line.strip_prefix("include:") {
cmd.arg("--also-match=".to_owned() + stripped);
} else if let Some(stripped) = line.strip_prefix("exclude:") {
cmd.arg("--exclude=".to_owned() + stripped);
}
}
let exit = &cmd.spawn()?.wait()?;
std::process::exit(exit.code().unwrap_or(1))
autobuilder::Autobuilder::new("ruby", PathBuf::from(database))
.include_extensions(&[".rb", ".erb", ".gemspec"])
.include_globs(&["**/Gemfile"])
.exclude_globs(&["**/.git"])
.size_limit("5m")
.run()
}

View File

@@ -1,3 +1,9 @@
## 0.6.2
### Minor Analysis Improvements
* Support for the `sqlite3` gem has been added. Method calls that execute queries against an SQLite3 database that may be vulnerable to injection attacks will now be recognized.
## 0.6.1
No user-facing changes.

View File

@@ -0,0 +1,4 @@
---
category: minorAnalysis
---
* Support for the `pg` gem has been added. Method calls that execute queries against a PostgreSQL database that may be vulnerable to injection attacks will now be recognized.

View File

@@ -1,4 +1,5 @@
---
category: minorAnalysis
---
## 0.6.2
### Minor Analysis Improvements
* Support for the `sqlite3` gem has been added. Method calls that execute queries against an SQLite3 database that may be vulnerable to injection attacks will now be recognized.

View File

@@ -1,2 +1,2 @@
---
lastReleaseVersion: 0.6.1
lastReleaseVersion: 0.6.2

View File

@@ -17,7 +17,7 @@ private string locationToString(Location loc) {
*
* For more information about locations see [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/).
*/
class Location extends @location {
class Location extends @location_default {
/** Gets the file for this location. */
File getFile() { locations_default(this, result, _, _, _, _) }

View File

@@ -121,13 +121,15 @@ private Ruby::AstNode getSuperParent(Ruby::Super sup) {
result = sup
or
result = getSuperParent(sup).getParent() and
not result instanceof Ruby::Method
not result instanceof Ruby::Method and
not result instanceof Ruby::SingletonMethod
}
private string getSuperMethodName(Ruby::Super sup) {
exists(Ruby::Method meth |
meth = getSuperParent(sup).getParent() and
exists(Ruby::AstNode meth | meth = getSuperParent(sup).getParent() |
result = any(Method c | toGenerated(c) = meth).getName()
or
result = any(SingletonMethod c | toGenerated(c) = meth).getName()
)
}

View File

@@ -936,10 +936,10 @@ module ExprNodes {
}
/** A control-flow node that wraps a `StringLiteral` AST expression. */
class StringLiteralCfgNode extends ExprCfgNode {
override string getAPrimaryQlClass() { result = "StringLiteralCfgNode" }
class StringLiteralCfgNode extends StringlikeLiteralCfgNode {
StringLiteralCfgNode() { e instanceof StringLiteral }
override StringLiteral e;
override string getAPrimaryQlClass() { result = "StringLiteralCfgNode" }
final override StringLiteral getExpr() { result = super.getExpr() }
}

View File

@@ -9,6 +9,23 @@ private import FlowSummaryImplSpecific as FlowSummaryImplSpecific
private import codeql.ruby.dataflow.FlowSummary
private import codeql.ruby.dataflow.SSA
/**
* A `LocalSourceNode` for a `self` variable. This is either an implicit `self`
* parameter or an implicit SSA entry definition.
*/
private class SelfLocalSourceNode extends DataFlow::LocalSourceNode {
private SelfVariable self;
SelfLocalSourceNode() {
self = this.(SelfParameterNode).getSelfVariable()
or
self = this.(SsaSelfDefinitionNode).getVariable()
}
/** Gets the `self` variable. */
SelfVariable getVariable() { result = self }
}
newtype TReturnKind =
TNormalReturnKind() or
TBreakReturnKind() or
@@ -316,7 +333,7 @@ private predicate extendCall(DataFlow::ExprNode receiver, Module m) {
exists(DataFlow::CallNode extendCall |
extendCall.getMethodName() = "extend" and
exists(DataFlow::LocalSourceNode sourceNode | sourceNode.flowsTo(extendCall.getArgument(_)) |
selfInModule(sourceNode.(SsaSelfDefinitionNode).getVariable(), m) or
selfInModule(sourceNode.(SelfLocalSourceNode).getVariable(), m) or
m = resolveConstantReadAccess(sourceNode.asExpr().getExpr())
) and
receiver = extendCall.getReceiver()
@@ -329,7 +346,7 @@ private predicate extendCallModule(Module m, Module n) {
exists(DataFlow::LocalSourceNode receiver, DataFlow::ExprNode e |
receiver.flowsTo(e) and extendCall(e, n)
|
selfInModule(receiver.(SsaSelfDefinitionNode).getVariable(), m) or
selfInModule(receiver.(SelfLocalSourceNode).getVariable(), m) or
m = resolveConstantReadAccess(receiver.asExpr().getExpr())
)
}
@@ -502,12 +519,12 @@ private predicate isStandardNewCall(RelevantCall new, Module m, boolean exact) {
exact = true
or
// `self.new` inside a module
selfInModule(sourceNode.(SsaSelfDefinitionNode).getVariable(), m) and
selfInModule(sourceNode.(SelfLocalSourceNode).getVariable(), m) and
exact = true
or
// `self.new` inside a singleton method
exists(MethodBase caller |
selfInMethod(sourceNode.(SsaSelfDefinitionNode).getVariable(), caller, m) and
selfInMethod(sourceNode.(SelfLocalSourceNode).getVariable(), caller, m) and
singletonMethod(caller, _, _) and
exact = false
)
@@ -573,7 +590,7 @@ private predicate isInstance(DataFlow::Node n, Module tp, boolean exact) {
// `self` reference in method or top-level (but not in module or singleton method,
// where instance methods cannot be called; only singleton methods)
n =
any(SsaSelfDefinitionNode self |
any(SelfLocalSourceNode self |
exists(MethodBase m |
selfInMethod(self.getVariable(), m, tp) and
not m instanceof SingletonMethod and
@@ -607,10 +624,10 @@ private DataFlow::Node trackInstance(Module tp, boolean exact, TypeTracker t) {
m = resolveConstantReadAccess(result.asExpr().getExpr())
or
// needed for e.g. `self.include`
selfInModule(result.(SsaSelfDefinitionNode).getVariable(), m)
selfInModule(result.(SelfLocalSourceNode).getVariable(), m)
or
// needed for e.g. `self.puts`
selfInMethod(result.(SsaSelfDefinitionNode).getVariable(), any(SingletonMethod sm), m)
selfInMethod(result.(SelfLocalSourceNode).getVariable(), any(SingletonMethod sm), m)
)
)
or
@@ -970,7 +987,7 @@ private DataFlow::Node trackSingletonMethodOnInstance(MethodBase method, string
/** 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 |
exists(SelfLocalSourceNode self |
flowsToMethodCallReceiver(call, self, method) and
selfInModule(self.getVariable(), m)
)
@@ -984,7 +1001,7 @@ pragma[nomagic]
private predicate selfInSingletonMethodFlowsToMethodCallReceiver(
RelevantCall call, Module m, string method
) {
exists(SsaSelfDefinitionNode self, MethodBase caller |
exists(SelfLocalSourceNode self, MethodBase caller |
flowsToMethodCallReceiver(call, self, method) and
selfInMethod(self.getVariable(), caller, m) and
singletonMethod(caller, _, _)
@@ -1062,10 +1079,13 @@ private CfgScope getTargetSingleton(RelevantCall call, string method) {
*/
pragma[nomagic]
private predicate argMustFlowToReceiver(
RelevantCall ctx, DataFlow::LocalSourceNode source, DataFlow::Node arg,
SsaDefinitionExtNode paramDef, RelevantCall call, Callable encl, string name
RelevantCall ctx, DataFlow::LocalSourceNode source, DataFlow::Node arg, RelevantCall call,
Callable encl, string name
) {
exists(ParameterNodeImpl p, ParameterPosition ppos, ArgumentPosition apos |
exists(
ParameterNodeImpl p, SsaDefinitionExtNode paramDef, ParameterPosition ppos,
ArgumentPosition apos
|
// the receiver of `call` references `p`
exists(DataFlow::Node receiver |
LocalFlow::localFlowSsaParamInput(p, paramDef) and
@@ -1106,7 +1126,7 @@ private predicate mayBenefitFromCallContextInitialize(
RelevantCall ctx, RelevantCall new, DataFlow::Node arg, Callable encl, Module tp, string name
) {
exists(DataFlow::LocalSourceNode source |
argMustFlowToReceiver(ctx, pragma[only_bind_into](source), arg, _, new, encl, "new") and
argMustFlowToReceiver(ctx, pragma[only_bind_into](source), arg, new, encl, "new") and
source = trackModuleAccess(tp) and
name = "initialize" and
exists(lookupMethod(tp, name))
@@ -1127,7 +1147,7 @@ private predicate mayBenefitFromCallContextInstance(
string name
) {
exists(DataFlow::LocalSourceNode source |
argMustFlowToReceiver(ctx, pragma[only_bind_into](source), arg, _, call, encl,
argMustFlowToReceiver(ctx, pragma[only_bind_into](source), arg, call, encl,
pragma[only_bind_into](name)) and
source = trackInstance(tp, exact) and
exists(lookupMethod(tp, pragma[only_bind_into](name)))
@@ -1148,7 +1168,7 @@ private predicate mayBenefitFromCallContextSingleton(
string name
) {
exists(DataFlow::LocalSourceNode source |
argMustFlowToReceiver(ctx, pragma[only_bind_into](source), pragma[only_bind_into](arg), _, call,
argMustFlowToReceiver(ctx, pragma[only_bind_into](source), pragma[only_bind_into](arg), call,
encl, pragma[only_bind_into](name)) and
exists(lookupSingletonMethod(tp, pragma[only_bind_into](name), exact))
|
@@ -1216,13 +1236,10 @@ DataFlowCallable viableImplInCallContext(DataFlowCall call, DataFlowCall ctx) {
or
// `ctx` cannot provide a type bound, and the receiver of the call is `self`;
// in this case, still apply an open-world assumption
exists(
RelevantCall call0, RelevantCall ctx0, DataFlow::Node arg, SsaSelfDefinitionNode self,
string name
|
exists(RelevantCall call0, RelevantCall ctx0, DataFlow::Node arg, string name |
call0 = call.asCall() and
ctx0 = ctx.asCall() and
argMustFlowToReceiver(ctx0, _, arg, self, call0, _, name) and
argMustFlowToReceiver(ctx0, _, arg, call0, _, name) and
not mayBenefitFromCallContextInitialize(ctx0, call0, arg, _, _, _) and
not mayBenefitFromCallContextInstance(ctx0, call0, arg, _, _, _, name) and
not mayBenefitFromCallContextSingleton(ctx0, call0, arg, _, _, _, name) and
@@ -1230,7 +1247,7 @@ DataFlowCallable viableImplInCallContext(DataFlowCall call, DataFlowCall ctx) {
)
or
// library calls should always be able to resolve
argMustFlowToReceiver(ctx.asCall(), _, _, _, call.asCall(), _, _) and
argMustFlowToReceiver(ctx.asCall(), _, _, call.asCall(), _, _) and
result = viableLibraryCallable(call)
)
}

View File

@@ -96,7 +96,8 @@ module LocalFlow {
exists(BasicBlock bb, int i |
lastRefBeforeRedefExt(def, bb, i, next.getDefinitionExt()) and
def = nodeFrom.getDefinitionExt() and
def.definesAt(_, bb, i, _)
def.definesAt(_, bb, i, _) and
nodeFrom != next
)
}
@@ -143,12 +144,13 @@ module LocalFlow {
* This is intended to recover from flow not currently recognised by ordinary capture flow.
*/
predicate localFlowSsaParamCaptureInput(Node nodeFrom, Node nodeTo) {
exists(Ssa::CapturedEntryDefinition def |
nodeFrom.asParameter().(NamedParameter).getVariable() = def.getSourceVariable()
or
nodeFrom.(SelfParameterNode).getSelfVariable() = def.getSourceVariable()
|
exists(Ssa::CapturedEntryDefinition def, ParameterNodeImpl p |
(nodeFrom = p or LocalFlow::localFlowSsaParamInput(p, nodeFrom)) and
nodeTo.(SsaDefinitionExtNode).getDefinitionExt() = def
|
p.getParameter().(NamedParameter).getVariable() = def.getSourceVariable()
or
p.(SelfParameterNode).getSelfVariable() = def.getSourceVariable()
)
}
@@ -939,6 +941,12 @@ private class NewCall extends DataFlowCall {
abstract class ReturningNode extends Node {
/** Gets the kind of this return node. */
abstract ReturnKind getKind();
pragma[nomagic]
predicate hasKind(ReturnKind kind, CfgScope scope) {
kind = this.getKind() and
scope = this.(NodeImpl).getCfgScope()
}
}
/** A data-flow node that represents a value returned by a callable. */
@@ -1059,10 +1067,8 @@ private module ReturnNodes {
SynthReturnNode() { this = TSynthReturnNode(scope, kind) }
/** Gets a syntactic return node that flows into this synthetic node. */
ReturningNode getAnInput() {
result.(NodeImpl).getCfgScope() = scope and
result.getKind() = kind
}
pragma[nomagic]
ReturningNode getAnInput() { result.hasKind(kind, scope) }
override ReturnKind getKind() { result = kind }

View File

@@ -166,28 +166,21 @@ module Public {
SummaryComponentStack return(ReturnKind rk) { result = singleton(SummaryComponent::return(rk)) }
}
private predicate noComponentSpecific(SummaryComponent sc) {
not exists(getComponentSpecific(sc))
}
/** Gets a textual representation of this component used for flow summaries. */
private string getComponent(SummaryComponent sc) {
result = getComponentSpecific(sc)
or
noComponentSpecific(sc) and
(
exists(ArgumentPosition pos |
sc = TParameterSummaryComponent(pos) and
result = "Parameter[" + getArgumentPosition(pos) + "]"
)
or
exists(ParameterPosition pos |
sc = TArgumentSummaryComponent(pos) and
result = "Argument[" + getParameterPosition(pos) + "]"
)
or
sc = TReturnSummaryComponent(getReturnValueKind()) and result = "ReturnValue"
exists(ArgumentPosition pos |
sc = TParameterSummaryComponent(pos) and
result = "Parameter[" + getArgumentPosition(pos) + "]"
)
or
exists(ParameterPosition pos |
sc = TArgumentSummaryComponent(pos) and
result = "Argument[" + getParameterPosition(pos) + "]"
)
or
sc = TReturnSummaryComponent(getReturnValueKind()) and result = "ReturnValue"
}
/** Gets a textual representation of this stack used for flow summaries. */
@@ -335,7 +328,7 @@ module Public {
class NeutralCallable extends SummarizedCallableBase {
private Provenance provenance;
NeutralCallable() { neutralElement(this, provenance) }
NeutralCallable() { neutralSummaryElement(this, provenance) }
/**
* Holds if the neutral is auto generated.

View File

@@ -62,10 +62,11 @@ predicate summaryElement(
}
/**
* Holds if a neutral model exists for `c` with provenance `provenance`,
* Holds if a neutral summary model exists for `c` with provenance `provenance`,
* which means that there is no flow through `c`.
* Note. Neutral models have not been implemented for Ruby.
*/
predicate neutralElement(FlowSummary::SummarizedCallable c, string provenance) { none() }
predicate neutralSummaryElement(FlowSummary::SummarizedCallable c, string provenance) { none() }
bindingset[arg]
private SummaryComponent interpretElementArg(string arg) {

View File

@@ -112,6 +112,13 @@ private module Cached {
)
}
cached
predicate summaryThroughStepTaint(
DataFlow::Node arg, DataFlow::Node out, FlowSummaryImpl::Public::SummarizedCallable sc
) {
FlowSummaryImpl::Private::Steps::summaryThroughStepTaint(arg, out, sc)
}
/**
* Holds if taint propagates from `nodeFrom` to `nodeTo` in exactly one local
* (intra-procedural) step.
@@ -122,7 +129,7 @@ private module Cached {
defaultAdditionalTaintStep(nodeFrom, nodeTo) or
// Simple flow through library code is included in the exposed local
// step relation, even though flow is technically inter-procedural
FlowSummaryImpl::Private::Steps::summaryThroughStepTaint(nodeFrom, nodeTo, _)
summaryThroughStepTaint(nodeFrom, nodeTo, _)
}
}

View File

@@ -0,0 +1,29 @@
/**
* Provides default sources, sinks and sanitizers for detecting
* "Unicode transformation"
* vulnerabilities, as well as extension points for adding your own.
*/
private import ruby
/**
* Provides default sources, sinks and sanitizers for detecting
* "Unicode transformation"
* vulnerabilities, as well as extension points for adding your own.
*/
module UnicodeBypassValidation {
/**
* A data flow source for "Unicode transformation" vulnerabilities.
*/
abstract class Source extends DataFlow::Node { }
/**
* A data flow sink for "Unicode transformation" vulnerabilities.
*/
abstract class Sink extends DataFlow::Node { }
/**
* A sanitizer for "Unicode transformation" vulnerabilities.
*/
abstract class Sanitizer extends DataFlow::Node { }
}

View File

@@ -0,0 +1,123 @@
/**
* Provides a taint-tracking configuration for detecting "Unicode transformation mishandling" vulnerabilities.
*/
private import ruby
private import codeql.ruby.dataflow.RemoteFlowSources
private import codeql.ruby.Concepts
private import codeql.ruby.TaintTracking
private import codeql.ruby.ApiGraphs
import UnicodeBypassValidationCustomizations::UnicodeBypassValidation
/** A state signifying that a logical validation has not been performed. */
class PreValidation extends DataFlow::FlowState {
PreValidation() { this = "PreValidation" }
}
/** A state signifying that a logical validation has been performed. */
class PostValidation extends DataFlow::FlowState {
PostValidation() { this = "PostValidation" }
}
/**
* A taint-tracking configuration for detecting "Unicode transformation mishandling" vulnerabilities.
*
* This configuration uses two flow states, `PreValidation` and `PostValidation`,
* to track the requirement that a logical validation has been performed before the Unicode Transformation.
*/
class Configuration extends TaintTracking::Configuration {
Configuration() { this = "UnicodeBypassValidation" }
override predicate isSource(DataFlow::Node source, DataFlow::FlowState state) {
source instanceof RemoteFlowSource and state instanceof PreValidation
}
override predicate isAdditionalTaintStep(
DataFlow::Node nodeFrom, DataFlow::FlowState stateFrom, DataFlow::Node nodeTo,
DataFlow::FlowState stateTo
) {
(
exists(Escaping escaping | nodeFrom = escaping.getAnInput() and nodeTo = escaping.getOutput())
or
exists(RegexExecution re | nodeFrom = re.getString() and nodeTo = re)
or
// String Manipulation Method Calls
// https://ruby-doc.org/core-2.7.0/String.html
exists(DataFlow::CallNode cn |
cn.getMethodName() =
[
[
"ljust", "lstrip", "succ", "next", "rjust", "capitalize", "chomp", "gsub", "chop",
"downcase", "swapcase", "uprcase", "scrub", "slice", "squeeze", "strip", "sub",
"tr", "tr_s", "reverse"
] + ["", "!"], "concat", "dump", "each_line", "replace", "insert", "inspect", "lines",
"partition", "prepend", "replace", "rpartition", "scan", "split", "undump",
"unpack" + ["", "1"]
] and
nodeFrom = cn.getReceiver() and
nodeTo = cn
)
or
exists(DataFlow::CallNode cn |
cn.getMethodName() =
[
"casecmp" + ["", "?"], "center", "count", "each_char", "index", "rindex", "sum",
["delete", "delete_prefix", "delete_suffix"] + ["", "!"],
["start_with", "end_with" + "eql", "include"] + ["?", "!"], "match" + ["", "?"],
] and
nodeFrom = cn.getReceiver() and
nodeTo = nodeFrom
)
or
exists(DataFlow::CallNode cn |
cn = API::getTopLevelMember("CGI").getAMethodCall("escapeHTML") and
nodeFrom = cn.getArgument(0) and
nodeTo = cn
)
) and
stateFrom instanceof PreValidation and
stateTo instanceof PostValidation
}
/* A Unicode Tranformation (Unicode tranformation) is considered a sink when the algorithm used is either NFC or NFKC. */
override predicate isSink(DataFlow::Node sink, DataFlow::FlowState state) {
(
exists(DataFlow::CallNode cn |
cn.getMethodName() = "unicode_normalize" and
cn.getArgument(0).getConstantValue().getSymbol() = ["nfkc", "nfc", "nfkd", "nfd"] and
sink = cn.getReceiver()
)
or
// unicode_utils
exists(API::MethodAccessNode mac |
mac = API::getTopLevelMember("UnicodeUtils").getMethod(["nfkd", "nfc", "nfd", "nfkc"]) and
sink = mac.getParameter(0).asSink()
)
or
// eprun
exists(API::MethodAccessNode mac |
mac = API::getTopLevelMember("Eprun").getMethod("normalize") and
sink = mac.getParameter(0).asSink()
)
or
// unf
exists(API::MethodAccessNode mac |
mac = API::getTopLevelMember("UNF").getMember("Normalizer").getMethod("normalize") and
sink = mac.getParameter(0).asSink()
)
or
// ActiveSupport::Multibyte::Chars
exists(DataFlow::CallNode cn, DataFlow::CallNode n |
cn =
API::getTopLevelMember("ActiveSupport")
.getMember("Multibyte")
.getMember("Chars")
.getMethod("new")
.getCallNode() and
n = cn.getAMethodCall("normalize") and
sink = cn.getArgument(0)
)
) and
state instanceof PostValidation
}
}

View File

@@ -0,0 +1,77 @@
/**
* Provides modeling for Pg, a Ruby library (gem) for interacting with PostgreSQL databases.
*/
private import codeql.ruby.ApiGraphs
private import codeql.ruby.dataflow.FlowSummary
private import codeql.ruby.Concepts
/**
* Provides modeling for Pg, a Ruby library (gem) for interacting with PostgreSQL databases.
*/
module Pg {
/**
* Flow summary for `PG.new()`. This method initializes a database connection.
*/
private class SqlSummary extends SummarizedCallable {
SqlSummary() { this = "PG.new()" }
override MethodCall getACall() { result = any(PgConnection c).asExpr().getExpr() }
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
input = "Argument[0]" and output = "ReturnValue" and preservesValue = false
}
}
/** A call to PG::Connection.open() is used to establish a connection to a PostgreSQL database. */
private class PgConnection extends DataFlow::CallNode {
PgConnection() {
this =
API::getTopLevelMember("PG")
.getMember("Connection")
.getAMethodCall(["open", "new", "connect_start"])
or
this = API::getTopLevelMember("PG").getAnInstantiation()
}
}
/** A call that prepares an SQL statement to be executed later. */
private class PgPrepareCall extends SqlConstruction::Range, DataFlow::CallNode {
private DataFlow::Node query;
private PgConnection pgConnection;
private string queryName;
PgPrepareCall() {
this = pgConnection.getAMethodCall("prepare") and
queryName = this.getArgument(0).getConstantValue().getStringlikeValue() and
query = this.getArgument(1)
}
PgConnection getConnection() { result = pgConnection }
string getQueryName() { result = queryName }
override DataFlow::Node getSql() { result = query }
}
/** A call that executes SQL statements against a PostgreSQL database. */
private class PgExecution extends SqlExecution::Range, DataFlow::CallNode {
private DataFlow::Node query;
PgExecution() {
exists(PgConnection pgConnection |
this =
pgConnection.getAMethodCall(["exec", "async_exec", "exec_params", "async_exec_params"]) and
query = this.getArgument(0)
or
exists(PgPrepareCall prepareCall |
pgConnection = prepareCall.getConnection() and
this.getArgument(0).getConstantValue().isStringlikeValue(prepareCall.getQueryName()) and
query = prepareCall.getSql()
)
)
}
override DataFlow::Node getSql() { result = query }
}
}

View File

@@ -583,7 +583,8 @@ module Array {
private class DeleteUnknownSummary extends DeleteSummary {
DeleteUnknownSummary() {
this = "delete" and
// Note: take care to avoid a name clash with the "delete" summary from String.qll
this = "delete-unknown-key" and
not exists(DataFlow::Content::getKnownElementIndex(mc.getArgument(0)))
}

View File

@@ -57,36 +57,40 @@ module Gem {
}
/** Gets the name of the gem */
string getName() { result = getSpecProperty("name").getConstantValue().getString() }
string getName() { result = this.getSpecProperty("name").getConstantValue().getString() }
/** Gets a path that is loaded when the gem is required */
private string getARequirePath() {
result = getSpecProperty(["require_paths", "require_path"]).getConstantValue().getString()
result =
this.getSpecProperty(["require_paths", "require_path"]).getConstantValue().getString()
or
not exists(getSpecProperty(["require_paths", "require_path"]).getConstantValue().getString()) and
not exists(
this.getSpecProperty(["require_paths", "require_path"]).getConstantValue().getString()
) and
result = "lib" // the default is "lib"
}
/** Gets a file that could be loaded when the gem is required. */
private File getAPossiblyRequiredFile() {
result = File.super.getParentContainer().getFolder(getARequirePath()).getAChildContainer*()
result =
File.super.getParentContainer().getFolder(this.getARequirePath()).getAChildContainer*()
}
/** Gets a class/module that is exported by this gem. */
private ModuleBase getAPublicModule() {
result.(Toplevel).getLocation().getFile() = getAPossiblyRequiredFile()
result.(Toplevel).getLocation().getFile() = this.getAPossiblyRequiredFile()
or
result = getAPublicModule().getAModule()
result = this.getAPublicModule().getAModule()
or
result = getAPublicModule().getAClass()
result = this.getAPublicModule().getAClass()
or
result = getAPublicModule().getStmt(_).(SingletonClass)
result = this.getAPublicModule().getStmt(_).(SingletonClass)
}
/** Gets a parameter from an exported method, which is an input to this gem. */
DataFlow::ParameterNode getAnInputParameter() {
exists(MethodBase method |
method = getAPublicModule().getAMethod() and
method = this.getAPublicModule().getAMethod() and
result.getParameter() = method.getAParameter()
|
method.isPublic()

View File

@@ -199,11 +199,13 @@ module Hash {
}
}
private class AssocUnknownSummary extends AssocSummary {
AssocUnknownSummary() {
this = "assoc" and
mc.getNumberOfArguments() = 1 and
not exists(DataFlow::Content::getKnownElementIndex(mc.getArgument(0)))
private class AssocUnknownSummary extends SummarizedCallable {
AssocUnknownSummary() { this = "assoc-unknown-arg" }
override MethodCall getACallSimple() {
result.getMethodName() = "assoc" and
result.getNumberOfArguments() = 1 and
not exists(DataFlow::Content::getKnownElementIndex(result.getArgument(0)))
}
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {

View File

@@ -21,6 +21,7 @@ private import codeql.ruby.typetracking.TypeTracker
private import codeql.ruby.ApiGraphs
private import codeql.ruby.Concepts
private import codeql.ruby.dataflow.internal.DataFlowPrivate as DataFlowPrivate
private import codeql.ruby.dataflow.internal.TaintTrackingPrivate as TaintTrackingPrivate
private import codeql.ruby.TaintTracking
private import codeql.ruby.frameworks.core.String
@@ -37,43 +38,6 @@ DataFlow::LocalSourceNode strStart() {
/** Gets a dataflow node for a regular expression literal. */
DataFlow::LocalSourceNode regStart() { result.asExpr().getExpr() instanceof Ast::RegExpLiteral }
/**
* Holds if the analysis should track flow from `nodeFrom` to `nodeTo` on top of the ordinary type-tracking steps.
* `nodeFrom` and `nodeTo` has type `fromType` and `toType` respectively.
* The types are either "string" or "regexp".
*/
predicate step(
DataFlow::Node nodeFrom, DataFlow::LocalSourceNode nodeTo, string fromType, string toType
) {
fromType = toType and
fromType = "string" and
(
// include taint flow through `String` summaries
TaintTracking::localTaintStep(nodeFrom, nodeTo) and
nodeFrom.(DataFlowPrivate::SummaryNode).getSummarizedCallable() instanceof
String::SummarizedCallable
or
// string concatenations, and
exists(CfgNodes::ExprNodes::OperationCfgNode op |
op = nodeTo.asExpr() and
op.getAnOperand() = nodeFrom.asExpr() and
op.getExpr().(Ast::BinaryOperation).getOperator() = "+"
)
or
// string interpolations
nodeFrom.asExpr() =
nodeTo.asExpr().(CfgNodes::ExprNodes::StringlikeLiteralCfgNode).getAComponent()
)
or
fromType = "string" and
toType = "reg" and
exists(DataFlow::CallNode call |
call = API::getTopLevelMember("Regexp").getAMethodCall(["compile", "new"]) and
nodeFrom = call.getArgument(0) and
nodeTo = call
)
}
/** Gets a node where string values that flow to the node are interpreted as regular expressions. */
DataFlow::Node stringSink() {
result instanceof RE::RegExpInterpretation::Range and
@@ -91,28 +55,139 @@ DataFlow::Node stringSink() {
/** Gets a node where regular expressions that flow to the node are used. */
DataFlow::Node regSink() { result = any(RegexExecution exec).getRegex() }
/** Gets a node that is reachable by type-tracking from any string or regular expression. */
DataFlow::LocalSourceNode forward(TypeTracker t) {
t.start() and
result = [strStart(), regStart()]
or
exists(TypeTracker t2 | result = forward(t2).track(t2, t))
or
exists(TypeTracker t2 | t2 = t.continue() | step(forward(t2).getALocalUse(), result, _, _))
private signature module TypeTrackInputSig {
DataFlow::LocalSourceNode start(TypeTracker t, DataFlow::Node start);
predicate end(DataFlow::Node n);
predicate additionalStep(DataFlow::Node nodeFrom, DataFlow::LocalSourceNode nodeTo);
}
/**
* Gets a node that is backwards reachable from any regular expression use,
* where that use is reachable by type-tracking from any string or regular expression.
* Provides a version of type tracking where we first prune for reachable nodes,
* before doing the type tracking computation.
*/
DataFlow::LocalSourceNode backwards(TypeBackTracker t) {
t.start() and
result.flowsTo([stringSink(), regSink()]) and
result = forward(TypeTracker::end())
or
exists(TypeBackTracker t2 | result = backwards(t2).backtrack(t2, t))
or
exists(TypeBackTracker t2 | t2 = t.continue() | step(result.getALocalUse(), backwards(t2), _, _))
private module TypeTrack<TypeTrackInputSig Input> {
private predicate additionalStep(
DataFlow::LocalSourceNode nodeFrom, DataFlow::LocalSourceNode nodeTo
) {
Input::additionalStep(nodeFrom.getALocalUse(), nodeTo)
}
/** Gets a node that is forwards reachable by type-tracking. */
pragma[nomagic]
private DataFlow::LocalSourceNode forward(TypeTracker t) {
result = Input::start(t, _)
or
exists(TypeTracker t2 | result = forward(t2).track(t2, t))
or
exists(TypeTracker t2 | t2 = t.continue() | additionalStep(forward(t2), result))
}
bindingset[result, tbt]
pragma[inline_late]
pragma[noopt]
private DataFlow::LocalSourceNode forwardLateInline(TypeBackTracker tbt) {
exists(TypeTracker tt |
result = forward(tt) and
tt = tbt.getACompatibleTypeTracker()
)
}
/** Gets a node that is backwards reachable by type-tracking. */
pragma[nomagic]
private DataFlow::LocalSourceNode backwards(TypeBackTracker t) {
result = forwardLateInline(t) and
(
t.start() and
Input::end(result.getALocalUse())
or
exists(TypeBackTracker t2 | result = backwards(t2).backtrack(t2, t))
or
exists(TypeBackTracker t2 | t2 = t.continue() | additionalStep(result, backwards(t2)))
)
}
bindingset[result, tt]
pragma[inline_late]
pragma[noopt]
private DataFlow::LocalSourceNode backwardsInlineLate(TypeTracker tt) {
exists(TypeBackTracker tbt |
result = backwards(tbt) and
tt = tbt.getACompatibleTypeTracker()
)
}
/** Holds if `n` is forwards and backwards reachable with type tracker `t`. */
pragma[nomagic]
private predicate reached(DataFlow::LocalSourceNode n, TypeTracker t) {
n = forward(t) and
n = backwardsInlineLate(t)
}
pragma[nomagic]
private TypeTracker stepReached(
TypeTracker t, DataFlow::LocalSourceNode nodeFrom, DataFlow::LocalSourceNode nodeTo
) {
exists(StepSummary summary |
StepSummary::step(nodeFrom, nodeTo, summary) and
reached(nodeFrom, t) and
reached(nodeTo, result) and
result = t.append(summary)
)
or
additionalStep(nodeFrom, nodeTo) and
reached(nodeFrom, pragma[only_bind_into](t)) and
reached(nodeTo, pragma[only_bind_into](t)) and
result = t.continue()
}
/** Gets a node that has been tracked from the start node `start`. */
DataFlow::LocalSourceNode track(DataFlow::Node start, TypeTracker t) {
t.start() and
result = Input::start(t, start) and
reached(result, t)
or
exists(TypeTracker t2 | t = stepReached(t2, track(start, t2), result))
}
}
/** Holds if `inputStr` is compiled to a regular expression that is returned at `call`. */
pragma[nomagic]
private predicate regFromString(DataFlow::LocalSourceNode inputStr, DataFlow::CallNode call) {
exists(DataFlow::Node mid |
inputStr.flowsTo(mid) and
call = API::getTopLevelMember("Regexp").getAMethodCall(["compile", "new"]) and
mid = call.getArgument(0)
)
}
private module StringTypeTrackInput implements TypeTrackInputSig {
DataFlow::LocalSourceNode start(TypeTracker t, DataFlow::Node start) {
start = strStart() and t.start() and result = start
}
predicate end(DataFlow::Node n) {
n = stringSink() or
regFromString(n, _)
}
predicate additionalStep(DataFlow::Node nodeFrom, DataFlow::LocalSourceNode nodeTo) {
// include taint flow through `String` summaries
TaintTrackingPrivate::summaryThroughStepTaint(nodeFrom, nodeTo,
any(String::SummarizedCallable c))
or
// string concatenations, and
exists(CfgNodes::ExprNodes::OperationCfgNode op |
op = nodeTo.asExpr() and
op.getAnOperand() = nodeFrom.asExpr() and
op.getExpr().(Ast::BinaryOperation).getOperator() = "+"
)
or
// string interpolations
nodeFrom.asExpr() =
nodeTo.asExpr().(CfgNodes::ExprNodes::StringlikeLiteralCfgNode).getAComponent()
}
}
/**
@@ -120,41 +195,34 @@ DataFlow::LocalSourceNode backwards(TypeBackTracker t) {
* This is used to figure out where `start` is evaluated as a regular expression against an input string,
* or where `start` is compiled into a regular expression.
*/
private DataFlow::LocalSourceNode trackStrings(DataFlow::Node start, TypeTracker t) {
result = backwards(_) and
(
private predicate trackStrings = TypeTrack<StringTypeTrackInput>::track/2;
/** Holds if `strConst` flows to a regex compilation (tracked by `t`), where the resulting regular expression is stored in `reg`. */
pragma[nomagic]
private predicate regFromStringStart(DataFlow::Node strConst, TypeTracker t, DataFlow::CallNode reg) {
regFromString(trackStrings(strConst, t), reg) and
exists(t.continue())
}
private module RegTypeTrackInput implements TypeTrackInputSig {
DataFlow::LocalSourceNode start(TypeTracker t, DataFlow::Node start) {
start = regStart() and
t.start() and
start = result and
result = strStart()
result = start
or
exists(TypeTracker t2 | result = trackStrings(start, t2).track(t2, t))
or
// an additional step from string to string
exists(TypeTracker t2 | t2 = t.continue() |
step(trackStrings(start, t2).getALocalUse(), result, "string", "string")
)
)
regFromStringStart(start, t, result)
}
predicate end(DataFlow::Node n) { n = regSink() }
predicate additionalStep(DataFlow::Node nodeFrom, DataFlow::LocalSourceNode nodeTo) { none() }
}
/**
* Gets a node that has been tracked from the regular expression `start` to some node.
* This is used to figure out where `start` is executed against an input string.
*/
private DataFlow::LocalSourceNode trackRegs(DataFlow::Node start, TypeTracker t) {
result = backwards(_) and
(
t.start() and
start = result and
result = regStart()
or
exists(TypeTracker t2 | result = trackRegs(start, t2).track(t2, t))
or
// an additional step where a string is converted to a regular expression
exists(TypeTracker t2 | t2 = t.continue() |
step(trackStrings(start, t2).getALocalUse(), result, "string", "reg")
)
)
}
private predicate trackRegs = TypeTrack<RegTypeTrackInput>::track/2;
/** Gets a node that references a regular expression. */
private DataFlow::LocalSourceNode trackRegexpType(TypeTracker t) {

View File

@@ -613,8 +613,17 @@ class TypeBackTracker extends TTypeBackTracker {
* also flow to `sink`.
*/
TypeTracker getACompatibleTypeTracker() {
exists(boolean hasCall | result = MkTypeTracker(hasCall, content) |
hasCall = false or this.hasReturn() = false
exists(boolean hasCall, OptionalTypeTrackerContent c |
result = MkTypeTracker(hasCall, c) and
(
compatibleContents(c, content)
or
content = noContent() and c = content
)
|
hasCall = false
or
this.hasReturn() = false
)
}
}

View File

@@ -80,10 +80,8 @@ predicate jumpStep = DataFlowPrivate::jumpStep/2;
pragma[nomagic]
private predicate flowThrough(DataFlowPublic::ParameterNode param) {
exists(DataFlowPrivate::ReturningNode returnNode, DataFlowDispatch::ReturnKind rk |
DataFlowPrivate::LocalFlow::getParameterDefNode(param.getParameter())
.(TypeTrackingNode)
.flowsTo(returnNode) and
rk = returnNode.getKind()
param.flowsTo(returnNode) and
returnNode.hasKind(rk, param.(DataFlowPrivate::NodeImpl).getCfgScope())
|
rk instanceof DataFlowDispatch::NormalReturnKind
or
@@ -91,12 +89,23 @@ private predicate flowThrough(DataFlowPublic::ParameterNode param) {
)
}
/** Holds if there is flow from `arg` to `p` via the call `call`, not counting `new -> initialize` call steps. */
pragma[nomagic]
predicate callStepNoInitialize(
ExprNodes::CallCfgNode call, Node arg, DataFlowPrivate::ParameterNodeImpl p
) {
exists(DataFlowDispatch::ParameterPosition pos |
argumentPositionMatch(call, arg, pos) and
p.isSourceParameterOf(DataFlowDispatch::getTarget(call), pos)
)
}
/** Holds if there is a level step from `nodeFrom` to `nodeTo`, which may depend on the call graph. */
pragma[nomagic]
predicate levelStepCall(Node nodeFrom, Node nodeTo) {
exists(DataFlowPublic::ParameterNode param |
flowThrough(param) and
callStep(nodeTo.asExpr(), nodeFrom, param)
callStepNoInitialize(nodeTo.asExpr(), nodeFrom, param)
)
}
@@ -600,14 +609,26 @@ private DataFlow::Node evaluateSummaryComponentStackLocal(
pragma[only_bind_out](tail)) and
stack = SCS::push(pragma[only_bind_out](head), pragma[only_bind_out](tail))
|
exists(DataFlowDispatch::ArgumentPosition apos, DataFlowDispatch::ParameterPosition ppos |
exists(
DataFlowDispatch::ArgumentPosition apos, DataFlowDispatch::ParameterPosition ppos,
DataFlowPrivate::ParameterNodeImpl p
|
head = SummaryComponent::parameter(apos) and
DataFlowDispatch::parameterMatch(ppos, apos) and
result.(DataFlowPrivate::ParameterNodeImpl).isSourceParameterOf(prev.asExpr().getExpr(), ppos)
p.isSourceParameterOf(prev.asExpr().getExpr(), ppos) and
// We need to include both `p` and the SSA definition for `p`, since in type-tracking
// the step from `p` to the SSA definition is considered a call step.
result =
[p.(DataFlow::Node), DataFlowPrivate::LocalFlow::getParameterDefNode(p.getParameter())]
)
or
head = SummaryComponent::return() and
result.(DataFlowPrivate::SynthReturnNode).getCfgScope() = prev.asExpr().getExpr()
exists(DataFlowPrivate::SynthReturnNode ret |
head = SummaryComponent::return() and
ret.getCfgScope() = prev.asExpr().getExpr() and
// We need to include both `ret` and `ret.getAnInput()`, since in type-tracking
// the step from `ret.getAnInput()` to `ret` is considered a return step.
result = [ret.(DataFlow::Node), ret.getAnInput()]
)
or
exists(DataFlow::ContentSet content |
head = SummaryComponent::withoutContent(content) and

View File

@@ -0,0 +1,22 @@
/**
* @name Print CFG
* @description Produces a representation of a file's Control Flow Graph.
* This query is used by the VS Code extension.
* @id rb/print-cfg
* @kind graph
* @tags ide-contextual-queries/print-cfg
*/
private import codeql.ruby.controlflow.internal.ControlFlowGraphImplShared::TestOutput
private import codeql.IDEContextual
/**
* Gets the source file to generate a CFG from.
*/
external string selectedSourceFile();
class MyRelevantNode extends RelevantNode {
MyRelevantNode() {
this.getScope().getLocation().getFile() = getFileBySourceArchiveName(selectedSourceFile())
}
}

View File

@@ -1,5 +1,5 @@
name: codeql/ruby-all
version: 0.6.2-dev
version: 0.6.3-dev
groups: ruby
extractor: ruby
dbscheme: ruby.dbscheme
@@ -12,3 +12,4 @@ dependencies:
codeql/util: ${workspace}
dataExtensions:
- codeql/ruby/frameworks/**/model.yml
warnOnImplicitThis: true

View File

@@ -1,15 +1,22 @@
// CodeQL database schema for Ruby
// Automatically generated from the tree-sitter grammar; do not edit
@location = @location_default
/*- Files and folders -*/
/**
* The location of an element.
* The location spans column `startcolumn` of line `startline` to
* column `endcolumn` of line `endline` in file `file`.
* For more information, see
* [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/).
*/
locations_default(
unique int id: @location_default,
int file: @file ref,
int start_line: int ref,
int start_column: int ref,
int end_line: int ref,
int end_column: int ref
int beginLine: int ref,
int beginColumn: int ref,
int endLine: int ref,
int endColumn: int ref
);
files(
@@ -29,9 +36,14 @@ containerparent(
unique int child: @container ref
);
sourceLocationPrefix(
string prefix: string ref
);
/*- Source location prefix -*/
/**
* The source location of the snapshot.
*/
sourceLocationPrefix(string prefix : string ref);
/*- Diagnostic messages -*/
diagnostics(
unique int id: @diagnostic,
@@ -42,6 +54,8 @@ diagnostics(
int location: @location_default ref
);
/*- Diagnostic messages: severity -*/
case @diagnostic.severity of
10 = @diagnostic_debug
| 20 = @diagnostic_info
@@ -49,7 +63,46 @@ case @diagnostic.severity of
| 40 = @diagnostic_error
;
/*- YAML -*/
#keyset[parent, idx]
yaml (unique int id: @yaml_node,
int kind: int ref,
int parent: @yaml_node_parent ref,
int idx: int ref,
string tag: string ref,
string tostring: string ref);
case @yaml_node.kind of
0 = @yaml_scalar_node
| 1 = @yaml_mapping_node
| 2 = @yaml_sequence_node
| 3 = @yaml_alias_node
;
@yaml_collection_node = @yaml_mapping_node | @yaml_sequence_node;
@yaml_node_parent = @yaml_collection_node | @file;
yaml_anchors (unique int node: @yaml_node ref,
string anchor: string ref);
yaml_aliases (unique int alias: @yaml_alias_node ref,
string target: string ref);
yaml_scalars (unique int scalar: @yaml_scalar_node ref,
int style: int ref,
string value: string ref);
yaml_errors (unique int id: @yaml_error,
string message: string ref);
yaml_locations(unique int locatable: @yaml_locatable ref,
int location: @location_default ref);
@yaml_locatable = @yaml_node | @yaml_error;
/*- Ruby dbscheme -*/
@ruby_underscore_arg = @ruby_assignment | @ruby_binary | @ruby_conditional | @ruby_operator_assignment | @ruby_range | @ruby_unary | @ruby_underscore_primary
@ruby_underscore_call_operator = @ruby_reserved_word
@@ -1375,9 +1428,10 @@ ruby_ast_node_info(
unique int node: @ruby_ast_node ref,
int parent: @ruby_ast_node_parent ref,
int parent_index: int ref,
int loc: @location ref
int loc: @location_default ref
);
/*- Erb dbscheme -*/
erb_comment_directive_child(
unique int erb_comment_directive: @erb_comment_directive ref,
unique int child: @erb_token_comment ref
@@ -1450,6 +1504,6 @@ erb_ast_node_info(
unique int node: @erb_ast_node ref,
int parent: @erb_ast_node_parent ref,
int parent_index: int ref,
int loc: @location ref
int loc: @location_default ref
);

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,2 @@
description: Sync dbscheme fragments
compatibility: full

View File

@@ -1,3 +1,7 @@
## 0.6.2
No user-facing changes.
## 0.6.1
No user-facing changes.

View File

@@ -0,0 +1,5 @@
---
category: minorAnalysis
---
* Fixed an issue where calls to `delete` or `assoc` with a constant-valued argument would be analyzed imprecisely,
as if the argument value was not a known constant.

View File

@@ -0,0 +1,6 @@
---
category: minorAnalysis
---
* Fixed a bug that would occur when an `initialize` method returns `self` or one of its parameters.
In such cases, the corresponding calls to `new` would be associated with an incorrect return type.
This could result in inaccurate call target resolution and cause false positive alerts.

View File

@@ -0,0 +1,3 @@
## 0.6.2
No user-facing changes.

View File

@@ -1,2 +1,2 @@
---
lastReleaseVersion: 0.6.1
lastReleaseVersion: 0.6.2

View File

@@ -0,0 +1,50 @@
<!DOCTYPE qhelp PUBLIC "-//Semmle//qhelp//EN" "qhelp.dtd">
<qhelp>
<overview>
<p>Security checks bypass due to a Unicode transformation</p>
<p>
If ever a unicode tranformation is performed after some security checks or logical
validation, the
latter could be bypassed due to a potential Unicode characters collision.
The validation of concern are any character escaping, any regex validation or any string
verification.
</p>
</overview>
<recommendation>
<p> Perform a Unicode normalization before the logical validation. </p>
</recommendation>
<example>
<p> The following example showcases the bypass of all checks performed by <code>
html_escape()</code> due to a post-unicode normalization.</p>
<p>For instance: the character U+FE64 (<code>﹤</code>) is not filtered-out by the
html_escape() function. But due to the Unicode normalization, the character is
transformed and would become U+003C (<code> &lt; </code> ).</p>
<sample src="./examples/unicode_normalization.rb" />
</example>
<example>
<p> The next example shows how an early deletion of a character may be bypassed due to a
potential Unicode character collision.</p>
<p>The character <code>&lt;</code> was expected to be omitted from the string <code>s</code>.
However, a malicious user may consider using its colliding Unicode character U+FE64 <code>
﹤</code> as an alternative. Due to the Late-Unicode normalization with the form NFKC,
the resulting string would contain the unintended character <code>&lt;</code> . </p>
<sample src="./examples/unicode_normalization2.rb" />
</example>
<references>
<li> Research study: <a
href="https://gosecure.github.io/presentations/2021-02-unicode-owasp-toronto/philippe_arteau_owasp_unicode_v4.pdf">
Unicode vulnerabilities that could bYte you
</a>
</li>
<li>
<a
href="https://gosecure.github.io/unicode-pentester-cheatsheet/">Unicode pentest
cheatsheet</a>. </li>
</references>
</qhelp>

View File

@@ -0,0 +1,24 @@
/**
* @name Bypass Logical Validation Using Unicode Characters
* @description A Unicode transformation is using a remote user-controlled data. The transformation is a Unicode normalization using the algorithms "NFC" or "NFKC". In all cases, the security measures implemented or the logical validation performed to escape any injection characters, to validate using regex patterns or to perform string-based checks, before the Unicode transformation are **bypassable** by special Unicode characters.
* @kind path-problem
* @id rb/unicode-bypass-validation
* @precision high
* @problem.severity error
* @tags security
* experimental
* external/cwe/cwe-176
* external/cwe/cwe-179
* external/cwe/cwe-180
*/
import ruby
import codeql.ruby.experimental.UnicodeBypassValidationQuery
import DataFlow::PathGraph
from Configuration config, DataFlow::PathNode source, DataFlow::PathNode sink
where config.hasFlowPath(source, sink)
select sink.getNode(), source, sink,
"This $@ processes unsafely $@ and any logical validation in-between could be bypassed using special Unicode characters.",
sink.getNode(), "Unicode transformation (Unicode normalization)", source.getNode(),
"remote user-controlled data"

View File

@@ -0,0 +1,10 @@
require "erb"
class UnicodeNormalizationHtMLSafeController < ActionController::Base
def unicodeNormalize
unicode_input = params[:unicode_input]
unicode_html_safe = ERB::Util.html_escape(unicode_input)
normalized_nfkc = unicode_html_safe.unicode_normalize(:nfkc) # $result=BAD
normalized_nfc = unicode_html_safe.unicode_normalize(:nfc) # $result=BAD
end
end

View File

@@ -0,0 +1,2 @@
s = "﹤xss>"
puts s.delete("<").unicode_normalize(:nfkc).include?("<")

View File

@@ -1,5 +1,5 @@
name: codeql/ruby-queries
version: 0.6.2-dev
version: 0.6.3-dev
groups:
- ruby
- queries
@@ -9,3 +9,4 @@ dependencies:
codeql/ruby-all: ${workspace}
codeql/suite-helpers: ${workspace}
codeql/util: ${workspace}
warnOnImplicitThis: true

View File

@@ -0,0 +1,16 @@
/**
* @name Summarized callable call sites
* @description A call site for which we have a summarized callable
* @kind problem
* @problem.severity recommendation
* @id rb/meta/summarized-callable-call-sites
* @tags meta
* @precision very-low
*/
import codeql.ruby.AST
import codeql.ruby.dataflow.FlowSummary
from Call invoke, SummarizedCallable f
where f.getACall() = invoke or f.getACallSimple() = invoke
select invoke, "Call to " + f

View File

@@ -11,7 +11,7 @@ private import codeql.ruby.security.UrlRedirectCustomizations
private import codeql.ruby.security.SqlInjectionCustomizations
class RelevantFile extends File {
RelevantFile() { not getRelativePath().regexpMatch(".*/test(case)?s?/.*") }
RelevantFile() { not this.getRelativePath().regexpMatch(".*/test(case)?s?/.*") }
}
RemoteFlowSource relevantTaintSource(string kind) {

View File

@@ -15,8 +15,7 @@
</p>
<sample language="ruby">
text.gsub!(/^\s+|\s+$/, '') # BAD
</sample>
text.gsub!(/^\s+|\s+$/, '') # BAD</sample>
<p>
@@ -74,8 +73,7 @@
</p>
<sample language="ruby">
/^0\.\d+E?\d+$/ # BAD
</sample>
/^0\.\d+E?\d+$/ # BAD</sample>
<p>
@@ -108,6 +106,33 @@
</example>
<example>
<p>
Sometimes it is unclear how a regular expression can be rewritten to
avoid the problem. In such cases, it often suffices to limit the
length of the input string. For instance, the following
regular expression is used to match numbers, and on some non-number
inputs it can have quadratic time complexity:
</p>
<sample language="ruby">
is_matching = /^(\+|-)?(\d+|(\d*\.\d*))?(E|e)?([-+])?(\d+)?$/.match?(str)</sample>
<p>
It is not immediately obvious how to rewrite this regular expression
to avoid the problem. However, you can mitigate performance issues by limiting the length
to 1000 characters, which will always finish in a reasonable amount
of time.
</p>
<sample language="ruby">
if str.length &gt; 1000
raise ArgumentError, "Input too long"
end
is_matching = /^(\+|-)?(\d+|(\d*\.\d*))?(E|e)?([-+])?(\d+)?$/.match?(str)</sample>
</example>
<include src="ReDoSReferences.inc.qhelp"/>
</qhelp>

View File

@@ -1,13 +0,0 @@
identityLocalStep
| calls/calls.rb:202:7:202:9 | SSA phi read(y) | Node steps to itself |
| calls/calls.rb:205:7:205:7 | SSA phi read(self) | Node steps to itself |
| calls/calls.rb:205:7:205:7 | SSA phi read(y) | Node steps to itself |
| calls/calls.rb:210:11:210:13 | SSA phi read(y) | Node steps to itself |
| calls/calls.rb:211:14:211:14 | SSA phi read(self) | Node steps to itself |
| calls/calls.rb:211:14:211:14 | SSA phi read(y) | Node steps to itself |
| calls/calls.rb:214:7:214:9 | SSA phi read(y) | Node steps to itself |
| calls/calls.rb:217:7:217:7 | SSA phi read(self) | Node steps to itself |
| calls/calls.rb:217:7:217:7 | SSA phi read(y) | Node steps to itself |
| calls/calls.rb:222:11:222:13 | SSA phi read(y) | Node steps to itself |
| calls/calls.rb:223:14:223:14 | SSA phi read(self) | Node steps to itself |
| calls/calls.rb:223:14:223:14 | SSA phi read(y) | Node steps to itself |

View File

@@ -1,13 +0,0 @@
identityLocalStep
| calls.rb:202:7:202:9 | SSA phi read(y) | Node steps to itself |
| calls.rb:205:7:205:7 | SSA phi read(self) | Node steps to itself |
| calls.rb:205:7:205:7 | SSA phi read(y) | Node steps to itself |
| calls.rb:210:11:210:13 | SSA phi read(y) | Node steps to itself |
| calls.rb:211:14:211:14 | SSA phi read(self) | Node steps to itself |
| calls.rb:211:14:211:14 | SSA phi read(y) | Node steps to itself |
| calls.rb:214:7:214:9 | SSA phi read(y) | Node steps to itself |
| calls.rb:217:7:217:7 | SSA phi read(self) | Node steps to itself |
| calls.rb:217:7:217:7 | SSA phi read(y) | Node steps to itself |
| calls.rb:222:11:222:13 | SSA phi read(y) | Node steps to itself |
| calls.rb:223:14:223:14 | SSA phi read(self) | Node steps to itself |
| calls.rb:223:14:223:14 | SSA phi read(y) | Node steps to itself |

View File

@@ -38,11 +38,11 @@ class ApiUseTest extends InlineExpectationsTest {
override predicate hasActualResult(Location location, string element, string tag, string value) {
tag = "use" and // def tags are always optional
exists(DataFlow::Node n | relevantNode(_, n, location, tag) |
exists(DataFlow::Node n | this.relevantNode(_, n, location, tag) |
// Only report the longest path on this line:
value =
max(API::Node a2, Location l2, DataFlow::Node n2 |
relevantNode(a2, n2, l2, tag) and
this.relevantNode(a2, n2, l2, tag) and
l2.getFile() = location.getFile() and
l2.getEndLine() = location.getEndLine()
|
@@ -57,7 +57,7 @@ class ApiUseTest extends InlineExpectationsTest {
// We also permit optional annotations for any other path on the line.
// This is used to test subclass paths, which typically have a shorter canonical path.
override predicate hasOptionalResult(Location location, string element, string tag, string value) {
exists(API::Node a, DataFlow::Node n | relevantNode(a, n, location, tag) |
exists(API::Node a, DataFlow::Node n | this.relevantNode(a, n, location, tag) |
element = n.toString() and
value = getAPath(a, _)
)

View File

@@ -2814,6 +2814,7 @@
| file://:0:0:0:0 | parameter position 0 of File.realdirpath | file://:0:0:0:0 | [summary] to write: return (return) in File.realdirpath |
| file://:0:0:0:0 | parameter position 0 of File.realpath | file://:0:0:0:0 | [summary] to write: return (return) in File.realpath |
| file://:0:0:0:0 | parameter position 0 of Hash[] | file://:0:0:0:0 | [summary] read: argument position 0.any element in Hash[] |
| file://:0:0:0:0 | parameter position 0 of PG.new() | file://:0:0:0:0 | [summary] to write: return (return) in PG.new() |
| file://:0:0:0:0 | parameter position 0 of String.try_convert | file://:0:0:0:0 | [summary] to write: return (return) in String.try_convert |
| file://:0:0:0:0 | parameter position 0 of \| | file://:0:0:0:0 | [summary] read: argument position 0.any element in \| |
| file://:0:0:0:0 | parameter position 1.. of File.join | file://:0:0:0:0 | [summary] to write: return (return) in File.join |
@@ -2827,6 +2828,7 @@
| file://:0:0:0:0 | parameter self of ActiveSupportStringTransform | file://:0:0:0:0 | [summary] to write: return (return) in ActiveSupportStringTransform |
| file://:0:0:0:0 | parameter self of [] | file://:0:0:0:0 | [summary] to write: return (return) in [] |
| file://:0:0:0:0 | parameter self of \| | file://:0:0:0:0 | [summary] read: argument self.any element in \| |
| file://:0:0:0:0 | parameter self of assoc-unknown-arg | file://:0:0:0:0 | [summary] read: argument self.any element in assoc-unknown-arg |
| file://:0:0:0:0 | parameter self of each(0) | file://:0:0:0:0 | [summary] read: argument self.any element in each(0) |
| local_dataflow.rb:1:1:7:3 | self (foo) | local_dataflow.rb:3:8:3:10 | self |
| local_dataflow.rb:1:1:7:3 | self in foo | local_dataflow.rb:1:1:7:3 | self (foo) |

View File

@@ -94,64 +94,64 @@ calls.rb:
# 325| C1
#-----| super -> Object
# 331| C2
# 335| C2
#-----| super -> C1
# 337| C3
# 341| C3
#-----| super -> C2
# 377| SingletonOverride1
# 385| SingletonOverride1
#-----| super -> Object
# 412| SingletonOverride2
# 420| SingletonOverride2
#-----| super -> SingletonOverride1
# 433| ConditionalInstanceMethods
# 441| ConditionalInstanceMethods
#-----| super -> Object
# 496| ExtendSingletonMethod
# 504| ExtendSingletonMethod
# 506| ExtendSingletonMethod2
# 514| ExtendSingletonMethod2
# 512| ExtendSingletonMethod3
# 520| ExtendSingletonMethod3
# 525| ProtectedMethodInModule
# 533| ProtectedMethodInModule
# 531| ProtectedMethods
# 539| ProtectedMethods
#-----| super -> Object
#-----| include -> ProtectedMethodInModule
# 550| ProtectedMethodsSub
# 558| ProtectedMethodsSub
#-----| super -> ProtectedMethods
# 564| SingletonUpCall_Base
# 572| SingletonUpCall_Base
#-----| super -> Object
# 568| SingletonUpCall_Sub
# 576| SingletonUpCall_Sub
#-----| super -> SingletonUpCall_Base
# 576| SingletonUpCall_SubSub
# 584| SingletonUpCall_SubSub
#-----| super -> SingletonUpCall_Sub
# 583| SingletonA
# 591| SingletonA
#-----| super -> Object
# 596| SingletonB
# 604| SingletonB
#-----| super -> SingletonA
# 605| SingletonC
# 613| SingletonC
#-----| super -> SingletonA
# 618| Included
# 626| Included
# 626| IncludesIncluded
# 634| IncludesIncluded
#-----| super -> Object
#-----| include -> Included
# 633| CustomNew1
# 641| CustomNew1
#-----| super -> Object
# 641| CustomNew2
# 649| CustomNew2
#-----| super -> Object
hello.rb:

View File

@@ -117,134 +117,146 @@ getTarget
| calls.rb:320:5:320:16 | call to instance | calls.rb:311:5:314:7 | instance |
| calls.rb:323:1:323:17 | call to singleton | calls.rb:316:5:318:7 | singleton |
| calls.rb:327:9:327:26 | call to puts | calls.rb:102:5:102:30 | puts |
| calls.rb:333:9:333:26 | call to puts | calls.rb:102:5:102:30 | puts |
| calls.rb:339:9:339:26 | call to puts | calls.rb:102:5:102:30 | puts |
| calls.rb:346:9:346:18 | call to instance | calls.rb:338:5:340:7 | instance |
| calls.rb:348:9:348:18 | call to instance | calls.rb:332:5:334:7 | instance |
| calls.rb:348:9:348:18 | call to instance | calls.rb:338:5:340:7 | instance |
| calls.rb:350:9:350:18 | call to instance | calls.rb:326:5:328:7 | instance |
| calls.rb:350:9:350:18 | call to instance | calls.rb:332:5:334:7 | instance |
| calls.rb:350:9:350:18 | call to instance | calls.rb:338:5:340:7 | instance |
| calls.rb:355:20:355:29 | call to instance | calls.rb:338:5:340:7 | instance |
| calls.rb:356:26:356:36 | call to instance | calls.rb:332:5:334:7 | instance |
| calls.rb:356:26:356:36 | call to instance | calls.rb:338:5:340:7 | instance |
| calls.rb:357:26:357:36 | call to instance | calls.rb:326:5:328:7 | instance |
| calls.rb:357:26:357:36 | call to instance | calls.rb:332:5:334:7 | instance |
| calls.rb:357:26:357:36 | call to instance | calls.rb:338:5:340:7 | instance |
| calls.rb:361:6:361:11 | call to new | calls.rb:117:5:117:16 | new |
| calls.rb:362:1:362:11 | call to instance | calls.rb:326:5:328:7 | instance |
| calls.rb:363:1:363:25 | call to pattern_dispatch | calls.rb:343:1:359:3 | pattern_dispatch |
| calls.rb:363:19:363:24 | call to new | calls.rb:117:5:117:16 | new |
| calls.rb:364:1:364:25 | call to pattern_dispatch | calls.rb:343:1:359:3 | pattern_dispatch |
| calls.rb:364:19:364:24 | call to new | calls.rb:117:5:117:16 | new |
| calls.rb:365:1:365:25 | call to pattern_dispatch | calls.rb:343:1:359:3 | pattern_dispatch |
| calls.rb:365:19:365:24 | call to new | calls.rb:117:5:117:16 | new |
| calls.rb:369:9:369:28 | call to puts | calls.rb:102:5:102:30 | puts |
| calls.rb:373:6:373:11 | call to new | calls.rb:117:5:117:16 | new |
| calls.rb:374:1:374:16 | call to add_singleton | calls.rb:367:1:371:3 | add_singleton |
| calls.rb:375:1:375:11 | call to instance | calls.rb:326:5:328:7 | instance |
| 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: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:337:9:337:26 | call to puts | calls.rb:102:5:102:30 | puts |
| calls.rb:343:9:343:26 | call to puts | calls.rb:102:5:102:30 | puts |
| calls.rb:350:9:350:18 | call to instance | calls.rb:342:5:344:7 | instance |
| calls.rb:352:9:352:18 | call to instance | calls.rb:336:5:338:7 | instance |
| calls.rb:352:9:352:18 | call to instance | calls.rb:342:5:344:7 | instance |
| calls.rb:354:9:354:18 | call to instance | calls.rb:326:5:328:7 | instance |
| calls.rb:354:9:354:18 | call to instance | calls.rb:336:5:338:7 | instance |
| calls.rb:354:9:354:18 | call to instance | calls.rb:342:5:344:7 | instance |
| calls.rb:359:20:359:29 | call to instance | calls.rb:342:5:344:7 | instance |
| calls.rb:360:26:360:36 | call to instance | calls.rb:336:5:338:7 | instance |
| calls.rb:360:26:360:36 | call to instance | calls.rb:342:5:344:7 | instance |
| calls.rb:361:26:361:36 | call to instance | calls.rb:326:5:328:7 | instance |
| calls.rb:361:26:361:36 | call to instance | calls.rb:336:5:338:7 | instance |
| calls.rb:361:26:361:36 | call to instance | calls.rb:342:5:344:7 | instance |
| calls.rb:365:6:365:11 | call to new | calls.rb:117:5:117:16 | new |
| calls.rb:366:1:366:11 | call to instance | calls.rb:326:5:328:7 | instance |
| calls.rb:368:1:368:25 | call to pattern_dispatch | calls.rb:347:1:363:3 | pattern_dispatch |
| calls.rb:368:19:368:24 | call to new | calls.rb:117:5:117:16 | new |
| calls.rb:369:1:369:25 | call to pattern_dispatch | calls.rb:347:1:363:3 | pattern_dispatch |
| calls.rb:369:19:369:24 | call to new | calls.rb:117:5:117:16 | new |
| calls.rb:370:1:370:25 | call to pattern_dispatch | calls.rb:347:1:363:3 | pattern_dispatch |
| calls.rb:370:19:370:24 | call to new | calls.rb:117:5:117:16 | new |
| calls.rb:372:1:372:6 | call to new | calls.rb:117:5:117:16 | new |
| calls.rb:372:1:372:18 | call to return_self | calls.rb:330:5:332:7 | return_self |
| calls.rb:372:1:372:27 | call to instance | calls.rb:326:5:328:7 | instance |
| calls.rb:372:1:372:27 | call to instance | calls.rb:336:5:338:7 | instance |
| calls.rb:372:1:372:27 | call to instance | calls.rb:342:5:344:7 | instance |
| calls.rb:376:9:376:28 | call to puts | calls.rb:102:5:102:30 | puts |
| calls.rb:380:6:380:11 | call to new | calls.rb:117:5:117:16 | new |
| calls.rb:381:1:381:16 | call to add_singleton | calls.rb:374:1:378:3 | add_singleton |
| calls.rb:382:1:382:11 | call to instance | calls.rb:326:5:328:7 | instance |
| calls.rb:382:1:382:11 | call to instance | calls.rb:375:5:377:7 | instance |
| calls.rb:383:1:383:14 | call to return_self | calls.rb:330:5:332:7 | return_self |
| calls.rb:383:1:383:23 | call to instance | calls.rb:326:5:328:7 | instance |
| calls.rb:383:1:383:23 | call to instance | calls.rb:336:5:338:7 | instance |
| calls.rb:383:1:383:23 | call to instance | calls.rb:342:5:344:7 | instance |
| calls.rb:383:1:383:23 | call to instance | calls.rb:375:5:377:7 | instance |
| calls.rb:388:13:388:48 | call to puts | calls.rb:102:5:102:30 | puts |
| calls.rb:392:13:392:22 | call to singleton1 | calls.rb:387:9:389:11 | singleton1 |
| calls.rb:392:13:392:22 | call to singleton1 | calls.rb:422:9:424:11 | singleton1 |
| calls.rb:396:13:396:20 | call to new | calls.rb:117:5:117:16 | new |
| calls.rb:396:13:396:30 | call to instance1 | calls.rb:410:5:412:7 | instance1 |
| calls.rb:396:13:396:30 | call to instance1 | calls.rb:431:5:433:7 | instance1 |
| calls.rb:401:9:401:44 | call to puts | calls.rb:102:5:102:30 | puts |
| calls.rb:405:9:405:18 | call to singleton2 | calls.rb:400:5:402:7 | singleton2 |
| calls.rb:405:9:405:18 | call to singleton2 | calls.rb:427:5:429:7 | singleton2 |
| calls.rb:408:5:408:14 | call to singleton2 | calls.rb:400:5:402:7 | singleton2 |
| calls.rb:411:9:411:43 | call to puts | calls.rb:102:5:102:30 | puts |
| calls.rb:415:1:415:29 | call to singleton1 | calls.rb:387:9:389:11 | singleton1 |
| calls.rb:416:1:416:29 | call to singleton2 | calls.rb:400:5:402:7 | singleton2 |
| calls.rb:417:1:417:34 | call to call_singleton1 | calls.rb:391:9:393:11 | call_singleton1 |
| calls.rb:418:1:418:34 | call to call_singleton2 | calls.rb:404:5:406:7 | call_singleton2 |
| calls.rb:423:13:423:48 | call to puts | calls.rb:102:5:102:30 | puts |
| calls.rb:428:9:428:44 | call to puts | calls.rb:102:5:102:30 | puts |
| calls.rb:432:9:432:43 | call to puts | calls.rb:102:5:102:30 | puts |
| calls.rb:436:1:436:29 | call to singleton1 | calls.rb:422:9:424:11 | singleton1 |
| calls.rb:437:1:437:29 | call to singleton2 | calls.rb:427:5:429:7 | singleton2 |
| calls.rb:438:1:438:34 | call to call_singleton1 | calls.rb:391:9:393:11 | call_singleton1 |
| calls.rb:439:1:439:34 | call to call_singleton2 | calls.rb:404:5:406:7 | call_singleton2 |
| 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 |
| calls.rb:620:9:620:16 | call to bar | calls.rb:622:5:623:7 | bar |
| calls.rb:620:9:620:16 | call to bar | calls.rb:628:5:630:7 | bar |
| calls.rb:627:5:627:20 | call to include | calls.rb:108:5:110:7 | include |
| calls.rb:629:9:629:13 | super call to bar | calls.rb:622:5:623:7 | bar |
| calls.rb:635:9:635:14 | call to new | calls.rb:117:5:117:16 | new |
| calls.rb:639:1:639:14 | call to new | calls.rb:117:5:117:16 | new |
| calls.rb:639:1:639:14 | call to new | calls.rb:634:5:636:7 | new |
| calls.rb:639:1:639:23 | call to instance | calls.rb:326:5:328:7 | instance |
| calls.rb:647:9:647:34 | call to puts | calls.rb:102:5:102:30 | puts |
| calls.rb:651:1:651:14 | call to new | calls.rb:117:5:117:16 | new |
| calls.rb:651:1:651:14 | call to new | calls.rb:642:5:644:7 | new |
| calls.rb:651:1:651:23 | call to instance | calls.rb:646:5:648:7 | instance |
| calls.rb:449:9:449:44 | call to puts | calls.rb:102:5:102:30 | puts |
| calls.rb:452:13:452:48 | call to puts | calls.rb:102:5:102:30 | puts |
| calls.rb:455:17:455:52 | call to puts | calls.rb:102:5:102:30 | puts |
| calls.rb:463:9:467:11 | call to new | calls.rb:117:5:117:16 | new |
| calls.rb:463:9:467:15 | call to new | calls.rb:117:5:117:16 | new |
| calls.rb:465:17:465:40 | call to puts | calls.rb:102:5:102:30 | puts |
| calls.rb:471:1:471:30 | call to new | calls.rb:117:5:117:16 | new |
| calls.rb:471:1:471:33 | call to m1 | calls.rb:443:9:445:11 | m1 |
| calls.rb:472:1:472:30 | call to new | calls.rb:117:5:117:16 | new |
| calls.rb:473:1:473:30 | call to new | calls.rb:117:5:117:16 | new |
| calls.rb:473:1:473:33 | call to m2 | calls.rb:448:5:460:7 | m2 |
| calls.rb:474:1:474:30 | call to new | calls.rb:117:5:117:16 | new |
| calls.rb:475:1:475:30 | call to new | calls.rb:117:5:117:16 | new |
| calls.rb:476:1:476:30 | call to new | calls.rb:117:5:117:16 | new |
| calls.rb:478:27:496:3 | call to new | calls.rb:117:5:117:16 | new |
| calls.rb:481:13:481:22 | call to puts | calls.rb:102:5:102:30 | puts |
| calls.rb:485:5:489:7 | call to new | calls.rb:117:5:117:16 | new |
| calls.rb:485:5:489:11 | call to new | calls.rb:117:5:117:16 | new |
| calls.rb:487:13:487:22 | call to puts | calls.rb:102:5:102:30 | puts |
| calls.rb:493:13:493:27 | call to puts | calls.rb:102:5:102:30 | puts |
| calls.rb:498:1:498:27 | call to new | calls.rb:117:5:117:16 | new |
| calls.rb:499:1:499:27 | call to new | calls.rb:117:5:117:16 | new |
| calls.rb:500:1:500:27 | call to new | calls.rb:117:5:117:16 | new |
| calls.rb:501:1:501:27 | call to new | calls.rb:117:5:117:16 | new |
| calls.rb:502:1:502:27 | call to new | calls.rb:117:5:117:16 | new |
| calls.rb:512:1:512:31 | call to singleton | calls.rb:505:5:507:7 | singleton |
| calls.rb:518:1:518:32 | call to singleton | calls.rb:505:5:507:7 | singleton |
| calls.rb:525:1:525:32 | call to singleton | calls.rb:505:5:507:7 | singleton |
| calls.rb:531:1:531:13 | call to singleton | calls.rb:505:5:507:7 | singleton |
| calls.rb:540:5:540:35 | call to include | calls.rb:108:5:110:7 | include |
| calls.rb:543:9:543:35 | call to puts | calls.rb:102:5:102:30 | puts |
| calls.rb:547:9:547:11 | call to foo | calls.rb:534:15:536:7 | foo |
| calls.rb:548:9:548:11 | call to bar | calls.rb:542:15:544:7 | bar |
| calls.rb:549:9:549:28 | call to new | calls.rb:117:5:117:16 | new |
| calls.rb:549:9:549:32 | call to foo | calls.rb:534:15:536:7 | foo |
| calls.rb:550:9:550:28 | call to new | calls.rb:117:5:117:16 | new |
| calls.rb:550:9:550:32 | call to bar | calls.rb:542:15:544:7 | bar |
| calls.rb:554:1:554:20 | call to new | calls.rb:117:5:117:16 | new |
| calls.rb:555:1:555:20 | call to new | calls.rb:117:5:117:16 | new |
| calls.rb:556:1:556:20 | call to new | calls.rb:117:5:117:16 | new |
| calls.rb:556:1:556:24 | call to baz | calls.rb:546:5:551:7 | baz |
| calls.rb:560:9:560:11 | call to foo | calls.rb:534:15:536:7 | foo |
| calls.rb:561:9:561:31 | call to new | calls.rb:117:5:117:16 | new |
| calls.rb:561:9:561:35 | call to foo | calls.rb:534:15:536:7 | foo |
| calls.rb:565:1:565:23 | call to new | calls.rb:117:5:117:16 | new |
| calls.rb:566:1:566:23 | call to new | calls.rb:117:5:117:16 | new |
| calls.rb:567:1:567:23 | call to new | calls.rb:117:5:117:16 | new |
| calls.rb:567:1:567:27 | call to baz | calls.rb:559:5:562:7 | baz |
| calls.rb:569:2:569:6 | call to new | calls.rb:117:5:117:16 | new |
| calls.rb:569:20:569:24 | call to baz | calls.rb:51:5:57:7 | baz |
| calls.rb:570:26:570:37 | call to capitalize | calls.rb:97:5:97:23 | capitalize |
| calls.rb:577:5:577:13 | call to singleton | calls.rb:573:5:574:7 | singleton |
| calls.rb:580:9:580:17 | call to singleton | calls.rb:573:5:574:7 | singleton |
| calls.rb:581:9:581:18 | call to singleton2 | calls.rb:585:5:586:7 | singleton2 |
| calls.rb:588:5:588:14 | call to mid_method | calls.rb:579:5:582:7 | mid_method |
| calls.rb:596:9:596:18 | call to singleton1 | calls.rb:592:5:593:7 | singleton1 |
| calls.rb:596:9:596:18 | call to singleton1 | calls.rb:605:5:606:7 | singleton1 |
| calls.rb:596:9:596:18 | call to singleton1 | calls.rb:614:5:615:7 | singleton1 |
| calls.rb:600:9:600:23 | call to call_singleton1 | calls.rb:595:5:597:7 | call_singleton1 |
| calls.rb:600:9:600:23 | call to call_singleton1 | calls.rb:608:5:610:7 | call_singleton1 |
| calls.rb:600:9:600:23 | call to call_singleton1 | calls.rb:617:5:619:7 | call_singleton1 |
| calls.rb:609:9:609:18 | call to singleton1 | calls.rb:605:5:606:7 | singleton1 |
| calls.rb:618:9:618:18 | call to singleton1 | calls.rb:614:5:615:7 | singleton1 |
| calls.rb:622:1:622:31 | call to call_call_singleton1 | calls.rb:599:5:601:7 | call_call_singleton1 |
| calls.rb:623:1:623:31 | call to call_call_singleton1 | calls.rb:599:5:601:7 | call_call_singleton1 |
| calls.rb:624:1:624:31 | call to call_call_singleton1 | calls.rb:599:5:601:7 | call_call_singleton1 |
| calls.rb:628:9:628:16 | call to bar | calls.rb:630:5:631:7 | bar |
| calls.rb:628:9:628:16 | call to bar | calls.rb:636:5:638:7 | bar |
| calls.rb:635:5:635:20 | call to include | calls.rb:108:5:110:7 | include |
| calls.rb:637:9:637:13 | super call to bar | calls.rb:630:5:631:7 | bar |
| calls.rb:643:9:643:14 | call to new | calls.rb:117:5:117:16 | new |
| calls.rb:647:1:647:14 | call to new | calls.rb:117:5:117:16 | new |
| calls.rb:647:1:647:14 | call to new | calls.rb:642:5:644:7 | new |
| calls.rb:647:1:647:23 | call to instance | calls.rb:326:5:328:7 | instance |
| calls.rb:655:9:655:34 | call to puts | calls.rb:102:5:102:30 | puts |
| calls.rb:659:1:659:14 | call to new | calls.rb:117:5:117:16 | new |
| calls.rb:659:1:659:14 | call to new | calls.rb:650:5:652:7 | new |
| calls.rb:659:1:659:23 | call to instance | calls.rb:654:5:656:7 | instance |
| calls.rb:667:2:667:25 | call to capture_parameter | calls.rb:661:1:665:3 | capture_parameter |
| calls.rb:667:20:667:25 | call to new | calls.rb:117:5:117:16 | new |
| 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 | super call to message | hello.rb:13:5:15:7 | message |
@@ -330,46 +342,49 @@ 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: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: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:442:8:442:13 | call to rand |
| calls.rb:442:8:442:17 | ... > ... |
| calls.rb:459:9:459:10 | call to m3 |
| calls.rb:462:8:462:13 | call to rand |
| calls.rb:462:8:462:17 | ... > ... |
| calls.rb:463:9:467:18 | call to m5 |
| calls.rb:472:1:472:33 | call to m3 |
| calls.rb:474:1:474:33 | call to m3 |
| calls.rb:475:1:475:33 | call to m4 |
| calls.rb:476:1:476:33 | call to m5 |
| calls.rb:479:5:479:11 | call to [] |
| calls.rb:479:5:483:7 | call to each |
| calls.rb:485:5:489:15 | call to bar |
| calls.rb:491:5:491:11 | call to [] |
| calls.rb:491:5:495:7 | call to each |
| calls.rb:492:9:494:11 | call to define_method |
| calls.rb:498:1:498:31 | call to foo |
| calls.rb:499:1:499:31 | call to bar |
| calls.rb:500:1:500:33 | call to baz_0 |
| calls.rb:501:1:501:33 | call to baz_1 |
| calls.rb:502:1:502:33 | call to baz_2 |
| calls.rb:506:9:506:46 | call to puts |
| calls.rb:509:5:509:15 | call to extend |
| calls.rb:515:5:515:32 | call to extend |
| calls.rb:523:1:523:51 | call to extend |
| calls.rb:528:1:528:13 | call to singleton |
| calls.rb:529:1:529:32 | call to extend |
| 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 |
| calls.rb:643:9:643:21 | call to allocate |
| calls.rb:535:9:535:42 | call to puts |
| calls.rb:542:5:544:7 | call to protected |
| calls.rb:554:1:554:24 | call to foo |
| calls.rb:555:1:555:24 | call to bar |
| calls.rb:565:1:565:27 | call to foo |
| calls.rb:566:1:566:27 | call to bar |
| calls.rb:569:1:569:7 | call to [] |
| calls.rb:569:1:569:26 | call to each |
| calls.rb:570:1:570:13 | call to [] |
| calls.rb:570:1:570:39 | call to each |
| calls.rb:578:5:578:14 | call to singleton2 |
| calls.rb:651:9:651:21 | call to allocate |
| calls.rb:662:5:662:11 | call to [] |
| calls.rb:662:5:664:7 | call to each |
| calls.rb:667:1:667:35 | call to instance |
| hello.rb:20:16:20:26 | ... + ... |
| hello.rb:20:16:20:34 | ... + ... |
| hello.rb:20:16:20:40 | ... + ... |
@@ -397,10 +412,11 @@ privateMethod
| calls.rb:158:1:160:3 | indirect |
| calls.rb:185:1:186:3 | private_on_main |
| 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:472:9:474:11 | foo |
| calls.rb:478:9:480:11 | bar |
| calls.rb:347:1:363:3 | pattern_dispatch |
| calls.rb:374:1:378:3 | add_singleton |
| calls.rb:480:9:482:11 | foo |
| calls.rb:486:9:488:11 | bar |
| calls.rb:661:1:665:3 | capture_parameter |
| private.rb:2:11:3:5 | private1 |
| private.rb:8:3:9:5 | private2 |
| private.rb:14:3:15:5 | private3 |
@@ -459,42 +475,43 @@ publicMethod
| calls.rb:311:5:314:7 | instance |
| calls.rb:316:5:318:7 | singleton |
| calls.rb:326:5:328:7 | instance |
| calls.rb:332:5:334:7 | instance |
| calls.rb:338:5:340:7 | instance |
| calls.rb:368:5:370:7 | instance |
| calls.rb:379:9:381:11 | singleton1 |
| calls.rb:383:9:385:11 | call_singleton1 |
| 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 |
| calls.rb:619:5:621:7 | foo |
| calls.rb:622:5:623:7 | bar |
| calls.rb:628:5:630:7 | bar |
| calls.rb:634:5:636:7 | new |
| calls.rb:330:5:332:7 | return_self |
| calls.rb:336:5:338:7 | instance |
| calls.rb:342:5:344:7 | instance |
| calls.rb:375:5:377:7 | instance |
| calls.rb:387:9:389:11 | singleton1 |
| calls.rb:391:9:393:11 | call_singleton1 |
| calls.rb:395:9:397:11 | factory |
| calls.rb:400:5:402:7 | singleton2 |
| calls.rb:404:5:406:7 | call_singleton2 |
| calls.rb:410:5:412:7 | instance1 |
| calls.rb:422:9:424:11 | singleton1 |
| calls.rb:427:5:429:7 | singleton2 |
| calls.rb:431:5:433:7 | instance1 |
| calls.rb:443:9:445:11 | m1 |
| calls.rb:448:5:460:7 | m2 |
| calls.rb:451:9:457:11 | m3 |
| calls.rb:454:13:456:15 | m4 |
| calls.rb:464:13:466:15 | m5 |
| calls.rb:505:5:507:7 | singleton |
| calls.rb:546:5:551:7 | baz |
| calls.rb:559:5:562:7 | baz |
| calls.rb:573:5:574:7 | singleton |
| calls.rb:579:5:582:7 | mid_method |
| calls.rb:585:5:586:7 | singleton2 |
| calls.rb:592:5:593:7 | singleton1 |
| calls.rb:595:5:597:7 | call_singleton1 |
| calls.rb:599:5:601:7 | call_call_singleton1 |
| calls.rb:605:5:606:7 | singleton1 |
| calls.rb:608:5:610:7 | call_singleton1 |
| calls.rb:614:5:615:7 | singleton1 |
| calls.rb:617:5:619:7 | call_singleton1 |
| calls.rb:627:5:629:7 | foo |
| calls.rb:630:5:631:7 | bar |
| calls.rb:636:5:638:7 | bar |
| calls.rb:642:5:644:7 | new |
| calls.rb:646:5:648:7 | instance |
| calls.rb:650:5:652:7 | new |
| calls.rb:654:5:656:7 | instance |
| hello.rb:2:5:4:7 | hello |
| hello.rb:5:5:7:7 | world |
| hello.rb:13:5:15:7 | message |
@@ -522,7 +539,7 @@ publicMethod
| toplevel_self_singleton.rb:26:9:27:11 | call_me |
| toplevel_self_singleton.rb:29:9:32:11 | call_you |
protectedMethod
| calls.rb:526:15:528:7 | foo |
| calls.rb:534:15:536:7 | bar |
| calls.rb:534:15:536:7 | foo |
| calls.rb:542:15:544:7 | bar |
| private.rb:32:3:33:5 | protected1 |
| private.rb:35:3:36:5 | protected2 |

View File

@@ -326,6 +326,10 @@ class C1
def instance
puts "C1#instance"
end
def return_self
self
end
end
class C2 < C1
@@ -360,10 +364,13 @@ end
c1 = C1.new
c1.instance
pattern_dispatch (C1.new)
pattern_dispatch (C2.new)
pattern_dispatch (C3.new)
C3.new.return_self.instance
def add_singleton x
def x.instance
puts "instance_on x"
@@ -373,6 +380,7 @@ end
c3 = C1.new
add_singleton c3
c3.instance
c3.return_self.instance
class SingletonOverride1
class << self
@@ -649,3 +657,11 @@ class CustomNew2
end
CustomNew2.new.instance
def capture_parameter x
[0,1,2].each do
x
end
end
(capture_parameter C1.new).instance # NoMethodError

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -90,62 +90,62 @@ calls.rb:
# 325| C1
#-----| -> Object
# 331| C2
# 335| C2
#-----| -> C1
# 337| C3
# 341| C3
#-----| -> C2
# 377| SingletonOverride1
# 385| SingletonOverride1
#-----| -> Object
# 412| SingletonOverride2
# 420| SingletonOverride2
#-----| -> SingletonOverride1
# 433| ConditionalInstanceMethods
# 441| ConditionalInstanceMethods
#-----| -> Object
# 496| ExtendSingletonMethod
# 504| ExtendSingletonMethod
# 506| ExtendSingletonMethod2
# 514| ExtendSingletonMethod2
# 512| ExtendSingletonMethod3
# 520| ExtendSingletonMethod3
# 525| ProtectedMethodInModule
# 533| ProtectedMethodInModule
# 531| ProtectedMethods
# 539| ProtectedMethods
#-----| -> Object
# 550| ProtectedMethodsSub
# 558| ProtectedMethodsSub
#-----| -> ProtectedMethods
# 564| SingletonUpCall_Base
# 572| SingletonUpCall_Base
#-----| -> Object
# 568| SingletonUpCall_Sub
# 576| SingletonUpCall_Sub
#-----| -> SingletonUpCall_Base
# 576| SingletonUpCall_SubSub
# 584| SingletonUpCall_SubSub
#-----| -> SingletonUpCall_Sub
# 583| SingletonA
# 591| SingletonA
#-----| -> Object
# 596| SingletonB
# 604| SingletonB
#-----| -> SingletonA
# 605| SingletonC
# 613| SingletonC
#-----| -> SingletonA
# 618| Included
# 626| Included
# 626| IncludesIncluded
# 634| IncludesIncluded
#-----| -> Object
# 633| CustomNew1
# 641| CustomNew1
#-----| -> Object
# 641| CustomNew2
# 649| CustomNew2
#-----| -> Object
hello.rb:

View File

@@ -6,3 +6,4 @@ dependencies:
codeql/ruby-all: ${workspace}
extractor: ruby
tests: .
warnOnImplicitThis: true

View File

@@ -0,0 +1,75 @@
edges
| unicode_normalization.rb:7:5:7:17 | unicode_input | unicode_normalization.rb:8:23:8:35 | unicode_input |
| unicode_normalization.rb:7:5:7:17 | unicode_input | unicode_normalization.rb:9:22:9:34 | unicode_input |
| unicode_normalization.rb:7:21:7:26 | call to params | unicode_normalization.rb:7:21:7:42 | ...[...] |
| unicode_normalization.rb:7:21:7:42 | ...[...] | unicode_normalization.rb:7:5:7:17 | unicode_input |
| unicode_normalization.rb:15:5:15:17 | unicode_input | unicode_normalization.rb:16:27:16:39 | unicode_input |
| unicode_normalization.rb:15:5:15:17 | unicode_input | unicode_normalization.rb:16:27:16:39 | unicode_input |
| unicode_normalization.rb:15:21:15:26 | call to params | unicode_normalization.rb:15:21:15:42 | ...[...] |
| unicode_normalization.rb:15:21:15:26 | call to params | unicode_normalization.rb:15:21:15:42 | ...[...] |
| unicode_normalization.rb:15:21:15:42 | ...[...] | unicode_normalization.rb:15:5:15:17 | unicode_input |
| unicode_normalization.rb:15:21:15:42 | ...[...] | unicode_normalization.rb:15:5:15:17 | unicode_input |
| unicode_normalization.rb:16:5:16:23 | unicode_input_manip | unicode_normalization.rb:17:23:17:41 | unicode_input_manip |
| unicode_normalization.rb:16:5:16:23 | unicode_input_manip | unicode_normalization.rb:18:22:18:40 | unicode_input_manip |
| unicode_normalization.rb:16:27:16:39 | unicode_input | unicode_normalization.rb:16:27:16:59 | call to sub |
| unicode_normalization.rb:16:27:16:39 | unicode_input | unicode_normalization.rb:16:27:16:59 | call to sub |
| unicode_normalization.rb:16:27:16:59 | call to sub | unicode_normalization.rb:16:5:16:23 | unicode_input_manip |
| unicode_normalization.rb:24:5:24:17 | unicode_input | unicode_normalization.rb:25:37:25:49 | unicode_input |
| unicode_normalization.rb:24:21:24:26 | call to params | unicode_normalization.rb:24:21:24:42 | ...[...] |
| unicode_normalization.rb:24:21:24:42 | ...[...] | unicode_normalization.rb:24:5:24:17 | unicode_input |
| unicode_normalization.rb:25:5:25:21 | unicode_html_safe | unicode_normalization.rb:26:23:26:39 | unicode_html_safe |
| unicode_normalization.rb:25:5:25:21 | unicode_html_safe | unicode_normalization.rb:27:22:27:38 | unicode_html_safe |
| unicode_normalization.rb:25:25:25:50 | call to html_escape | unicode_normalization.rb:25:5:25:21 | unicode_html_safe |
| unicode_normalization.rb:25:37:25:49 | unicode_input | unicode_normalization.rb:25:25:25:50 | call to html_escape |
| unicode_normalization.rb:33:5:33:17 | unicode_input | unicode_normalization.rb:34:40:34:52 | unicode_input |
| unicode_normalization.rb:33:21:33:26 | call to params | unicode_normalization.rb:33:21:33:42 | ...[...] |
| unicode_normalization.rb:33:21:33:42 | ...[...] | unicode_normalization.rb:33:5:33:17 | unicode_input |
| unicode_normalization.rb:34:5:34:21 | unicode_html_safe | unicode_normalization.rb:35:23:35:39 | unicode_html_safe |
| unicode_normalization.rb:34:5:34:21 | unicode_html_safe | unicode_normalization.rb:36:22:36:38 | unicode_html_safe |
| unicode_normalization.rb:34:25:34:53 | call to escapeHTML | unicode_normalization.rb:34:25:34:63 | call to html_safe |
| unicode_normalization.rb:34:25:34:63 | call to html_safe | unicode_normalization.rb:34:5:34:21 | unicode_html_safe |
| unicode_normalization.rb:34:40:34:52 | unicode_input | unicode_normalization.rb:34:25:34:53 | call to escapeHTML |
nodes
| unicode_normalization.rb:7:5:7:17 | unicode_input | semmle.label | unicode_input |
| unicode_normalization.rb:7:21:7:26 | call to params | semmle.label | call to params |
| unicode_normalization.rb:7:21:7:42 | ...[...] | semmle.label | ...[...] |
| unicode_normalization.rb:8:23:8:35 | unicode_input | semmle.label | unicode_input |
| unicode_normalization.rb:9:22:9:34 | unicode_input | semmle.label | unicode_input |
| unicode_normalization.rb:15:5:15:17 | unicode_input | semmle.label | unicode_input |
| unicode_normalization.rb:15:5:15:17 | unicode_input | semmle.label | unicode_input |
| unicode_normalization.rb:15:21:15:26 | call to params | semmle.label | call to params |
| unicode_normalization.rb:15:21:15:42 | ...[...] | semmle.label | ...[...] |
| unicode_normalization.rb:15:21:15:42 | ...[...] | semmle.label | ...[...] |
| unicode_normalization.rb:16:5:16:23 | unicode_input_manip | semmle.label | unicode_input_manip |
| unicode_normalization.rb:16:27:16:39 | unicode_input | semmle.label | unicode_input |
| unicode_normalization.rb:16:27:16:39 | unicode_input | semmle.label | unicode_input |
| unicode_normalization.rb:16:27:16:59 | call to sub | semmle.label | call to sub |
| unicode_normalization.rb:17:23:17:41 | unicode_input_manip | semmle.label | unicode_input_manip |
| unicode_normalization.rb:18:22:18:40 | unicode_input_manip | semmle.label | unicode_input_manip |
| unicode_normalization.rb:24:5:24:17 | unicode_input | semmle.label | unicode_input |
| unicode_normalization.rb:24:21:24:26 | call to params | semmle.label | call to params |
| unicode_normalization.rb:24:21:24:42 | ...[...] | semmle.label | ...[...] |
| unicode_normalization.rb:25:5:25:21 | unicode_html_safe | semmle.label | unicode_html_safe |
| unicode_normalization.rb:25:25:25:50 | call to html_escape | semmle.label | call to html_escape |
| unicode_normalization.rb:25:37:25:49 | unicode_input | semmle.label | unicode_input |
| unicode_normalization.rb:26:23:26:39 | unicode_html_safe | semmle.label | unicode_html_safe |
| unicode_normalization.rb:27:22:27:38 | unicode_html_safe | semmle.label | unicode_html_safe |
| unicode_normalization.rb:33:5:33:17 | unicode_input | semmle.label | unicode_input |
| unicode_normalization.rb:33:21:33:26 | call to params | semmle.label | call to params |
| unicode_normalization.rb:33:21:33:42 | ...[...] | semmle.label | ...[...] |
| unicode_normalization.rb:34:5:34:21 | unicode_html_safe | semmle.label | unicode_html_safe |
| unicode_normalization.rb:34:25:34:53 | call to escapeHTML | semmle.label | call to escapeHTML |
| unicode_normalization.rb:34:25:34:63 | call to html_safe | semmle.label | call to html_safe |
| unicode_normalization.rb:34:40:34:52 | unicode_input | semmle.label | unicode_input |
| unicode_normalization.rb:35:23:35:39 | unicode_html_safe | semmle.label | unicode_html_safe |
| unicode_normalization.rb:36:22:36:38 | unicode_html_safe | semmle.label | unicode_html_safe |
subpaths
#select
| unicode_normalization.rb:8:23:8:35 | unicode_input | unicode_normalization.rb:7:21:7:26 | call to params | unicode_normalization.rb:8:23:8:35 | unicode_input | This $@ processes unsafely $@ and any logical validation in-between could be bypassed using special Unicode characters. | unicode_normalization.rb:8:23:8:35 | unicode_input | Unicode transformation (Unicode normalization) | unicode_normalization.rb:7:21:7:26 | call to params | remote user-controlled data |
| unicode_normalization.rb:9:22:9:34 | unicode_input | unicode_normalization.rb:7:21:7:26 | call to params | unicode_normalization.rb:9:22:9:34 | unicode_input | This $@ processes unsafely $@ and any logical validation in-between could be bypassed using special Unicode characters. | unicode_normalization.rb:9:22:9:34 | unicode_input | Unicode transformation (Unicode normalization) | unicode_normalization.rb:7:21:7:26 | call to params | remote user-controlled data |
| unicode_normalization.rb:17:23:17:41 | unicode_input_manip | unicode_normalization.rb:15:21:15:26 | call to params | unicode_normalization.rb:17:23:17:41 | unicode_input_manip | This $@ processes unsafely $@ and any logical validation in-between could be bypassed using special Unicode characters. | unicode_normalization.rb:17:23:17:41 | unicode_input_manip | Unicode transformation (Unicode normalization) | unicode_normalization.rb:15:21:15:26 | call to params | remote user-controlled data |
| unicode_normalization.rb:18:22:18:40 | unicode_input_manip | unicode_normalization.rb:15:21:15:26 | call to params | unicode_normalization.rb:18:22:18:40 | unicode_input_manip | This $@ processes unsafely $@ and any logical validation in-between could be bypassed using special Unicode characters. | unicode_normalization.rb:18:22:18:40 | unicode_input_manip | Unicode transformation (Unicode normalization) | unicode_normalization.rb:15:21:15:26 | call to params | remote user-controlled data |
| unicode_normalization.rb:26:23:26:39 | unicode_html_safe | unicode_normalization.rb:24:21:24:26 | call to params | unicode_normalization.rb:26:23:26:39 | unicode_html_safe | This $@ processes unsafely $@ and any logical validation in-between could be bypassed using special Unicode characters. | unicode_normalization.rb:26:23:26:39 | unicode_html_safe | Unicode transformation (Unicode normalization) | unicode_normalization.rb:24:21:24:26 | call to params | remote user-controlled data |
| unicode_normalization.rb:27:22:27:38 | unicode_html_safe | unicode_normalization.rb:24:21:24:26 | call to params | unicode_normalization.rb:27:22:27:38 | unicode_html_safe | This $@ processes unsafely $@ and any logical validation in-between could be bypassed using special Unicode characters. | unicode_normalization.rb:27:22:27:38 | unicode_html_safe | Unicode transformation (Unicode normalization) | unicode_normalization.rb:24:21:24:26 | call to params | remote user-controlled data |
| unicode_normalization.rb:35:23:35:39 | unicode_html_safe | unicode_normalization.rb:33:21:33:26 | call to params | unicode_normalization.rb:35:23:35:39 | unicode_html_safe | This $@ processes unsafely $@ and any logical validation in-between could be bypassed using special Unicode characters. | unicode_normalization.rb:35:23:35:39 | unicode_html_safe | Unicode transformation (Unicode normalization) | unicode_normalization.rb:33:21:33:26 | call to params | remote user-controlled data |
| unicode_normalization.rb:36:22:36:38 | unicode_html_safe | unicode_normalization.rb:33:21:33:26 | call to params | unicode_normalization.rb:36:22:36:38 | unicode_html_safe | This $@ processes unsafely $@ and any logical validation in-between could be bypassed using special Unicode characters. | unicode_normalization.rb:36:22:36:38 | unicode_html_safe | Unicode transformation (Unicode normalization) | unicode_normalization.rb:33:21:33:26 | call to params | remote user-controlled data |

View File

@@ -0,0 +1 @@
experimental/cwe-176/UnicodeBypassValidation.ql

View File

@@ -0,0 +1,38 @@
require "erb"
include ERB::Util
require 'cgi'
class UnicodeNormalizationOKController < ActionController::Base
def unicodeNormalize
unicode_input = params[:unicode_input]
normalized_nfkc = unicode_input.unicode_normalize(:nfkc) # $MISSING:result=OK
normalized_nfc = unicode_input.unicode_normalize(:nfc) # $MISSING:result=OK
end
end
class UnicodeNormalizationStrManipulationController < ActionController::Base
def unicodeNormalize
unicode_input = params[:unicode_input]
unicode_input_manip = unicode_input.sub(/[aeiou]/, "*")
normalized_nfkc = unicode_input_manip.unicode_normalize(:nfkc) # $result=BAD
normalized_nfc = unicode_input_manip.unicode_normalize(:nfc) # $result=BAD
end
end
class UnicodeNormalizationHtMLEscapeController < ActionController::Base
def unicodeNormalize
unicode_input = params[:unicode_input]
unicode_html_safe = html_escape(unicode_input)
normalized_nfkc = unicode_html_safe.unicode_normalize(:nfkc) # $result=BAD
normalized_nfc = unicode_html_safe.unicode_normalize(:nfc) # $result=BAD
end
end
class UnicodeNormalizationCGIHtMLEscapeController < ActionController::Base
def unicodeNormalize
unicode_input = params[:unicode_input]
unicode_html_safe = CGI.escapeHTML(unicode_input).html_safe
normalized_nfkc = unicode_html_safe.unicode_normalize(:nfkd) # $result=BAD
normalized_nfc = unicode_html_safe.unicode_normalize(:nfd) # $result=BAD
end
end

View File

@@ -0,0 +1,70 @@
class FooController < ActionController::Base
def some_request_handler
# A string tainted by user input is inserted into a query
# (i.e a remote flow source)
name = params[:name]
# Establish a connection to a PostgreSQL database
conn = PG::Connection.open(:dbname => 'postgresql', :user => 'user', :password => 'pass', :host => 'localhost', :port => '5432')
# .exec() and .async_exec()
# BAD: SQL statement constructed from user input
qry1 = "SELECT * FROM users WHERE username = '#{name}';"
conn.exec(qry1)
conn.async_exec(qry1)
# .exec_params() and .async_exec_params()
# BAD: SQL statement constructed from user input
qry2 = "SELECT * FROM users WHERE username = '#{name}';"
conn.exec_params(qry2)
conn.async_exec_params(qry2)
# .exec_params() and .async_exec_params()
# GOOD: SQL statement constructed from sanitized user input
qry2 = "SELECT * FROM users WHERE username = $1;"
conn.exec_params(qry2, [name])
conn.async_exec_params(qry2, [name])
# .prepare() and .exec_prepared()
# BAD: SQL statement constructed from user input
qry3 = "SELECT * FROM users WHERE username = '#{name}';"
conn.prepare("query_1", qry3)
conn.exec_prepared('query_1')
# .prepare() and .exec_prepared()
# GOOD: SQL statement constructed from sanitized user input
qry3 = "SELECT * FROM users WHERE username = $1;"
conn.prepare("query_2", qry3)
conn.exec_prepared('query_2', [name])
# .prepare() and .exec_prepared()
# NOT EXECUTED: SQL statement constructed from user input but not executed
qry3 = "SELECT * FROM users WHERE username = '#{name}';"
conn.prepare("query_3", qry3)
end
end
class BarController < ApplicationController
def safe_paths
name1 = params["name1"]
# GOOD: barrier guard prevents taint flow
if name == "admin"
qry_bar1 = "SELECT * FROM users WHERE username = '%s';" % name
else
qry_bar1 = "SELECT * FROM users WHERE username = 'none';"
end
conn.exec_params(qry_bar1)
name2 = params["name2"]
# GOOD: barrier guard prevents taint flow
name2 = if ["admin", "guest"].include? name2
name2
else
name2 = "none"
end
qry_bar2 = "SELECT * FROM users WHERE username = '%s';" % name
conn.exec_params(qry_bar2)
end
end

View File

@@ -55,6 +55,18 @@ edges
| ArelInjection.rb:4:5:4:8 | name | ArelInjection.rb:6:20:6:61 | "SELECT * FROM users WHERE nam..." |
| ArelInjection.rb:4:12:4:17 | call to params | ArelInjection.rb:4:12:4:29 | ...[...] |
| ArelInjection.rb:4:12:4:29 | ...[...] | ArelInjection.rb:4:5:4:8 | name |
| PgInjection.rb:6:5:6:8 | name | PgInjection.rb:13:5:13:8 | qry1 |
| PgInjection.rb:6:5:6:8 | name | PgInjection.rb:19:5:19:8 | qry2 |
| PgInjection.rb:6:5:6:8 | name | PgInjection.rb:31:5:31:8 | qry3 |
| PgInjection.rb:6:5:6:8 | name | PgInjection.rb:43:5:43:8 | qry3 |
| PgInjection.rb:6:12:6:17 | call to params | PgInjection.rb:6:12:6:24 | ...[...] |
| PgInjection.rb:6:12:6:24 | ...[...] | PgInjection.rb:6:5:6:8 | name |
| PgInjection.rb:13:5:13:8 | qry1 | PgInjection.rb:14:15:14:18 | qry1 |
| PgInjection.rb:13:5:13:8 | qry1 | PgInjection.rb:15:21:15:24 | qry1 |
| PgInjection.rb:19:5:19:8 | qry2 | PgInjection.rb:20:22:20:25 | qry2 |
| PgInjection.rb:19:5:19:8 | qry2 | PgInjection.rb:21:28:21:31 | qry2 |
| PgInjection.rb:31:5:31:8 | qry3 | PgInjection.rb:32:29:32:32 | qry3 |
| PgInjection.rb:43:5:43:8 | qry3 | PgInjection.rb:44:29:44:32 | qry3 |
nodes
| ActiveRecordInjection.rb:8:25:8:28 | name | semmle.label | name |
| ActiveRecordInjection.rb:8:31:8:34 | pass | semmle.label | pass |
@@ -133,6 +145,19 @@ nodes
| ArelInjection.rb:4:12:4:17 | call to params | semmle.label | call to params |
| ArelInjection.rb:4:12:4:29 | ...[...] | semmle.label | ...[...] |
| ArelInjection.rb:6:20:6:61 | "SELECT * FROM users WHERE nam..." | semmle.label | "SELECT * FROM users WHERE nam..." |
| PgInjection.rb:6:5:6:8 | name | semmle.label | name |
| PgInjection.rb:6:12:6:17 | call to params | semmle.label | call to params |
| PgInjection.rb:6:12:6:24 | ...[...] | semmle.label | ...[...] |
| PgInjection.rb:13:5:13:8 | qry1 | semmle.label | qry1 |
| PgInjection.rb:14:15:14:18 | qry1 | semmle.label | qry1 |
| PgInjection.rb:15:21:15:24 | qry1 | semmle.label | qry1 |
| PgInjection.rb:19:5:19:8 | qry2 | semmle.label | qry2 |
| PgInjection.rb:20:22:20:25 | qry2 | semmle.label | qry2 |
| PgInjection.rb:21:28:21:31 | qry2 | semmle.label | qry2 |
| PgInjection.rb:31:5:31:8 | qry3 | semmle.label | qry3 |
| PgInjection.rb:32:29:32:32 | qry3 | semmle.label | qry3 |
| PgInjection.rb:43:5:43:8 | qry3 | semmle.label | qry3 |
| PgInjection.rb:44:29:44:32 | qry3 | semmle.label | qry3 |
subpaths
#select
| ActiveRecordInjection.rb:10:33:10:67 | "name='#{...}' and pass='#{...}'" | ActiveRecordInjection.rb:70:23:70:28 | call to params | ActiveRecordInjection.rb:10:33:10:67 | "name='#{...}' and pass='#{...}'" | This SQL query depends on a $@. | ActiveRecordInjection.rb:70:23:70:28 | call to params | user-provided value |
@@ -159,3 +184,9 @@ subpaths
| ActiveRecordInjection.rb:177:43:177:104 | "SELECT * FROM users WHERE id ..." | ActiveRecordInjection.rb:173:5:173:10 | call to params | ActiveRecordInjection.rb:177:43:177:104 | "SELECT * FROM users WHERE id ..." | This SQL query depends on a $@. | ActiveRecordInjection.rb:173:5:173:10 | call to params | user-provided value |
| ActiveRecordInjection.rb:178:35:178:96 | "SELECT * FROM users WHERE id ..." | ActiveRecordInjection.rb:173:5:173:10 | call to params | ActiveRecordInjection.rb:178:35:178:96 | "SELECT * FROM users WHERE id ..." | This SQL query depends on a $@. | ActiveRecordInjection.rb:173:5:173:10 | call to params | user-provided value |
| ArelInjection.rb:6:20:6:61 | "SELECT * FROM users WHERE nam..." | ArelInjection.rb:4:12:4:17 | call to params | ArelInjection.rb:6:20:6:61 | "SELECT * FROM users WHERE nam..." | This SQL query depends on a $@. | ArelInjection.rb:4:12:4:17 | call to params | user-provided value |
| PgInjection.rb:14:15:14:18 | qry1 | PgInjection.rb:6:12:6:17 | call to params | PgInjection.rb:14:15:14:18 | qry1 | This SQL query depends on a $@. | PgInjection.rb:6:12:6:17 | call to params | user-provided value |
| PgInjection.rb:15:21:15:24 | qry1 | PgInjection.rb:6:12:6:17 | call to params | PgInjection.rb:15:21:15:24 | qry1 | This SQL query depends on a $@. | PgInjection.rb:6:12:6:17 | call to params | user-provided value |
| PgInjection.rb:20:22:20:25 | qry2 | PgInjection.rb:6:12:6:17 | call to params | PgInjection.rb:20:22:20:25 | qry2 | This SQL query depends on a $@. | PgInjection.rb:6:12:6:17 | call to params | user-provided value |
| PgInjection.rb:21:28:21:31 | qry2 | PgInjection.rb:6:12:6:17 | call to params | PgInjection.rb:21:28:21:31 | qry2 | This SQL query depends on a $@. | PgInjection.rb:6:12:6:17 | call to params | user-provided value |
| PgInjection.rb:32:29:32:32 | qry3 | PgInjection.rb:6:12:6:17 | call to params | PgInjection.rb:32:29:32:32 | qry3 | This SQL query depends on a $@. | PgInjection.rb:6:12:6:17 | call to params | user-provided value |
| PgInjection.rb:44:29:44:32 | qry3 | PgInjection.rb:6:12:6:17 | call to params | PgInjection.rb:44:29:44:32 | qry3 | This SQL query depends on a $@. | PgInjection.rb:6:12:6:17 | call to params | user-provided value |