mirror of
https://github.com/github/codeql.git
synced 2026-04-25 08:45:14 +02:00
Merge branch 'main' into maikypedia/sqli-sink-2
This commit is contained in:
@@ -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
@@ -0,0 +1,2 @@
|
||||
description: Sync dbscheme fragments
|
||||
compatibility: full
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
|
||||
4
ruby/ql/lib/change-notes/2023-05-06-pg.md
Normal file
4
ruby/ql/lib/change-notes/2023-05-06-pg.md
Normal 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.
|
||||
@@ -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.
|
||||
@@ -1,2 +1,2 @@
|
||||
---
|
||||
lastReleaseVersion: 0.6.1
|
||||
lastReleaseVersion: 0.6.2
|
||||
|
||||
@@ -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, _, _, _, _) }
|
||||
|
||||
|
||||
@@ -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()
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -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() }
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -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 }
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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, _)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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 { }
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
77
ruby/ql/lib/codeql/ruby/frameworks/Pg.qll
Normal file
77
ruby/ql/lib/codeql/ruby/frameworks/Pg.qll
Normal 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 }
|
||||
}
|
||||
}
|
||||
@@ -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)))
|
||||
}
|
||||
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
22
ruby/ql/lib/ide-contextual-queries/printCfg.ql
Normal file
22
ruby/ql/lib/ide-contextual-queries/printCfg.ql
Normal 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())
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -0,0 +1,2 @@
|
||||
description: Sync dbscheme fragments
|
||||
compatibility: full
|
||||
@@ -1,3 +1,7 @@
|
||||
## 0.6.2
|
||||
|
||||
No user-facing changes.
|
||||
|
||||
## 0.6.1
|
||||
|
||||
No user-facing changes.
|
||||
|
||||
5
ruby/ql/src/change-notes/2023-05-24-delete-name-clash.md
Normal file
5
ruby/ql/src/change-notes/2023-05-24-delete-name-clash.md
Normal 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.
|
||||
@@ -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.
|
||||
3
ruby/ql/src/change-notes/released/0.6.2.md
Normal file
3
ruby/ql/src/change-notes/released/0.6.2.md
Normal file
@@ -0,0 +1,3 @@
|
||||
## 0.6.2
|
||||
|
||||
No user-facing changes.
|
||||
@@ -1,2 +1,2 @@
|
||||
---
|
||||
lastReleaseVersion: 0.6.1
|
||||
lastReleaseVersion: 0.6.2
|
||||
|
||||
@@ -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> < </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><</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><</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>
|
||||
24
ruby/ql/src/experimental/cwe-176/UnicodeBypassValidation.ql
Normal file
24
ruby/ql/src/experimental/cwe-176/UnicodeBypassValidation.ql
Normal 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"
|
||||
@@ -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
|
||||
@@ -0,0 +1,2 @@
|
||||
s = "﹤xss>"
|
||||
puts s.delete("<").unicode_normalize(:nfkc).include?("<")
|
||||
@@ -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
|
||||
|
||||
16
ruby/ql/src/queries/meta/SummarizedCallableCallSites.ql
Normal file
16
ruby/ql/src/queries/meta/SummarizedCallableCallSites.ql
Normal 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
|
||||
@@ -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) {
|
||||
|
||||
@@ -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 > 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>
|
||||
|
||||
@@ -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 |
|
||||
@@ -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 |
|
||||
@@ -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, _)
|
||||
)
|
||||
|
||||
@@ -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) |
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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 |
|
||||
|
||||
@@ -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
@@ -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:
|
||||
|
||||
@@ -6,3 +6,4 @@ dependencies:
|
||||
codeql/ruby-all: ${workspace}
|
||||
extractor: ruby
|
||||
tests: .
|
||||
warnOnImplicitThis: true
|
||||
|
||||
@@ -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 |
|
||||
@@ -0,0 +1 @@
|
||||
experimental/cwe-176/UnicodeBypassValidation.ql
|
||||
@@ -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
|
||||
70
ruby/ql/test/query-tests/security/cwe-089/PgInjection.rb
Normal file
70
ruby/ql/test/query-tests/security/cwe-089/PgInjection.rb
Normal 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
|
||||
@@ -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 |
|
||||
Reference in New Issue
Block a user