Merge branch 'main' into patch-7

This commit is contained in:
Joe Farebrother
2024-08-12 15:12:33 +01:00
committed by GitHub
2515 changed files with 188168 additions and 39715 deletions

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,4 @@
description: Remove `ruby_element_reference_block` DB relation
compatibility: backwards
ruby_element_reference_block.rel: delete

Binary file not shown.

View File

@@ -9,7 +9,7 @@ edition = "2021"
[dependencies]
tree-sitter = ">= 0.22.6"
tree-sitter-embedded-template = { git = "https://github.com/tree-sitter/tree-sitter-embedded-template.git", rev = "38d5004a797298dc42c85e7706c5ceac46a3f29f" }
tree-sitter-ruby = { git = "https://github.com/tree-sitter/tree-sitter-ruby.git", rev = "dc2d7d6b50f9975bc3c35bbec0ba11b2617b736b" }
tree-sitter-ruby = { git = "https://github.com/tree-sitter/tree-sitter-ruby.git", rev = "0ffe457fb6aabf064f173fd30ea356845cef2513" }
clap = { version = "4.2", features = ["derive"] }
tracing = "0.1"
tracing-subscriber = { version = "0.3.3", features = ["env-filter"] }

View File

@@ -1,7 +1,2 @@
import os
from create_database_utils import *
from diagnostics_test_utils import *
run_codeql_database_create([], lang="ruby", runFunction = runSuccessfully, db = None)
check_diagnostics()
def test(codeql, ruby):
codeql.database.create()

View File

@@ -16,4 +16,4 @@
"visibility": {
"statusPage": true
}
}
}

View File

@@ -1,7 +1,2 @@
import os
from create_database_utils import *
from diagnostics_test_utils import *
run_codeql_database_create([], lang="ruby", runFunction = runSuccessfully, db = None)
check_diagnostics()
def test(codeql, ruby):
codeql.database.create()

View File

@@ -1,4 +0,0 @@
dependencies:
codeql/ruby-all: '*'
codeql/ruby-queries: '*'
warnOnImplicitThis: true

View File

@@ -1 +0,0 @@
These tests are still run with the legacy test runner

View File

@@ -1,3 +1,22 @@
## 1.0.5
No user-facing changes.
## 1.0.4
No user-facing changes.
## 1.0.3
### Minor Analysis Improvements
* Element references with blocks, such as `foo[:bar] { |x| puts x}`, are now parsed correctly.
* The `CleartextSources.qll` library, used by `rb/clear-text-logging-sensitive-data` and `rb/clear-text-logging-sensitive-data`, has been updated to consider heuristics for additional categories of sensitive data.
## 1.0.2
No user-facing changes.
## 1.0.1
No user-facing changes.

View File

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

View File

@@ -1,4 +1,6 @@
---
category: minorAnalysis
---
* The `CleartextSources.qll` library, used by `rb/clear-text-logging-sensitive-data` and `rb/clear-text-logging-sensitive-data`, has been updated to consider heuristics for additional categories of sensitive data.
## 1.0.3
### Minor Analysis Improvements
* Element references with blocks, such as `foo[:bar] { |x| puts x}`, are now parsed correctly.
* The `CleartextSources.qll` library, used by `rb/clear-text-logging-sensitive-data` and `rb/clear-text-logging-sensitive-data`, has been updated to consider heuristics for additional categories of sensitive data.

View File

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

View File

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

View File

@@ -1,2 +1,2 @@
---
lastReleaseVersion: 1.0.1
lastReleaseVersion: 1.0.5

View File

@@ -112,7 +112,7 @@ class ElementReferenceImpl extends MethodCallImpl, TElementReference {
final override string getMethodNameImpl() { result = "[]" }
final override Block getBlockImpl() { none() }
final override Block getBlockImpl() { toGenerated(result) = g.getBlock() }
}
abstract class SuperCallImpl extends MethodCallImpl, TSuperCall { }

View File

@@ -633,6 +633,9 @@ module Ruby {
/** Gets the name of the primary QL class for this element. */
final override string getAPrimaryQlClass() { result = "ElementReference" }
/** Gets the node corresponding to the field `block`. */
final AstNode getBlock() { ruby_element_reference_block(this, result) }
/** Gets the node corresponding to the field `object`. */
final UnderscorePrimary getObject() { ruby_element_reference_def(this, result) }
@@ -641,7 +644,9 @@ module Ruby {
/** Gets a field or child node of this node. */
final override AstNode getAFieldOrChild() {
ruby_element_reference_def(this, result) or ruby_element_reference_child(this, _, result)
ruby_element_reference_block(this, result) or
ruby_element_reference_def(this, result) or
ruby_element_reference_child(this, _, result)
}
}

View File

@@ -0,0 +1,11 @@
private import codeql.ruby.CFG
/** Holds if the guard `guard` controls block `bb` upon evaluating to `branch`. */
pragma[nomagic]
predicate guardControlsBlock(CfgNodes::AstCfgNode guard, BasicBlock bb, boolean branch) {
exists(ConditionBlock conditionBlock, SuccessorTypes::ConditionalSuccessor s |
guard = conditionBlock.getLastNode() and
s.getValue() = branch and
conditionBlock.controls(bb, s)
)
}

View File

@@ -176,9 +176,6 @@ module Ssa {
override string toString() { result = this.getControlFlowNode().toString() }
/** Gets the location of this SSA definition. */
Location getLocation() { result = this.getControlFlowNode().getLocation() }
/** Gets the scope of this SSA definition. */
CfgScope getScope() { result = this.getBasicBlock().getScope() }
}
@@ -205,8 +202,17 @@ module Ssa {
final VariableWriteAccessCfgNode getWriteAccess() { result = write }
/**
* Holds if this SSA definition represents a direct assignment of `value`
* to the underlying variable.
* Holds if this SSA definition assigns `value` to the underlying variable.
*
* This is either a direct assignment, `x = value`, or an assignment via
* simple pattern matching
*
* ```rb
* case value
* in Foo => x then ...
* in y => then ...
* end
* ```
*/
predicate assigns(CfgNodes::ExprCfgNode value) {
exists(CfgNodes::ExprNodes::AssignExprCfgNode a, BasicBlock bb, int i |
@@ -214,6 +220,14 @@ module Ssa {
a = bb.getNode(i) and
value = a.getRhs()
)
or
exists(CfgNodes::ExprNodes::CaseExprCfgNode case, CfgNodes::AstCfgNode pattern |
case.getValue() = value and
pattern = case.getBranch(_).(CfgNodes::ExprNodes::InClauseCfgNode).getPattern()
|
this.getWriteAccess() =
[pattern, pattern.(CfgNodes::ExprNodes::AsPatternCfgNode).getVariableAccess()]
)
}
final override string toString() { result = write.toString() }

View File

@@ -113,16 +113,6 @@ class DataFlowCallable extends TDataFlowCallable {
this instanceof TLibraryCallable and
result instanceof EmptyLocation
}
/** Gets a best-effort total ordering. */
int totalorder() {
this =
rank[result](DataFlowCallable c, string file, int startline, int startcolumn |
c.getLocation().hasLocationInfo(file, startline, startcolumn, _, _)
|
c order by file, startline, startcolumn
)
}
}
/**
@@ -154,16 +144,6 @@ abstract class DataFlowCall extends TDataFlowCall {
) {
this.getLocation().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
}
/** Gets a best-effort total ordering. */
int totalorder() {
this =
rank[result](DataFlowCall c, int startline, int startcolumn |
c.hasLocationInfo(_, startline, startcolumn, _, _)
|
c order by startline, startcolumn
)
}
}
/**
@@ -1093,8 +1073,7 @@ private module TrackSingletonMethodOnInstanceInput implements CallGraphConstruct
singletonMethodOnInstance(_, _, nodeFromPreExpr.getExpr())
)
|
nodeFromPreExpr =
LocalFlow::getParameterDefNode(p.getParameter()).getDefinitionExt().getARead()
nodeFromPreExpr = getParameterDef(p.getParameter()).getARead()
or
nodeFromPreExpr = p.(SelfParameterNodeImpl).getSelfDefinition().getARead()
)

View File

@@ -72,134 +72,48 @@ CfgNodes::ExprCfgNode getAPostUpdateNodeForArg(Argument arg) {
not exists(getALastEvalNode(result))
}
/** An SSA definition into which another SSA definition may flow. */
class SsaInputDefinitionExt extends SsaImpl::DefinitionExt {
SsaInputDefinitionExt() {
this instanceof Ssa::PhiNode
/** Gets the SSA definition node corresponding to parameter `p`. */
pragma[nomagic]
SsaImpl::DefinitionExt getParameterDef(NamedParameter p) {
exists(BasicBlock bb, int i |
bb.getNode(i).getAstNode() = p.getDefiningAccess() and
result.definesAt(_, bb, i, _)
)
}
/** Provides logic related to SSA. */
module SsaFlow {
private module Impl = SsaImpl::DataFlowIntegration;
private ParameterNodeImpl toParameterNode(SsaImpl::ParameterExt p) {
result = TNormalParameterNode(p.asParameter())
or
this instanceof SsaImpl::PhiReadNode
result = TSelfMethodParameterNode(p.asMethodSelf())
or
result = TSelfToplevelParameterNode(p.asToplevelSelf())
}
predicate hasInputFromBlock(SsaImpl::DefinitionExt def, BasicBlock bb, int i, BasicBlock input) {
SsaImpl::lastRefBeforeRedefExt(def, bb, i, input, this)
Impl::Node asNode(Node n) {
n = TSsaNode(result)
or
result.(Impl::ExprNode).getExpr() = n.asExpr()
or
result.(Impl::ExprPostUpdateNode).getExpr() = n.(PostUpdateNode).getPreUpdateNode().asExpr()
or
n = toParameterNode(result.(Impl::ParameterNode).getParameter())
}
predicate localFlowStep(SsaImpl::DefinitionExt def, Node nodeFrom, Node nodeTo, boolean isUseStep) {
Impl::localFlowStep(def, asNode(nodeFrom), asNode(nodeTo), isUseStep)
}
predicate localMustFlowStep(SsaImpl::DefinitionExt def, Node nodeFrom, Node nodeTo) {
Impl::localMustFlowStep(def, asNode(nodeFrom), asNode(nodeTo))
}
}
/** Provides predicates related to local data flow. */
module LocalFlow {
/**
* Holds if `nodeFrom` is a node for SSA definition `def`, which can reach `next`.
*/
pragma[nomagic]
private predicate localFlowSsaInputFromDef(
SsaDefinitionExtNode nodeFrom, SsaImpl::DefinitionExt def, SsaInputNode nodeTo
) {
exists(BasicBlock bb, int i, BasicBlock input, SsaInputDefinitionExt next |
next.hasInputFromBlock(def, bb, i, input) and
def = nodeFrom.getDefinitionExt() and
def.definesAt(_, bb, i, _) and
nodeTo = TSsaInputNode(next, input)
)
}
/**
* Holds if `nodeFrom` is a last read of SSA definition `def`, which
* can reach `nodeTo`.
*/
pragma[nomagic]
predicate localFlowSsaInputFromRead(SsaImpl::DefinitionExt def, Node nodeFrom, SsaInputNode nodeTo) {
exists(
BasicBlock bb, int i, CfgNodes::ExprCfgNode exprFrom, BasicBlock input,
SsaInputDefinitionExt next
|
next.hasInputFromBlock(def, bb, i, input) and
exprFrom = bb.getNode(i) and
exprFrom.getExpr() instanceof VariableReadAccess and
exprFrom = [nodeFrom.asExpr(), nodeFrom.(PostUpdateNodeImpl).getPreUpdateNode().asExpr()] and
nodeTo = TSsaInputNode(next, input)
)
}
/** Gets the SSA definition node corresponding to parameter `p`. */
pragma[nomagic]
SsaDefinitionExtNode getParameterDefNode(NamedParameter p) {
exists(BasicBlock bb, int i |
bb.getNode(i).getAstNode() = p.getDefiningAccess() and
result.getDefinitionExt().definesAt(_, bb, i, _)
)
}
/**
* Holds if `nodeFrom` is a parameter node, and `nodeTo` is a corresponding SSA node.
*/
pragma[nomagic]
predicate localFlowSsaParamInput(ParameterNodeImpl nodeFrom, SsaDefinitionExtNode nodeTo) {
nodeTo = getParameterDefNode(nodeFrom.getParameter())
or
nodeTo.getDefinitionExt() = nodeFrom.(SelfParameterNodeImpl).getSelfDefinition()
}
/**
* Holds if there is a local use-use flow step from `nodeFrom` to `nodeTo`
* involving SSA definition `def`.
*/
predicate localSsaFlowStepUseUse(SsaImpl::DefinitionExt def, Node nodeFrom, Node nodeTo) {
SsaImpl::adjacentReadPairExt(def, nodeFrom.asExpr(), nodeTo.asExpr())
}
/**
* Holds if SSA definition `def` assigns `value` to the underlying variable.
*
* This is either a direct assignment, `x = value`, or an assignment via
* simple pattern matching
*
* ```rb
* case value
* in Foo => x then ...
* in y => then ...
* end
* ```
*/
predicate ssaDefAssigns(Ssa::WriteDefinition def, CfgNodes::ExprCfgNode value) {
def.assigns(value)
or
exists(CfgNodes::ExprNodes::CaseExprCfgNode case, CfgNodes::AstCfgNode pattern |
case.getValue() = value and
pattern = case.getBranch(_).(CfgNodes::ExprNodes::InClauseCfgNode).getPattern()
|
def.getWriteAccess() = pattern
or
def.getWriteAccess() = pattern.(CfgNodes::ExprNodes::AsPatternCfgNode).getVariableAccess()
)
}
/**
* Holds if there is a local flow step from `nodeFrom` to `nodeTo` involving
* SSA definition `def`.
*/
pragma[nomagic]
predicate localSsaFlowStep(SsaImpl::DefinitionExt def, Node nodeFrom, Node nodeTo) {
// Flow from assignment into SSA definition
ssaDefAssigns(def, nodeFrom.asExpr()) and
nodeTo.(SsaDefinitionExtNode).getDefinitionExt() = def
or
// Flow from SSA definition to first read
def = nodeFrom.(SsaDefinitionExtNode).getDefinitionExt() and
SsaImpl::firstReadExt(def, nodeTo.asExpr())
or
// Flow from post-update read to next read
localSsaFlowStepUseUse(def, nodeFrom.(PostUpdateNodeImpl).getPreUpdateNode(), nodeTo)
or
// Flow into phi (read) SSA definition node from def
localFlowSsaInputFromDef(nodeFrom, def, nodeTo)
or
nodeTo.(SsaDefinitionExtNode).getDefinitionExt() = def and
def = nodeFrom.(SsaInputNode).getDefinitionExt()
or
localFlowSsaParamInput(nodeFrom, nodeTo) and
def = nodeTo.(SsaDefinitionExtNode).getDefinitionExt()
}
pragma[nomagic]
predicate localFlowStepCommon(Node nodeFrom, Node nodeTo) {
nodeFrom.asExpr() = nodeTo.asExpr().(CfgNodes::ExprNodes::BlockArgumentCfgNode).getValue()
@@ -240,7 +154,7 @@ module LocalFlow {
p.(KeywordParameter).getDefaultValue() = nodeFrom.asExpr().getExpr()
)
or
nodeTo.(BlockArgumentNode).getParameterNode(true) = nodeFrom
nodeTo.(ImplicitBlockArgumentNode).getParameterNode(true) = nodeFrom
}
predicate flowSummaryLocalStep(
@@ -253,21 +167,13 @@ module LocalFlow {
}
predicate localMustFlowStep(Node node1, Node node2) {
LocalFlow::localFlowSsaParamInput(node1, node2)
or
exists(SsaImpl::Definition def |
def.(Ssa::WriteDefinition).assigns(node1.asExpr()) and
node2.(SsaDefinitionExtNode).getDefinitionExt() = def
or
def = node1.(SsaDefinitionExtNode).getDefinitionExt() and
node2.asExpr() = SsaImpl::getARead(def)
)
SsaFlow::localMustFlowStep(_, node1, node2)
or
node1.asExpr() = node2.asExpr().(CfgNodes::ExprNodes::AssignExprCfgNode).getRhs()
or
node1.asExpr() = node2.asExpr().(CfgNodes::ExprNodes::BlockArgumentCfgNode).getValue()
or
node1 = node2.(BlockArgumentNode).getParameterNode(true)
node1 = node2.(ImplicitBlockArgumentNode).getParameterNode(true)
or
node1 =
unique(FlowSummaryNode n1 |
@@ -347,13 +253,12 @@ module VariableCapture {
or
exists(Ssa::Definition def |
def.getARead() = e2 and
LocalFlow::ssaDefAssigns(def.getAnUltimateDefinition(), e1)
def.getAnUltimateDefinition().(Ssa::WriteDefinition).assigns(e1)
)
}
private module CaptureInput implements Shared::InputSig<Location> {
private import ruby as R
private import codeql.ruby.controlflow.ControlFlowGraph
private import codeql.ruby.controlflow.ControlFlowGraph as Cfg
private import codeql.ruby.controlflow.BasicBlocks as BasicBlocks
private import TaintTrackingPrivate as TaintTrackingPrivate
@@ -361,6 +266,8 @@ module VariableCapture {
Callable getEnclosingCallable() { result = this.getScope() }
}
class ControlFlowNode = Cfg::CfgNode;
BasicBlock getImmediateBasicBlockDominator(BasicBlock bb) {
result = bb.getImmediateDominator()
}
@@ -537,23 +444,14 @@ private module Cached {
newtype TNode =
TExprNode(CfgNodes::ExprCfgNode n) or
TReturningNode(CfgNodes::ReturningCfgNode n) { exists(n.getReturnedValueNode()) } or
TSsaDefinitionExtNode(SsaImpl::DefinitionExt def) or
TSsaInputNode(SsaInputDefinitionExt def, BasicBlock input) {
def.hasInputFromBlock(_, _, _, input)
} or
TSsaNode(SsaImpl::DataFlowIntegration::SsaNode node) or
TCapturedVariableNode(VariableCapture::CapturedVariable v) or
TNormalParameterNode(Parameter p) {
p instanceof SimpleParameter or
p instanceof OptionalParameter or
p instanceof KeywordParameter or
p instanceof HashSplatParameter or
p instanceof SplatParameter
} or
TNormalParameterNode(SsaImpl::NormalParameter p) or
TSelfMethodParameterNode(MethodBase m) or
TSelfToplevelParameterNode(Toplevel t) or
TLambdaSelfReferenceNode(Callable c) { lambdaCreationExpr(_, _, c) } or
TBlockParameterNode(MethodBase m) or
TBlockArgumentNode(CfgNodes::ExprNodes::CallCfgNode yield) {
TImplicitBlockParameterNode(MethodBase m) { not m.getAParameter() instanceof BlockParameter } or
TImplicitBlockArgumentNode(CfgNodes::ExprNodes::CallCfgNode yield) {
yield = any(BlockParameterNode b).getAYieldCall()
} or
TSynthHashSplatParameterNode(DataFlowCallable c) {
@@ -599,7 +497,7 @@ private module Cached {
class TSelfParameterNode = TSelfMethodParameterNode or TSelfToplevelParameterNode;
class TSourceParameterNode =
TNormalParameterNode or TBlockParameterNode or TSelfParameterNode or
TNormalParameterNode or TImplicitBlockParameterNode or TSelfParameterNode or
TSynthHashSplatParameterNode or TSynthSplatParameterNode;
cached
@@ -617,16 +515,13 @@ private module Cached {
(
LocalFlow::localFlowStepCommon(nodeFrom, nodeTo)
or
exists(SsaImpl::DefinitionExt def |
exists(SsaImpl::DefinitionExt def, boolean isUseStep |
SsaFlow::localFlowStep(def, nodeFrom, nodeTo, isUseStep) and
// captured variables are handled by the shared `VariableCapture` library
not def instanceof VariableCapture::CapturedSsaDefinitionExt
|
LocalFlow::localSsaFlowStep(def, nodeFrom, nodeTo)
isUseStep = false
or
LocalFlow::localSsaFlowStepUseUse(def, nodeFrom, nodeTo) and
not FlowSummaryImpl::Private::Steps::prohibitsUseUseFlow(nodeFrom, _)
or
LocalFlow::localFlowSsaInputFromRead(def, nodeFrom, nodeTo) and
not FlowSummaryImpl::Private::Steps::prohibitsUseUseFlow(nodeFrom, _)
)
or
@@ -642,9 +537,7 @@ private module Cached {
predicate localFlowStepImpl(Node nodeFrom, Node nodeTo) {
LocalFlow::localFlowStepCommon(nodeFrom, nodeTo)
or
LocalFlow::localSsaFlowStep(_, nodeFrom, nodeTo)
or
LocalFlow::localSsaFlowStepUseUse(_, nodeFrom, nodeTo)
SsaFlow::localFlowStep(_, nodeFrom, nodeTo, _)
or
// Simple flow through library code is included in the exposed local
// step relation, even though flow is technically inter-procedural
@@ -658,11 +551,7 @@ private module Cached {
predicate localFlowStepTypeTracker(Node nodeFrom, Node nodeTo) {
LocalFlow::localFlowStepCommon(nodeFrom, nodeTo)
or
LocalFlow::localSsaFlowStep(_, nodeFrom, nodeTo)
or
LocalFlow::localSsaFlowStepUseUse(_, nodeFrom, nodeTo)
or
LocalFlow::localFlowSsaInputFromRead(_, nodeFrom, nodeTo)
SsaFlow::localFlowStep(_, nodeFrom, nodeTo, _)
or
VariableCapture::flowInsensitiveStep(nodeFrom, nodeTo)
or
@@ -672,7 +561,8 @@ private module Cached {
/** Holds if `n` wraps an SSA definition without ingoing flow. */
private predicate entrySsaDefinition(SsaDefinitionExtNode n) {
n.getDefinitionExt() = any(SsaImpl::WriteDefinition def | not LocalFlow::ssaDefAssigns(def, _))
n.getDefinitionExt() =
any(SsaImpl::WriteDefinition def | not def.(Ssa::WriteDefinition).assigns(_))
}
pragma[nomagic]
@@ -712,13 +602,15 @@ private module Cached {
// Ensure all entry SSA definitions are local sources, except those that correspond
// to parameters (which are themselves local sources)
entrySsaDefinition(n) and
not LocalFlow::localFlowSsaParamInput(_, n)
not exists(SsaImpl::ParameterExt p |
p.isInitializedBy(n.(SsaDefinitionExtNode).getDefinitionExt())
)
or
isStoreTargetNode(n)
or
TypeTrackingInput::loadStep(_, n, _)
or
n instanceof BlockArgumentNode
n instanceof ImplicitBlockArgumentNode
}
cached
@@ -822,11 +714,7 @@ import Cached
/** Holds if `n` should be hidden from path explanations. */
predicate nodeIsHidden(Node n) {
n.(SsaDefinitionExtNode).isHidden()
or
n instanceof SsaInputNode
or
n = LocalFlow::getParameterDefNode(_)
n.(SsaNode).isHidden()
or
exists(AstNode desug |
isDesugarNode(desug) and
@@ -858,33 +746,55 @@ predicate nodeIsHidden(Node n) {
or
n instanceof CaptureNode
or
n instanceof BlockArgumentNode
n instanceof ImplicitBlockArgumentNode
}
/** An SSA definition, viewed as a node in a data flow graph. */
class SsaDefinitionExtNode extends NodeImpl, TSsaDefinitionExtNode {
/** An SSA node. */
abstract class SsaNode extends NodeImpl, TSsaNode {
SsaImpl::DataFlowIntegration::SsaNode node;
SsaImpl::DefinitionExt def;
SsaDefinitionExtNode() { this = TSsaDefinitionExtNode(def) }
SsaNode() {
this = TSsaNode(node) and
def = node.getDefinitionExt()
}
/** Gets the underlying SSA definition. */
SsaImpl::DefinitionExt getDefinitionExt() { result = def }
/** Holds if this node should be hidden from path explanations. */
abstract predicate isHidden();
override Location getLocationImpl() { result = node.getLocation() }
override string toStringImpl() { result = node.toString() }
}
/** An (extended) SSA definition, viewed as a node in a data flow graph. */
class SsaDefinitionExtNode extends SsaNode {
override SsaImpl::DataFlowIntegration::SsaDefinitionExtNode node;
/** Gets the underlying variable. */
Variable getVariable() { result = def.getSourceVariable() }
/** Holds if this node should be hidden from path explanations. */
predicate isHidden() {
override predicate isHidden() {
not def instanceof Ssa::WriteDefinition
or
isDesugarNode(def.(Ssa::WriteDefinition).getWriteAccess().getExpr())
or
def = getParameterDef(_)
}
override CfgScope getCfgScope() { result = def.getBasicBlock().getScope() }
}
override Location getLocationImpl() { result = def.getLocation() }
class SsaDefinitionNodeImpl extends SsaDefinitionExtNode {
Ssa::Definition ssaDef;
override string toStringImpl() { result = def.toString() }
SsaDefinitionNodeImpl() { ssaDef = def }
override Location getLocationImpl() { result = ssaDef.getLocation() }
override string toStringImpl() { result = ssaDef.toString() }
}
/**
@@ -922,20 +832,12 @@ class SsaDefinitionExtNode extends NodeImpl, TSsaDefinitionExtNode {
*
* both inputs into the phi read node after the outer condition are guarded.
*/
class SsaInputNode extends NodeImpl, TSsaInputNode {
SsaImpl::DefinitionExt def;
BasicBlock input;
class SsaInputNode extends SsaNode {
override SsaImpl::DataFlowIntegration::SsaInputNode node;
SsaInputNode() { this = TSsaInputNode(def, input) }
override predicate isHidden() { any() }
/** Gets the underlying SSA definition. */
SsaImpl::DefinitionExt getDefinitionExt() { result = def }
override CfgScope getCfgScope() { result = input.getScope() }
override Location getLocationImpl() { result = input.getLastNode().getLocation() }
override string toStringImpl() { result = "[input] " + def }
override CfgScope getCfgScope() { result = node.getDefinitionExt().getBasicBlock().getScope() }
}
/** An SSA definition for a `self` variable. */
@@ -1023,7 +925,7 @@ private module ParameterNodes {
* flow graph.
*/
class NormalParameterNode extends ParameterNodeImpl, TNormalParameterNode {
private Parameter parameter;
Parameter parameter;
NormalParameterNode() { this = TNormalParameterNode(parameter) }
@@ -1052,6 +954,9 @@ private module ParameterNodes {
// There are no positional parameters after the splat
not exists(SimpleParameter p, int m | m > n | p = callable.getParameter(m))
)
or
parameter = callable.getAParameter().(BlockParameter) and
pos.isBlock()
)
}
@@ -1159,24 +1064,32 @@ private module ParameterNodes {
* The value of a block parameter at function entry, viewed as a node in a data
* flow graph.
*/
class BlockParameterNode extends ParameterNodeImpl, TBlockParameterNode {
private MethodBase method;
BlockParameterNode() { this = TBlockParameterNode(method) }
final MethodBase getMethod() { result = method }
override Parameter getParameter() {
result = method.getAParameter() and result instanceof BlockParameter
}
override predicate isParameterOf(DataFlowCallable c, ParameterPosition pos) {
c.asCfgScope() = method and pos.isBlock()
}
abstract class BlockParameterNode extends ParameterNodeImpl {
abstract MethodBase getMethod();
CfgNodes::ExprNodes::CallCfgNode getAYieldCall() {
this.getMethod() = result.getExpr().(YieldCall).getEnclosingMethod()
}
}
private class ExplicitBlockParameterNode extends BlockParameterNode, NormalParameterNode {
override BlockParameter parameter;
final override MethodBase getMethod() { result.getAParameter() = parameter }
}
private class ImplicitBlockParameterNode extends BlockParameterNode, TImplicitBlockParameterNode {
private MethodBase method;
ImplicitBlockParameterNode() { this = TImplicitBlockParameterNode(method) }
final override MethodBase getMethod() { result = method }
override Parameter getParameter() { none() }
override predicate isParameterOf(DataFlowCallable c, ParameterPosition pos) {
c.asCfgScope() = method and pos.isBlock()
}
override CfgScope getCfgScope() { result = method }
@@ -1449,10 +1362,10 @@ module ArgumentNodes {
}
}
class BlockArgumentNode extends NodeImpl, ArgumentNode, TBlockArgumentNode {
class ImplicitBlockArgumentNode extends NodeImpl, ArgumentNode, TImplicitBlockArgumentNode {
CfgNodes::ExprNodes::CallCfgNode yield;
BlockArgumentNode() { this = TBlockArgumentNode(yield) }
ImplicitBlockArgumentNode() { this = TImplicitBlockArgumentNode(yield) }
CfgNodes::ExprNodes::CallCfgNode getYieldCall() { result = yield }
@@ -1890,7 +1803,7 @@ predicate jumpStep(Node pred, Node succ) {
or
any(AdditionalJumpStep s).step(pred, succ)
or
succ.(BlockArgumentNode).getParameterNode(false) = pred
succ.(ImplicitBlockArgumentNode).getParameterNode(false) = pred
}
private ContentSet getArrayContent(int n) {
@@ -2074,9 +1987,6 @@ DataFlowType getNodeType(Node n) {
result = TUnknownDataFlowType()
}
/** Gets a string representation of a `DataFlowType`. */
string ppReprType(DataFlowType t) { none() }
pragma[inline]
private predicate compatibleTypesNonSymRefl(DataFlowType t1, DataFlowType t2) {
t1 != TUnknownDataFlowType() and
@@ -2087,7 +1997,6 @@ private predicate compatibleTypesNonSymRefl(DataFlowType t1, DataFlowType t2) {
* Holds if `t1` and `t2` are compatible, that is, whether data can flow from
* a node of type `t1` to a node of type `t2`.
*/
pragma[inline]
predicate compatibleTypes(DataFlowType t1, DataFlowType t2) {
t1 = t2
or
@@ -2181,8 +2090,6 @@ class NodeRegion instanceof Unit {
string toString() { result = "NodeRegion" }
predicate contains(Node n) { none() }
int totalOrder() { result = 1 }
}
/**
@@ -2239,7 +2146,7 @@ private predicate lambdaCallExpr(
*/
predicate lambdaSourceCall(CfgNodes::ExprNodes::CallCfgNode call, LambdaCallKind kind, Node receiver) {
kind = TYieldCallKind() and
call = receiver.(BlockArgumentNode).getYieldCall()
call = receiver.(ImplicitBlockArgumentNode).getYieldCall()
or
kind = TLambdaCallKind() and
lambdaCallExpr(call, receiver.asExpr())

View File

@@ -6,6 +6,7 @@ private import codeql.ruby.typetracking.internal.TypeTrackingImpl
private import codeql.ruby.dataflow.SSA
private import FlowSummaryImpl as FlowSummaryImpl
private import codeql.ruby.ApiGraphs
private import SsaImpl as SsaImpl
/**
* An element, viewed as a node in a data flow graph. Either an expression
@@ -360,16 +361,12 @@ class PostUpdateNode extends Node {
}
/** An SSA definition, viewed as a node in a data flow graph. */
class SsaDefinitionNode extends Node instanceof SsaDefinitionExtNode {
Ssa::Definition def;
SsaDefinitionNode() { this = TSsaDefinitionExtNode(def) }
class SsaDefinitionNode extends Node instanceof SsaDefinitionNodeImpl {
/** Gets the underlying SSA definition. */
Ssa::Definition getDefinition() { result = def }
Ssa::Definition getDefinition() { result = super.getDefinitionExt() }
/** Gets the underlying variable. */
Variable getVariable() { result = def.getSourceVariable() }
Variable getVariable() { result = this.getDefinition().getSourceVariable() }
}
cached
@@ -870,56 +867,7 @@ private predicate sameSourceVariable(Ssa::Definition def1, Ssa::Definition def2)
* in data flow and taint tracking.
*/
module BarrierGuard<guardChecksSig/3 guardChecks> {
private import SsaImpl as SsaImpl
pragma[nomagic]
private predicate guardChecksSsaDef(CfgNodes::AstCfgNode g, boolean branch, Ssa::Definition def) {
guardChecks(g, def.getARead(), branch)
}
pragma[nomagic]
private predicate guardControlsSsaRead(
CfgNodes::AstCfgNode g, boolean branch, Ssa::Definition def, Node n
) {
def.getARead() = n.asExpr() and
guardControlsBlock(g, n.asExpr().getBasicBlock(), branch)
}
pragma[nomagic]
private predicate guardControlsPhiInput(
CfgNodes::AstCfgNode g, boolean branch, Ssa::Definition def, BasicBlock input,
SsaInputDefinitionExt phi
) {
phi.hasInputFromBlock(def, _, _, input) and
(
guardControlsBlock(g, input, branch)
or
exists(SuccessorTypes::ConditionalSuccessor s |
g = input.getLastNode() and
s.getValue() = branch and
input.getASuccessor(s) = phi.getBasicBlock()
)
)
}
/** Gets a node that is safely guarded by the given guard check. */
Node getABarrierNode() {
exists(CfgNodes::AstCfgNode g, boolean branch, Ssa::Definition def |
guardChecksSsaDef(g, branch, def) and
guardControlsSsaRead(g, branch, def, result)
)
or
exists(
CfgNodes::AstCfgNode g, boolean branch, Ssa::Definition def, BasicBlock input,
SsaInputDefinitionExt phi
|
guardChecksSsaDef(g, branch, def) and
guardControlsPhiInput(g, branch, def, input, phi) and
result = TSsaInputNode(phi, input)
)
or
result.asExpr() = getAMaybeGuardedCapturedDef().getARead()
}
private import codeql.ruby.controlflow.internal.Guards
/**
* Gets an implicit entry definition for a captured variable that
@@ -928,6 +876,7 @@ module BarrierGuard<guardChecksSig/3 guardChecks> {
* This is restricted to calls where the variable is captured inside a
* block.
*/
pragma[nomagic]
private Ssa::CapturedEntryDefinition getAMaybeGuardedCapturedDef() {
exists(
CfgNodes::ExprCfgNode g, boolean branch, CfgNodes::ExprCfgNode testedNode,
@@ -940,15 +889,14 @@ module BarrierGuard<guardChecksSig/3 guardChecks> {
sameSourceVariable(def, result)
)
}
}
/** Holds if the guard `guard` controls block `bb` upon evaluating to `branch`. */
private predicate guardControlsBlock(CfgNodes::AstCfgNode guard, BasicBlock bb, boolean branch) {
exists(ConditionBlock conditionBlock, SuccessorTypes::ConditionalSuccessor s |
guard = conditionBlock.getLastNode() and
s.getValue() = branch and
conditionBlock.controls(bb, s)
)
/** Gets a node that is safely guarded by the given guard check. */
Node getABarrierNode() {
SsaFlow::asNode(result) =
SsaImpl::DataFlowIntegration::BarrierGuard<guardChecks/3>::getABarrierNode()
or
result.asExpr() = getAMaybeGuardedCapturedDef().getARead()
}
}
/**

View File

@@ -6,16 +6,19 @@ private import codeql.ruby.dataflow.SSA
private import codeql.ruby.ast.Variable
private import Cfg::CfgNodes::ExprNodes
private module SsaInput implements SsaImplCommon::InputSig<Location> {
module SsaInput implements SsaImplCommon::InputSig<Location> {
private import codeql.ruby.controlflow.ControlFlowGraph as Cfg
private import codeql.ruby.controlflow.BasicBlocks as BasicBlocks
class BasicBlock = BasicBlocks::BasicBlock;
class ControlFlowNode = Cfg::CfgNode;
BasicBlock getImmediateBasicBlockDominator(BasicBlock bb) { result = bb.getImmediateDominator() }
BasicBlock getABasicBlockSuccessor(BasicBlock bb) { result = bb.getASuccessor() }
class ExitBasicBlock = BasicBlocks::ExitBasicBlock;
class ExitBasicBlock extends BasicBlock, BasicBlocks::ExitBasicBlock { }
class SourceVariable = LocalVariable;
@@ -62,7 +65,7 @@ private module SsaInput implements SsaImplCommon::InputSig<Location> {
}
}
private import SsaImplCommon::Make<Location, SsaInput> as Impl
import SsaImplCommon::Make<Location, SsaInput> as Impl
class Definition = Impl::Definition;
@@ -280,15 +283,6 @@ private predicate adjacentDefSkipUncertainReads(
SsaInput::variableRead(bb2, i2, _, true)
}
/** Same as `adjacentDefReadExt`, but skips uncertain reads. */
pragma[nomagic]
private predicate adjacentDefSkipUncertainReadsExt(
DefinitionExt def, SsaInput::BasicBlock bb1, int i1, SsaInput::BasicBlock bb2, int i2
) {
adjacentDefReachesReadExt(def, bb1, i1, bb2, i2) and
SsaInput::variableRead(bb2, i2, _, true)
}
private predicate adjacentDefReachesUncertainReadExt(
DefinitionExt def, SsaInput::BasicBlock bb1, int i1, SsaInput::BasicBlock bb2, int i2
) {
@@ -390,19 +384,6 @@ private module Cached {
)
}
/**
* Holds if the value defined at SSA definition `def` can reach a read at `read`,
* without passing through any other non-pseudo read.
*/
cached
predicate firstReadExt(DefinitionExt def, VariableReadAccessCfgNode read) {
exists(Cfg::BasicBlock bb1, int i1, Cfg::BasicBlock bb2, int i2 |
def.definesAt(_, bb1, i1, _) and
adjacentDefSkipUncertainReadsExt(def, bb1, i1, bb2, i2) and
read = bb2.getNode(i2)
)
}
/**
* Holds if the read at `read2` is a read of the same SSA definition `def`
* as the read at `read1`, and `read2` can be reached from `read1` without
@@ -420,23 +401,6 @@ private module Cached {
)
}
/**
* Holds if the read at `read2` is a read of the same SSA definition `def`
* as the read at `read1`, and `read2` can be reached from `read1` without
* passing through another non-pseudo read.
*/
cached
predicate adjacentReadPairExt(
DefinitionExt def, VariableReadAccessCfgNode read1, VariableReadAccessCfgNode read2
) {
exists(Cfg::BasicBlock bb1, int i1, Cfg::BasicBlock bb2, int i2 |
read1 = bb1.getNode(i1) and
variableReadActual(bb1, i1, _) and
adjacentDefSkipUncertainReadsExt(def, bb1, i1, bb2, i2) and
read2 = bb2.getNode(i2)
)
}
/**
* Holds if the read of `def` at `read` may be a last read. That is, `read`
* can either reach another definition of the underlying source variable or
@@ -451,32 +415,42 @@ private module Cached {
)
}
/**
* Holds if the reference to `def` at index `i` in basic block `bb` can reach
* another definition `next` of the same underlying source variable, without
* passing through another write or non-pseudo read.
*
* The reference is either a read of `def` or `def` itself.
*/
cached
predicate lastRefBeforeRedefExt(
DefinitionExt def, Cfg::BasicBlock bb, int i, Cfg::BasicBlock input, DefinitionExt next
) {
exists(LocalVariable v |
Impl::lastRefRedefExt(def, v, bb, i, input, next) and
not SsaInput::variableRead(bb, i, v, false)
)
or
exists(SsaInput::BasicBlock bb0, int i0 |
Impl::lastRefRedefExt(def, _, bb0, i0, input, next) and
adjacentDefReachesUncertainReadExt(def, bb, i, bb0, i0)
)
}
cached
Definition uncertainWriteDefinitionInput(UncertainWriteDefinition def) {
Impl::uncertainWriteDefinitionInput(def, result)
}
cached
module DataFlowIntegration {
import DataFlowIntegrationImpl
cached
predicate localFlowStep(DefinitionExt def, Node nodeFrom, Node nodeTo, boolean isUseStep) {
DataFlowIntegrationImpl::localFlowStep(def, nodeFrom, nodeTo, isUseStep)
}
cached
predicate localMustFlowStep(DefinitionExt def, Node nodeFrom, Node nodeTo) {
DataFlowIntegrationImpl::localMustFlowStep(def, nodeFrom, nodeTo)
}
signature predicate guardChecksSig(Cfg::CfgNodes::AstCfgNode g, Cfg::CfgNode e, boolean branch);
cached // nothing is actually cached
module BarrierGuard<guardChecksSig/3 guardChecks> {
private predicate guardChecksAdjTypes(
DataFlowIntegrationInput::Guard g, DataFlowIntegrationInput::Expr e, boolean branch
) {
guardChecks(g, e, branch)
}
private Node getABarrierNodeImpl() {
result = DataFlowIntegrationImpl::BarrierGuard<guardChecksAdjTypes/3>::getABarrierNode()
}
predicate getABarrierNode = getABarrierNodeImpl/0;
}
}
}
import Cached
@@ -494,8 +468,7 @@ class DefinitionExt extends Impl::DefinitionExt {
override string toString() { result = this.(Ssa::Definition).toString() }
/** Gets the location of this definition. */
Location getLocation() { result = this.(Ssa::Definition).getLocation() }
override Location getLocation() { result = this.(Ssa::Definition).getLocation() }
}
/**
@@ -506,5 +479,99 @@ class DefinitionExt extends Impl::DefinitionExt {
class PhiReadNode extends DefinitionExt, Impl::PhiReadNode {
override string toString() { result = "SSA phi read(" + this.getSourceVariable() + ")" }
override Location getLocation() { result = this.getBasicBlock().getLocation() }
override Location getLocation() { result = Impl::PhiReadNode.super.getLocation() }
}
class NormalParameter extends Parameter {
NormalParameter() {
this instanceof SimpleParameter or
this instanceof OptionalParameter or
this instanceof KeywordParameter or
this instanceof HashSplatParameter or
this instanceof SplatParameter or
this instanceof BlockParameter
}
}
/** Gets the SSA definition node corresponding to parameter `p`. */
pragma[nomagic]
DefinitionExt getParameterDef(NamedParameter p) {
exists(Cfg::BasicBlock bb, int i |
bb.getNode(i).getAstNode() = p.getDefiningAccess() and
result.definesAt(_, bb, i, _)
)
}
private newtype TParameterExt =
TNormalParameter(NormalParameter p) or
TSelfMethodParameter(MethodBase m) or
TSelfToplevelParameter(Toplevel t)
/** A normal parameter or an implicit `self` parameter. */
class ParameterExt extends TParameterExt {
NormalParameter asParameter() { this = TNormalParameter(result) }
MethodBase asMethodSelf() { this = TSelfMethodParameter(result) }
Toplevel asToplevelSelf() { this = TSelfToplevelParameter(result) }
predicate isInitializedBy(WriteDefinition def) {
def = getParameterDef(this.asParameter())
or
def.(Ssa::SelfDefinition).getSourceVariable().getDeclaringScope() =
[this.asMethodSelf().(Scope), this.asToplevelSelf()]
}
string toString() {
result =
[
this.asParameter().toString(), this.asMethodSelf().toString(),
this.asToplevelSelf().toString()
]
}
Location getLocation() {
result =
[
this.asParameter().getLocation(), this.asMethodSelf().getLocation(),
this.asToplevelSelf().getLocation()
]
}
}
private module DataFlowIntegrationInput implements Impl::DataFlowIntegrationInputSig {
private import codeql.ruby.controlflow.internal.Guards as Guards
class Parameter = ParameterExt;
class Expr extends Cfg::CfgNodes::ExprCfgNode {
predicate hasCfgNode(SsaInput::BasicBlock bb, int i) { this = bb.getNode(i) }
}
Expr getARead(Definition def) { result = Cached::getARead(def) }
predicate ssaDefAssigns(WriteDefinition def, Expr value) {
def.(Ssa::WriteDefinition).assigns(value)
}
predicate ssaDefInitializesParam(WriteDefinition def, Parameter p) { p.isInitializedBy(def) }
class Guard extends Cfg::CfgNodes::AstCfgNode {
predicate hasCfgNode(SsaInput::BasicBlock bb, int i) { this = bb.getNode(i) }
}
/** Holds if the guard `guard` controls block `bb` upon evaluating to `branch`. */
predicate guardControlsBlock(Guard guard, SsaInput::BasicBlock bb, boolean branch) {
Guards::guardControlsBlock(guard, bb, branch)
}
/** Gets an immediate conditional successor of basic block `bb`, if any. */
SsaInput::BasicBlock getAConditionalBasicBlockSuccessor(SsaInput::BasicBlock bb, boolean branch) {
exists(Cfg::SuccessorTypes::ConditionalSuccessor s |
result = bb.getASuccessor(s) and
s.getValue() = branch
)
}
}
private module DataFlowIntegrationImpl = Impl::DataFlowIntegration<DataFlowIntegrationInput>;

View File

@@ -5,6 +5,7 @@ private import codeql.ruby.CFG
private import codeql.ruby.DataFlow
private import FlowSummaryImpl as FlowSummaryImpl
private import codeql.ruby.dataflow.SSA
private import SsaImpl as SsaImpl
/**
* Holds if `node` should be a sanitizer in all global taint flow configurations
@@ -89,7 +90,7 @@ private module Cached {
clause = case.getBranch(_) and
def = nodeTo.(SsaDefinitionExtNode).getDefinitionExt() and
def.getControlFlowNode() = variablesInPattern(clause.getPattern()) and
not LocalFlow::ssaDefAssigns(def, value)
not def.(Ssa::WriteDefinition).assigns(value)
)
or
// operation involving `nodeFrom`

View File

@@ -29,7 +29,7 @@ class NetHttpRequest extends Http::Client::Request::Range, DataFlow::CallNode {
this = request
|
// Net::HTTP.get(...)
method = "get" and
method in ["get", "get_response"] and
requestNode = API::getTopLevelMember("Net").getMember("HTTP").getReturn(method) and
returnsResponseBody = true
or

View File

@@ -0,0 +1,238 @@
/**
* Provides default sources, sinks and sanitizers for detecting
* "use of a broken or weak cryptographic hashing algorithm on sensitive data"
* vulnerabilities, as well as extension points for adding your own.
*/
private import ruby
private import codeql.ruby.Concepts
private import codeql.ruby.security.SensitiveActions
private import codeql.ruby.dataflow.BarrierGuards
private import codeql.ruby.dataflow.SSA
private module SensitiveDataSources {
/**
* A data flow source of sensitive data, such as secrets, certificates, or passwords.
*
* Extend this class to refine existing API models. If you want to model new APIs,
* extend `SensitiveDataSource::Range` instead.
*/
class SensitiveDataSource extends DataFlow::Node instanceof SensitiveDataSource::Range {
/**
* Gets the classification of the sensitive data.
*/
SensitiveDataClassification getClassification() { result = super.getClassification() }
}
/** Provides a class for modeling new sources of sensitive data, such as secrets, certificates, or passwords. */
module SensitiveDataSource {
/**
* A data flow source of sensitive data, such as secrets, certificates, or passwords.
*
* Extend this class to model new APIs. If you want to refine existing API models,
* extend `SensitiveDataSource` instead.
*/
abstract class Range extends DataFlow::Node {
/**
* Gets the classification of the sensitive data.
*/
abstract SensitiveDataClassification getClassification();
}
}
/**
* A call to a method that may return sensitive data.
*/
class SensitiveMethodCall extends SensitiveDataSource::Range instanceof SensitiveCall {
override SensitiveDataClassification getClassification() {
result = SensitiveCall.super.getClassification()
}
}
/**
* An assignment to a variable that may contain sensitive data.
*/
class SensitiveVariableAssignment extends SensitiveDataSource::Range, DataFlow::SsaDefinitionNode {
SensitiveNode sensitiveNode;
SensitiveVariableAssignment() {
this.getDefinition().(Ssa::WriteDefinition).getWriteAccess() = sensitiveNode.asExpr()
}
override SensitiveDataClassification getClassification() {
result = sensitiveNode.getClassification()
}
}
/**
* A read from a hash value that may return sensitive data.
*/
class SensitiveHashValueAccess extends SensitiveDataSource::Range instanceof SensitiveNode {
SensitiveHashValueAccess() {
this.asExpr() instanceof Cfg::CfgNodes::ExprNodes::ElementReferenceCfgNode
}
override SensitiveDataClassification getClassification() {
result = SensitiveNode.super.getClassification()
}
}
/**
* A parameter node that may contain sensitive data.
*/
class SensitiveParameter extends SensitiveDataSource::Range, DataFlow::ParameterNode instanceof SensitiveNode
{
override SensitiveDataClassification getClassification() {
result = SensitiveNode.super.getClassification()
}
}
}
/**
* Provides default sources, sinks and sanitizers for detecting
* "use of a broken or weak cryptographic hashing algorithm on sensitive data"
* vulnerabilities on sensitive data that does NOT require computationally expensive
* hashing, as well as extension points for adding your own.
*
* Also see the `ComputationallyExpensiveHashFunction` module.
*/
module NormalHashFunction {
/**
* A data flow source for "use of a broken or weak cryptographic hashing algorithm on
* sensitive data" vulnerabilities.
*/
abstract class Source extends DataFlow::Node {
Source() { not this instanceof ComputationallyExpensiveHashFunction::Source }
/** Gets the classification of the sensitive data. */
abstract string getClassification();
}
/**
* A data flow sink for "use of a broken or weak cryptographic hashing algorithm on
* sensitive data" vulnerabilities.
*/
abstract class Sink extends DataFlow::Node {
/**
* Gets the name of the weak hashing algorithm.
*/
abstract string getAlgorithmName();
}
/**
* A sanitizer for "use of a broken or weak cryptographic hashing algorithm on
* sensitive data" vulnerabilities.
*/
abstract class Sanitizer extends DataFlow::Node { }
/**
* A source of sensitive data, considered as a flow source.
*/
class SensitiveDataSourceAsSource extends Source instanceof SensitiveDataSources::SensitiveDataSource
{
override SensitiveDataClassification getClassification() {
result = SensitiveDataSources::SensitiveDataSource.super.getClassification()
}
}
/** The input to a hashing operation using a weak algorithm, considered as a flow sink. */
class WeakHashingOperationInputSink extends Sink {
Cryptography::HashingAlgorithm algorithm;
WeakHashingOperationInputSink() {
exists(Cryptography::CryptographicOperation operation |
algorithm = operation.getAlgorithm() and
algorithm.isWeak() and
this = operation.getAnInput()
)
}
override string getAlgorithmName() { result = algorithm.getName() }
}
}
/**
* Provides default sources, sinks and sanitizers for detecting
* "use of a broken or weak cryptographic hashing algorithm on sensitive data"
* vulnerabilities on sensitive data that DOES require computationally expensive
* hashing, as well as extension points for adding your own.
*
* Also see the `NormalHashFunction` module.
*/
module ComputationallyExpensiveHashFunction {
/**
* A data flow source of sensitive data that requires computationally expensive
* hashing for "use of a broken or weak cryptographic hashing algorithm on sensitive
* data" vulnerabilities.
*/
abstract class Source extends DataFlow::Node {
/** Gets the classification of the sensitive data. */
abstract string getClassification();
}
/**
* A data flow sink for sensitive data that requires computationally expensive
* hashing for "use of a broken or weak cryptographic hashing algorithm on sensitive
* data" vulnerabilities.
*/
abstract class Sink extends DataFlow::Node {
/**
* Gets the name of the weak hashing algorithm.
*/
abstract string getAlgorithmName();
/**
* Holds if this sink is for a computationally expensive hash function (meaning that
* hash function is just weak in some other regard.
*/
abstract predicate isComputationallyExpensive();
}
/**
* A sanitizer of sensitive data that requires computationally expensive
* hashing for "use of a broken or weak cryptographic hashing
* algorithm on sensitive data" vulnerabilities.
*/
abstract class Sanitizer extends DataFlow::Node { }
/**
* A source of passwords, considered as a flow source.
*/
class PasswordSourceAsSource extends Source instanceof SensitiveDataSources::SensitiveDataSource {
PasswordSourceAsSource() {
this.(SensitiveDataSources::SensitiveDataSource).getClassification() =
SensitiveDataClassification::password()
}
override SensitiveDataClassification getClassification() {
result = SensitiveDataSources::SensitiveDataSource.super.getClassification()
}
}
/**
* The input to a password hashing operation using a weak algorithm, considered as a
* flow sink.
*/
class WeakPasswordHashingOperationInputSink extends Sink {
Cryptography::CryptographicAlgorithm algorithm;
WeakPasswordHashingOperationInputSink() {
(
algorithm instanceof Cryptography::PasswordHashingAlgorithm and
algorithm.isWeak()
or
algorithm instanceof Cryptography::HashingAlgorithm // Note that HashingAlgorithm and PasswordHashingAlgorithm are disjoint
) and
exists(Cryptography::CryptographicOperation operation |
algorithm = operation.getAlgorithm() and
this = operation.getAnInput()
)
}
override string getAlgorithmName() { result = algorithm.getName() }
override predicate isComputationallyExpensive() {
algorithm instanceof Cryptography::PasswordHashingAlgorithm
}
}
}

View File

@@ -0,0 +1,87 @@
/**
* Provides a taint-tracking configuration for detecting use of a broken or weak
* cryptographic hashing algorithm on sensitive data.
*
* Note, for performance reasons: only import this file if
* `WeakSensitiveDataHashing::Configuration` is needed, otherwise
* `WeakSensitiveDataHashingCustomizations` should be imported instead.
*/
private import ruby
private import codeql.ruby.Concepts
private import codeql.ruby.TaintTracking
private import codeql.ruby.dataflow.RemoteFlowSources
private import codeql.ruby.dataflow.BarrierGuards
private import codeql.ruby.security.SensitiveActions
/**
* Provides a taint-tracking configuration for detecting use of a broken or weak
* cryptographic hash function on sensitive data, that does NOT require a
* computationally expensive hash function.
*/
module NormalHashFunction {
import WeakSensitiveDataHashingCustomizations::NormalHashFunction
private module Config implements DataFlow::ConfigSig {
predicate isSource(DataFlow::Node source) { source instanceof Source }
predicate isSink(DataFlow::Node sink) { sink instanceof Sink }
predicate isBarrier(DataFlow::Node node) { node instanceof Sanitizer }
}
/** Global taint-tracking for detecting "use of a broken or weak cryptographic hashing algorithm on sensitive data" vulnerabilities. */
module Flow = TaintTracking::Global<Config>;
}
/**
* Provides a taint-tracking configuration for detecting use of a broken or weak
* cryptographic hashing algorithm on passwords.
*
* Passwords has stricter requirements on the hashing algorithm used (must be
* computationally expensive to prevent brute-force attacks).
*/
module ComputationallyExpensiveHashFunction {
import WeakSensitiveDataHashingCustomizations::ComputationallyExpensiveHashFunction
/**
* Passwords has stricter requirements on the hashing algorithm used (must be
* computationally expensive to prevent brute-force attacks).
*/
private module Config implements DataFlow::ConfigSig {
predicate isSource(DataFlow::Node source) { source instanceof Source }
predicate isSink(DataFlow::Node sink) { sink instanceof Sink }
predicate isBarrier(DataFlow::Node node) { node instanceof Sanitizer }
}
/** Global taint-tracking for detecting "use of a broken or weak cryptographic hashing algorithm on passwords" vulnerabilities. */
module Flow = TaintTracking::Global<Config>;
}
/**
* Global taint-tracking for detecting both variants of "use of a broken or weak
* cryptographic hashing algorithm on sensitive data" vulnerabilities.
*
* See convenience predicates `normalHashFunctionFlowPath` and
* `computationallyExpensiveHashFunctionFlowPath`.
*/
module WeakSensitiveDataHashingFlow =
DataFlow::MergePathGraph<NormalHashFunction::Flow::PathNode,
ComputationallyExpensiveHashFunction::Flow::PathNode, NormalHashFunction::Flow::PathGraph,
ComputationallyExpensiveHashFunction::Flow::PathGraph>;
/** Holds if data can flow from `source` to `sink` with `NormalHashFunction::Flow`. */
predicate normalHashFunctionFlowPath(
WeakSensitiveDataHashingFlow::PathNode source, WeakSensitiveDataHashingFlow::PathNode sink
) {
NormalHashFunction::Flow::flowPath(source.asPathNode1(), sink.asPathNode1())
}
/** Holds if data can flow from `source` to `sink` with `ComputationallyExpensiveHashFunction::Flow`. */
predicate computationallyExpensiveHashFunctionFlowPath(
WeakSensitiveDataHashingFlow::PathNode source, WeakSensitiveDataHashingFlow::PathNode sink
) {
ComputationallyExpensiveHashFunction::Flow::flowPath(source.asPathNode2(), sink.asPathNode2())
}

View File

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

View File

@@ -569,6 +569,13 @@ ruby_do_block_def(
unique int id: @ruby_do_block
);
@ruby_element_reference_block_type = @ruby_block | @ruby_do_block
ruby_element_reference_block(
unique int ruby_element_reference: @ruby_element_reference ref,
unique int block: @ruby_element_reference_block_type ref
);
@ruby_element_reference_child_type = @ruby_block_argument | @ruby_hash_splat_argument | @ruby_pair | @ruby_splat_argument | @ruby_token_forward_argument | @ruby_underscore_expression
#keyset[ruby_element_reference, index]

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,2 @@
description: Add `ruby_element_reference_block` DB relation
compatibility: backwards

View File

@@ -1,3 +1,21 @@
## 1.1.0
### New Queries
* Added a new query, `rb/weak-sensitive-data-hashing`, to detect cases where sensitive data is hashed using a weak cryptographic hashing algorithm.
## 1.0.4
No user-facing changes.
## 1.0.3
No user-facing changes.
## 1.0.2
No user-facing changes.
## 1.0.1
No user-facing changes.

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,5 @@
## 1.1.0
### New Queries
* Added a new query, `rb/weak-sensitive-data-hashing`, to detect cases where sensitive data is hashed using a weak cryptographic hashing algorithm.

View File

@@ -1,2 +1,2 @@
---
lastReleaseVersion: 1.0.1
lastReleaseVersion: 1.1.0

View File

@@ -1,5 +1,5 @@
name: codeql/ruby-queries
version: 1.0.2-dev
version: 1.1.1-dev
groups:
- ruby
- queries

View File

@@ -0,0 +1,104 @@
<!DOCTYPE qhelp PUBLIC
"-//Semmle//qhelp//EN"
"qhelp.dtd">
<qhelp>
<overview>
<p>
Using a broken or weak cryptographic hash function can leave data
vulnerable, and should not be used in security related code.
</p>
<p>
A strong cryptographic hash function should be resistant to:
</p>
<ul>
<li>
pre-image attacks: if you know a hash value <code>h(x)</code>,
you should not be able to easily find the input <code>x</code>.
</li>
<li>
collision attacks: if you know a hash value <code>h(x)</code>,
you should not be able to easily find a different input <code>y</code>
with the same hash value <code>h(x) = h(y)</code>.
</li>
</ul>
<p>
In cases with a limited input space, such as for passwords, the hash
function also needs to be computationally expensive to be resistant to
brute-force attacks. Passwords should also have an unique salt applied
before hashing, but that is not considered by this query.
</p>
<p>
As an example, both MD5 and SHA-1 are known to be vulnerable to collision attacks.
</p>
<p>
Since it's OK to use a weak cryptographic hash function in a non-security
context, this query only alerts when these are used to hash sensitive
data (such as passwords, certificates, usernames).
</p>
<p>
Use of broken or weak cryptographic algorithms that are not hashing algorithms, is
handled by the <code>rb/weak-cryptographic-algorithm</code> query.
</p>
</overview>
<recommendation>
<p>
Ensure that you use a strong, modern cryptographic hash function:
</p>
<ul>
<li>
such as Argon2, scrypt, bcrypt, or PBKDF2 for passwords and other data with limited input space.
</li>
<li>
such as SHA-2, or SHA-3 in other cases.
</li>
</ul>
</recommendation>
<example>
<p>
The following example shows two functions for checking whether the hash
of a certificate matches a known value -- to prevent tampering.
The first function uses MD5 that is known to be vulnerable to collision attacks.
The second function uses SHA-256 that is a strong cryptographic hashing function.
</p>
<sample src="examples/weak_certificate_hashing.rb" />
</example>
<example>
<p>
The following example shows two functions for hashing passwords.
The first function uses SHA-256 to hash passwords. Although SHA-256 is a
strong cryptographic hash function, it is not suitable for password
hashing since it is not computationally expensive.
</p>
<sample src="examples/weak_password_hashing_bad.rb" />
<p>
The second function uses Argon2 (through the <code>argon2</code>
gem), which is a strong password hashing algorithm (and
includes a per-password salt by default).
</p>
<sample src="examples/weak_password_hashing_good.rb" />
</example>
<references>
<li>OWASP: <a href="https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html">Password Storage Cheat Sheet</a></li>
</references>
</qhelp>

View File

@@ -0,0 +1,43 @@
/**
* @name Use of a broken or weak cryptographic hashing algorithm on sensitive data
* @description Using broken or weak cryptographic hashing algorithms can compromise security.
* @kind path-problem
* @problem.severity warning
* @security-severity 7.5
* @precision high
* @id rb/weak-sensitive-data-hashing
* @tags security
* external/cwe/cwe-327
* external/cwe/cwe-328
* external/cwe/cwe-916
*/
import ruby
import codeql.ruby.security.WeakSensitiveDataHashingQuery
import WeakSensitiveDataHashingFlow::PathGraph
from
WeakSensitiveDataHashingFlow::PathNode source, WeakSensitiveDataHashingFlow::PathNode sink,
string ending, string algorithmName, string classification
where
normalHashFunctionFlowPath(source, sink) and
algorithmName = sink.getNode().(NormalHashFunction::Sink).getAlgorithmName() and
classification = source.getNode().(NormalHashFunction::Source).getClassification() and
ending = "."
or
computationallyExpensiveHashFunctionFlowPath(source, sink) and
algorithmName = sink.getNode().(ComputationallyExpensiveHashFunction::Sink).getAlgorithmName() and
classification =
source.getNode().(ComputationallyExpensiveHashFunction::Source).getClassification() and
(
sink.getNode().(ComputationallyExpensiveHashFunction::Sink).isComputationallyExpensive() and
ending = "."
or
not sink.getNode().(ComputationallyExpensiveHashFunction::Sink).isComputationallyExpensive() and
ending =
" for " + classification +
" hashing, since it is not a computationally expensive hash function."
)
select sink.getNode(), source, sink,
"$@ is used in a hashing algorithm (" + algorithmName + ") that is insecure" + ending,
source.getNode(), "Sensitive data (" + classification + ")"

View File

@@ -0,0 +1,11 @@
require 'openssl'
def certificate_matches_known_hash_bad(certificate, known_hash)
hash = OpenSSL::Digest.new('SHA1').digest certificate
hash == known_hash
end
def certificate_matches_known_hash_good(certificate, known_hash)
hash = OpenSSL::Digest.new('SHA256').digest certificate
hash == known_hash
end

View File

@@ -0,0 +1,5 @@
require 'openssl'
def get_password_hash(password, salt)
OpenSSL::Digest.new('SHA256').digest(password + salt) # BAD
end

View File

@@ -0,0 +1,9 @@
require 'argon2'
def get_initial_hash(password)
Argon2::Password.create(password)
end
def check_password(password, known_hash)
Argon2::Password.verify_password(password, known_hash)
end

View File

@@ -1,6 +1,10 @@
testFailures
edges
| blocks.rb:14:12:14:20 | call to source | blocks.rb:8:10:8:14 | yield ... | provenance | |
| blocks.rb:17:10:17:10 | x | blocks.rb:18:11:18:11 | x | provenance | |
| blocks.rb:18:11:18:11 | x | blocks.rb:24:18:24:18 | x | provenance | |
| blocks.rb:24:3:24:11 | call to source | blocks.rb:17:10:17:10 | x | provenance | |
| blocks.rb:24:18:24:18 | x | blocks.rb:25:8:25:8 | x | provenance | |
| captured_variables.rb:9:24:9:24 | x | captured_variables.rb:11:5:11:6 | fn [captured x] | provenance | |
| captured_variables.rb:11:5:11:6 | fn [captured x] | captured_variables.rb:10:20:10:20 | x | provenance | |
| captured_variables.rb:13:20:13:29 | call to taint | captured_variables.rb:9:24:9:24 | x | provenance | |
@@ -254,6 +258,11 @@ edges
nodes
| blocks.rb:8:10:8:14 | yield ... | semmle.label | yield ... |
| blocks.rb:14:12:14:20 | call to source | semmle.label | call to source |
| blocks.rb:17:10:17:10 | x | semmle.label | x |
| blocks.rb:18:11:18:11 | x | semmle.label | x |
| blocks.rb:24:3:24:11 | call to source | semmle.label | call to source |
| blocks.rb:24:18:24:18 | x | semmle.label | x |
| blocks.rb:25:8:25:8 | x | semmle.label | x |
| captured_variables.rb:9:24:9:24 | x | semmle.label | x |
| captured_variables.rb:10:20:10:20 | x | semmle.label | x |
| captured_variables.rb:11:5:11:6 | fn [captured x] | semmle.label | fn [captured x] |
@@ -554,6 +563,7 @@ subpaths
| instance_variables.rb:120:6:120:10 | foo16 [@field] | instance_variables.rb:13:5:15:7 | self in get_field [@field] | instance_variables.rb:14:9:14:21 | return | instance_variables.rb:120:6:120:20 | call to get_field |
#select
| blocks.rb:8:10:8:14 | yield ... | blocks.rb:14:12:14:20 | call to source | blocks.rb:8:10:8:14 | yield ... | $@ | blocks.rb:14:12:14:20 | call to source | call to source |
| blocks.rb:25:8:25:8 | x | blocks.rb:24:3:24:11 | call to source | blocks.rb:25:8:25:8 | x | $@ | blocks.rb:24:3:24:11 | call to source | call to source |
| captured_variables.rb:10:20:10:20 | x | captured_variables.rb:13:20:13:29 | call to taint | captured_variables.rb:10:20:10:20 | x | $@ | captured_variables.rb:13:20:13:29 | call to taint | call to taint |
| captured_variables.rb:17:14:17:14 | x | captured_variables.rb:20:25:20:34 | call to taint | captured_variables.rb:17:14:17:14 | x | $@ | captured_variables.rb:20:25:20:34 | call to taint | call to taint |
| captured_variables.rb:24:14:24:14 | x | captured_variables.rb:27:48:27:57 | call to taint | captured_variables.rb:24:14:24:14 | x | $@ | captured_variables.rb:27:48:27:57 | call to taint | call to taint |

View File

@@ -12,3 +12,15 @@ end
A.new.m1 { source(1) }
A.new.m2 { source(2) }
class B
def [](x)
yield x
end
end
b = B.new
b[source(3)] do |x|
sink x # $ hasValueFlow=3
end

File diff suppressed because it is too large Load Diff

View File

@@ -10,14 +10,14 @@ edges
| summaries.rb:1:11:1:36 | call to identity | summaries.rb:26:31:26:37 | tainted | provenance | |
| summaries.rb:1:11:1:36 | call to identity | summaries.rb:30:24:30:30 | tainted | provenance | |
| summaries.rb:1:11:1:36 | call to identity | summaries.rb:31:27:31:33 | tainted | provenance | |
| summaries.rb:1:11:1:36 | call to identity | summaries.rb:34:16:34:22 | tainted | provenance | Sink:MaD:6 |
| summaries.rb:1:11:1:36 | call to identity | summaries.rb:34:16:34:22 | tainted | provenance | Sink:MaD:6 |
| summaries.rb:1:11:1:36 | call to identity | summaries.rb:35:16:35:22 | tainted | provenance | Sink:MaD:6 |
| summaries.rb:1:11:1:36 | call to identity | summaries.rb:35:16:35:22 | tainted | provenance | Sink:MaD:6 |
| summaries.rb:1:11:1:36 | call to identity | summaries.rb:36:21:36:27 | tainted | provenance | Sink:MaD:6 |
| summaries.rb:1:11:1:36 | call to identity | summaries.rb:36:21:36:27 | tainted | provenance | Sink:MaD:6 |
| summaries.rb:1:11:1:36 | call to identity | summaries.rb:37:36:37:42 | tainted | provenance | Sink:MaD:6 |
| summaries.rb:1:11:1:36 | call to identity | summaries.rb:37:36:37:42 | tainted | provenance | Sink:MaD:6 |
| summaries.rb:1:11:1:36 | call to identity | summaries.rb:34:16:34:22 | tainted | provenance | Sink:MaD:68 |
| summaries.rb:1:11:1:36 | call to identity | summaries.rb:34:16:34:22 | tainted | provenance | Sink:MaD:68 |
| summaries.rb:1:11:1:36 | call to identity | summaries.rb:35:16:35:22 | tainted | provenance | Sink:MaD:68 |
| summaries.rb:1:11:1:36 | call to identity | summaries.rb:35:16:35:22 | tainted | provenance | Sink:MaD:68 |
| summaries.rb:1:11:1:36 | call to identity | summaries.rb:36:21:36:27 | tainted | provenance | Sink:MaD:68 |
| summaries.rb:1:11:1:36 | call to identity | summaries.rb:36:21:36:27 | tainted | provenance | Sink:MaD:68 |
| summaries.rb:1:11:1:36 | call to identity | summaries.rb:37:36:37:42 | tainted | provenance | Sink:MaD:68 |
| summaries.rb:1:11:1:36 | call to identity | summaries.rb:37:36:37:42 | tainted | provenance | Sink:MaD:68 |
| summaries.rb:1:11:1:36 | call to identity | summaries.rb:51:24:51:30 | tainted | provenance | |
| summaries.rb:1:11:1:36 | call to identity | summaries.rb:56:22:56:28 | tainted | provenance | |
| summaries.rb:1:11:1:36 | call to identity | summaries.rb:57:17:57:23 | tainted | provenance | |
@@ -27,30 +27,30 @@ edges
| summaries.rb:1:11:1:36 | call to identity | summaries.rb:122:16:122:22 | tainted | provenance | |
| summaries.rb:1:11:1:36 | call to identity | summaries.rb:128:14:128:20 | tainted | provenance | |
| summaries.rb:1:11:1:36 | call to identity | summaries.rb:131:16:131:22 | tainted | provenance | |
| summaries.rb:1:11:1:36 | call to identity | summaries.rb:131:16:131:22 | tainted | provenance | Sink:MaD:3 |
| summaries.rb:1:11:1:36 | call to identity | summaries.rb:131:16:131:22 | tainted | provenance | Sink:MaD:3 |
| summaries.rb:1:11:1:36 | call to identity | summaries.rb:132:21:132:27 | tainted | provenance | Sink:MaD:3 |
| summaries.rb:1:11:1:36 | call to identity | summaries.rb:132:21:132:27 | tainted | provenance | Sink:MaD:3 |
| summaries.rb:1:11:1:36 | call to identity | summaries.rb:135:26:135:32 | tainted | provenance | Sink:MaD:4 |
| summaries.rb:1:11:1:36 | call to identity | summaries.rb:135:26:135:32 | tainted | provenance | Sink:MaD:4 |
| summaries.rb:1:11:1:36 | call to identity | summaries.rb:137:23:137:29 | tainted | provenance | Sink:MaD:6 |
| summaries.rb:1:11:1:36 | call to identity | summaries.rb:137:23:137:29 | tainted | provenance | Sink:MaD:6 |
| summaries.rb:1:11:1:36 | call to identity | summaries.rb:140:19:140:25 | tainted | provenance | Sink:MaD:6 |
| summaries.rb:1:11:1:36 | call to identity | summaries.rb:140:19:140:25 | tainted | provenance | Sink:MaD:6 |
| summaries.rb:1:11:1:36 | call to identity | summaries.rb:141:19:141:25 | tainted | provenance | Sink:MaD:6 |
| summaries.rb:1:11:1:36 | call to identity | summaries.rb:141:19:141:25 | tainted | provenance | Sink:MaD:6 |
| summaries.rb:1:11:1:36 | call to identity | summaries.rb:145:26:145:32 | tainted | provenance | Sink:MaD:1 |
| summaries.rb:1:11:1:36 | call to identity | summaries.rb:145:26:145:32 | tainted | provenance | Sink:MaD:1 |
| summaries.rb:1:11:1:36 | call to identity | summaries.rb:147:16:147:22 | tainted | provenance | Sink:MaD:0 |
| summaries.rb:1:11:1:36 | call to identity | summaries.rb:147:16:147:22 | tainted | provenance | Sink:MaD:0 |
| summaries.rb:1:11:1:36 | call to identity | summaries.rb:150:39:150:45 | tainted | provenance | Sink:MaD:2 |
| summaries.rb:1:11:1:36 | call to identity | summaries.rb:150:39:150:45 | tainted | provenance | Sink:MaD:2 |
| summaries.rb:1:11:1:36 | call to identity | summaries.rb:154:20:154:26 | tainted | provenance | Sink:MaD:5 |
| summaries.rb:1:11:1:36 | call to identity | summaries.rb:154:20:154:26 | tainted | provenance | Sink:MaD:5 |
| summaries.rb:1:11:1:36 | call to identity | summaries.rb:155:28:155:34 | tainted | provenance | Sink:MaD:5 |
| summaries.rb:1:11:1:36 | call to identity | summaries.rb:155:28:155:34 | tainted | provenance | Sink:MaD:5 |
| summaries.rb:1:11:1:36 | call to identity | summaries.rb:156:27:156:33 | tainted | provenance | Sink:MaD:5 |
| summaries.rb:1:11:1:36 | call to identity | summaries.rb:156:27:156:33 | tainted | provenance | Sink:MaD:5 |
| summaries.rb:1:11:1:36 | call to identity | summaries.rb:131:16:131:22 | tainted | provenance | Sink:MaD:65 |
| summaries.rb:1:11:1:36 | call to identity | summaries.rb:131:16:131:22 | tainted | provenance | Sink:MaD:65 |
| summaries.rb:1:11:1:36 | call to identity | summaries.rb:132:21:132:27 | tainted | provenance | Sink:MaD:65 |
| summaries.rb:1:11:1:36 | call to identity | summaries.rb:132:21:132:27 | tainted | provenance | Sink:MaD:65 |
| summaries.rb:1:11:1:36 | call to identity | summaries.rb:135:26:135:32 | tainted | provenance | Sink:MaD:66 |
| summaries.rb:1:11:1:36 | call to identity | summaries.rb:135:26:135:32 | tainted | provenance | Sink:MaD:66 |
| summaries.rb:1:11:1:36 | call to identity | summaries.rb:137:23:137:29 | tainted | provenance | Sink:MaD:68 |
| summaries.rb:1:11:1:36 | call to identity | summaries.rb:137:23:137:29 | tainted | provenance | Sink:MaD:68 |
| summaries.rb:1:11:1:36 | call to identity | summaries.rb:140:19:140:25 | tainted | provenance | Sink:MaD:68 |
| summaries.rb:1:11:1:36 | call to identity | summaries.rb:140:19:140:25 | tainted | provenance | Sink:MaD:68 |
| summaries.rb:1:11:1:36 | call to identity | summaries.rb:141:19:141:25 | tainted | provenance | Sink:MaD:68 |
| summaries.rb:1:11:1:36 | call to identity | summaries.rb:141:19:141:25 | tainted | provenance | Sink:MaD:68 |
| summaries.rb:1:11:1:36 | call to identity | summaries.rb:145:26:145:32 | tainted | provenance | Sink:MaD:63 |
| summaries.rb:1:11:1:36 | call to identity | summaries.rb:145:26:145:32 | tainted | provenance | Sink:MaD:63 |
| summaries.rb:1:11:1:36 | call to identity | summaries.rb:147:16:147:22 | tainted | provenance | Sink:MaD:62 |
| summaries.rb:1:11:1:36 | call to identity | summaries.rb:147:16:147:22 | tainted | provenance | Sink:MaD:62 |
| summaries.rb:1:11:1:36 | call to identity | summaries.rb:150:39:150:45 | tainted | provenance | Sink:MaD:64 |
| summaries.rb:1:11:1:36 | call to identity | summaries.rb:150:39:150:45 | tainted | provenance | Sink:MaD:64 |
| summaries.rb:1:11:1:36 | call to identity | summaries.rb:154:20:154:26 | tainted | provenance | Sink:MaD:67 |
| summaries.rb:1:11:1:36 | call to identity | summaries.rb:154:20:154:26 | tainted | provenance | Sink:MaD:67 |
| summaries.rb:1:11:1:36 | call to identity | summaries.rb:155:28:155:34 | tainted | provenance | Sink:MaD:67 |
| summaries.rb:1:11:1:36 | call to identity | summaries.rb:155:28:155:34 | tainted | provenance | Sink:MaD:67 |
| summaries.rb:1:11:1:36 | call to identity | summaries.rb:156:27:156:33 | tainted | provenance | Sink:MaD:67 |
| summaries.rb:1:11:1:36 | call to identity | summaries.rb:156:27:156:33 | tainted | provenance | Sink:MaD:67 |
| summaries.rb:1:11:1:36 | call to identity | summaries.rb:157:14:160:3 | do ... end [captured tainted] | provenance | |
| summaries.rb:1:11:1:36 | call to identity | summaries.rb:157:14:160:3 | do ... end [captured tainted] | provenance | |
| summaries.rb:1:20:1:36 | call to source | summaries.rb:1:11:1:36 | call to identity | provenance | |
@@ -81,34 +81,34 @@ edges
| summaries.rb:16:36:16:42 | tainted | summaries.rb:16:12:16:43 | call to apply_lambda | provenance | |
| summaries.rb:20:1:20:8 | tainted4 | summaries.rb:21:6:21:13 | tainted4 | provenance | |
| summaries.rb:20:12:20:32 | call to firstArg | summaries.rb:20:1:20:8 | tainted4 | provenance | |
| summaries.rb:20:25:20:31 | tainted | summaries.rb:20:12:20:32 | call to firstArg | provenance | MaD:11 |
| summaries.rb:20:25:20:31 | tainted | summaries.rb:20:12:20:32 | call to firstArg | provenance | MaD:73 |
| summaries.rb:26:1:26:8 | tainted5 | summaries.rb:27:6:27:13 | tainted5 | provenance | |
| summaries.rb:26:12:26:38 | call to secondArg | summaries.rb:26:1:26:8 | tainted5 | provenance | |
| summaries.rb:26:31:26:37 | tainted | summaries.rb:26:12:26:38 | call to secondArg | provenance | MaD:17 |
| summaries.rb:30:24:30:30 | tainted | summaries.rb:30:6:30:42 | call to onlyWithBlock | provenance | MaD:15 |
| summaries.rb:31:27:31:33 | tainted | summaries.rb:31:6:31:34 | call to onlyWithoutBlock | provenance | MaD:16 |
| summaries.rb:26:31:26:37 | tainted | summaries.rb:26:12:26:38 | call to secondArg | provenance | MaD:79 |
| summaries.rb:30:24:30:30 | tainted | summaries.rb:30:6:30:42 | call to onlyWithBlock | provenance | MaD:77 |
| summaries.rb:31:27:31:33 | tainted | summaries.rb:31:6:31:34 | call to onlyWithoutBlock | provenance | MaD:78 |
| summaries.rb:40:3:40:3 | t | summaries.rb:41:24:41:24 | t | provenance | |
| summaries.rb:40:3:40:3 | t | summaries.rb:42:24:42:24 | t | provenance | |
| summaries.rb:40:3:40:3 | t | summaries.rb:44:8:44:8 | t | provenance | |
| summaries.rb:40:7:40:17 | call to source | summaries.rb:40:3:40:3 | t | provenance | |
| summaries.rb:41:24:41:24 | t | summaries.rb:41:8:41:25 | call to matchedByName | provenance | MaD:24 |
| summaries.rb:42:24:42:24 | t | summaries.rb:42:8:42:25 | call to matchedByName | provenance | MaD:24 |
| summaries.rb:44:8:44:8 | t | summaries.rb:44:8:44:27 | call to matchedByNameRcv | provenance | MaD:23 |
| summaries.rb:48:24:48:41 | call to source | summaries.rb:48:8:48:42 | call to preserveTaint | provenance | MaD:10 |
| summaries.rb:51:24:51:30 | tainted | summaries.rb:51:6:51:31 | call to namedArg | provenance | MaD:14 |
| summaries.rb:41:24:41:24 | t | summaries.rb:41:8:41:25 | call to matchedByName | provenance | MaD:86 |
| summaries.rb:42:24:42:24 | t | summaries.rb:42:8:42:25 | call to matchedByName | provenance | MaD:86 |
| summaries.rb:44:8:44:8 | t | summaries.rb:44:8:44:27 | call to matchedByNameRcv | provenance | MaD:85 |
| summaries.rb:48:24:48:41 | call to source | summaries.rb:48:8:48:42 | call to preserveTaint | provenance | MaD:72 |
| summaries.rb:51:24:51:30 | tainted | summaries.rb:51:6:51:31 | call to namedArg | provenance | MaD:76 |
| summaries.rb:53:1:53:4 | args [element :foo] | summaries.rb:54:21:54:24 | args [element :foo] | provenance | |
| summaries.rb:53:8:53:33 | call to [] [element :foo] | summaries.rb:53:1:53:4 | args [element :foo] | provenance | |
| summaries.rb:53:15:53:31 | call to source | summaries.rb:53:8:53:33 | call to [] [element :foo] | provenance | |
| summaries.rb:54:19:54:24 | ** ... [element :foo] | summaries.rb:54:6:54:25 | call to namedArg | provenance | MaD:14 |
| summaries.rb:54:19:54:24 | ** ... [element :foo] | summaries.rb:54:6:54:25 | call to namedArg | provenance | MaD:76 |
| summaries.rb:54:21:54:24 | args [element :foo] | summaries.rb:54:19:54:24 | ** ... [element :foo] | provenance | |
| summaries.rb:56:22:56:28 | tainted | summaries.rb:56:6:56:29 | call to anyArg | provenance | MaD:7 |
| summaries.rb:57:17:57:23 | tainted | summaries.rb:57:6:57:24 | call to anyArg | provenance | MaD:7 |
| summaries.rb:59:27:59:33 | tainted | summaries.rb:59:6:59:34 | call to anyNamedArg | provenance | MaD:8 |
| summaries.rb:63:32:63:38 | tainted | summaries.rb:63:6:63:39 | call to anyPositionFromOne | provenance | MaD:9 |
| summaries.rb:65:23:65:29 | tainted | summaries.rb:65:40:65:40 | x | provenance | MaD:12 |
| summaries.rb:56:22:56:28 | tainted | summaries.rb:56:6:56:29 | call to anyArg | provenance | MaD:69 |
| summaries.rb:57:17:57:23 | tainted | summaries.rb:57:6:57:24 | call to anyArg | provenance | MaD:69 |
| summaries.rb:59:27:59:33 | tainted | summaries.rb:59:6:59:34 | call to anyNamedArg | provenance | MaD:70 |
| summaries.rb:63:32:63:38 | tainted | summaries.rb:63:6:63:39 | call to anyPositionFromOne | provenance | MaD:71 |
| summaries.rb:65:23:65:29 | tainted | summaries.rb:65:40:65:40 | x | provenance | MaD:74 |
| summaries.rb:65:40:65:40 | x | summaries.rb:66:8:66:8 | x | provenance | |
| summaries.rb:73:24:73:53 | call to source | summaries.rb:73:8:73:54 | call to preserveTaint | provenance | MaD:18 |
| summaries.rb:76:26:76:56 | call to source | summaries.rb:76:8:76:57 | call to preserveTaint | provenance | MaD:19 |
| summaries.rb:73:24:73:53 | call to source | summaries.rb:73:8:73:54 | call to preserveTaint | provenance | MaD:80 |
| summaries.rb:76:26:76:56 | call to source | summaries.rb:76:8:76:57 | call to preserveTaint | provenance | MaD:81 |
| summaries.rb:79:1:79:1 | a [element 1] | summaries.rb:82:6:82:6 | a [element 1] | provenance | |
| summaries.rb:79:1:79:1 | a [element 1] | summaries.rb:82:6:82:6 | a [element 1] | provenance | |
| summaries.rb:79:1:79:1 | a [element 1] | summaries.rb:83:6:83:6 | a [element 1] | provenance | |
@@ -145,12 +145,12 @@ edges
| summaries.rb:81:1:81:1 | [post] a [element] | summaries.rb:95:1:95:1 | a [element] | provenance | |
| summaries.rb:81:13:81:27 | call to source | summaries.rb:81:1:81:1 | [post] a [element] | provenance | |
| summaries.rb:81:13:81:27 | call to source | summaries.rb:81:1:81:1 | [post] a [element] | provenance | |
| summaries.rb:82:6:82:6 | a [element 1] | summaries.rb:82:6:82:24 | call to readElementOne | provenance | MaD:25 |
| summaries.rb:82:6:82:6 | a [element 1] | summaries.rb:82:6:82:24 | call to readElementOne | provenance | MaD:25 |
| summaries.rb:82:6:82:6 | a [element] | summaries.rb:82:6:82:24 | call to readElementOne | provenance | MaD:25 |
| summaries.rb:82:6:82:6 | a [element] | summaries.rb:82:6:82:24 | call to readElementOne | provenance | MaD:25 |
| summaries.rb:83:6:83:6 | a [element 1] | summaries.rb:83:6:83:31 | call to readExactlyElementOne | provenance | MaD:26 |
| summaries.rb:83:6:83:6 | a [element 1] | summaries.rb:83:6:83:31 | call to readExactlyElementOne | provenance | MaD:26 |
| summaries.rb:82:6:82:6 | a [element 1] | summaries.rb:82:6:82:24 | call to readElementOne | provenance | MaD:87 |
| summaries.rb:82:6:82:6 | a [element 1] | summaries.rb:82:6:82:24 | call to readElementOne | provenance | MaD:87 |
| summaries.rb:82:6:82:6 | a [element] | summaries.rb:82:6:82:24 | call to readElementOne | provenance | MaD:87 |
| summaries.rb:82:6:82:6 | a [element] | summaries.rb:82:6:82:24 | call to readElementOne | provenance | MaD:87 |
| summaries.rb:83:6:83:6 | a [element 1] | summaries.rb:83:6:83:31 | call to readExactlyElementOne | provenance | MaD:88 |
| summaries.rb:83:6:83:6 | a [element 1] | summaries.rb:83:6:83:31 | call to readExactlyElementOne | provenance | MaD:88 |
| summaries.rb:84:6:84:6 | a [element] | summaries.rb:84:6:84:9 | ...[...] | provenance | |
| summaries.rb:84:6:84:6 | a [element] | summaries.rb:84:6:84:9 | ...[...] | provenance | |
| summaries.rb:85:6:85:6 | a [element 1] | summaries.rb:85:6:85:9 | ...[...] | provenance | |
@@ -169,10 +169,10 @@ edges
| summaries.rb:87:1:87:1 | b [element] | summaries.rb:89:6:89:6 | b [element] | provenance | |
| summaries.rb:87:1:87:1 | b [element] | summaries.rb:90:6:90:6 | b [element] | provenance | |
| summaries.rb:87:1:87:1 | b [element] | summaries.rb:90:6:90:6 | b [element] | provenance | |
| summaries.rb:87:5:87:5 | a [element 1] | summaries.rb:87:5:87:22 | call to withElementOne [element 1] | provenance | MaD:28 |
| summaries.rb:87:5:87:5 | a [element 1] | summaries.rb:87:5:87:22 | call to withElementOne [element 1] | provenance | MaD:28 |
| summaries.rb:87:5:87:5 | a [element] | summaries.rb:87:5:87:22 | call to withElementOne [element] | provenance | MaD:28 |
| summaries.rb:87:5:87:5 | a [element] | summaries.rb:87:5:87:22 | call to withElementOne [element] | provenance | MaD:28 |
| summaries.rb:87:5:87:5 | a [element 1] | summaries.rb:87:5:87:22 | call to withElementOne [element 1] | provenance | MaD:90 |
| summaries.rb:87:5:87:5 | a [element 1] | summaries.rb:87:5:87:22 | call to withElementOne [element 1] | provenance | MaD:90 |
| summaries.rb:87:5:87:5 | a [element] | summaries.rb:87:5:87:22 | call to withElementOne [element] | provenance | MaD:90 |
| summaries.rb:87:5:87:5 | a [element] | summaries.rb:87:5:87:22 | call to withElementOne [element] | provenance | MaD:90 |
| summaries.rb:87:5:87:22 | call to withElementOne [element 1] | summaries.rb:87:1:87:1 | b [element 1] | provenance | |
| summaries.rb:87:5:87:22 | call to withElementOne [element 1] | summaries.rb:87:1:87:1 | b [element 1] | provenance | |
| summaries.rb:87:5:87:22 | call to withElementOne [element] | summaries.rb:87:1:87:1 | b [element] | provenance | |
@@ -187,8 +187,8 @@ edges
| summaries.rb:90:6:90:6 | b [element] | summaries.rb:90:6:90:9 | ...[...] | provenance | |
| summaries.rb:91:1:91:1 | c [element 1] | summaries.rb:93:6:93:6 | c [element 1] | provenance | |
| summaries.rb:91:1:91:1 | c [element 1] | summaries.rb:93:6:93:6 | c [element 1] | provenance | |
| summaries.rb:91:5:91:5 | a [element 1] | summaries.rb:91:5:91:29 | call to withExactlyElementOne [element 1] | provenance | MaD:29 |
| summaries.rb:91:5:91:5 | a [element 1] | summaries.rb:91:5:91:29 | call to withExactlyElementOne [element 1] | provenance | MaD:29 |
| summaries.rb:91:5:91:5 | a [element 1] | summaries.rb:91:5:91:29 | call to withExactlyElementOne [element 1] | provenance | MaD:91 |
| summaries.rb:91:5:91:5 | a [element 1] | summaries.rb:91:5:91:29 | call to withExactlyElementOne [element 1] | provenance | MaD:91 |
| summaries.rb:91:5:91:29 | call to withExactlyElementOne [element 1] | summaries.rb:91:1:91:1 | c [element 1] | provenance | |
| summaries.rb:91:5:91:29 | call to withExactlyElementOne [element 1] | summaries.rb:91:1:91:1 | c [element 1] | provenance | |
| summaries.rb:93:6:93:6 | c [element 1] | summaries.rb:93:6:93:9 | ...[...] | provenance | |
@@ -203,10 +203,10 @@ edges
| summaries.rb:95:1:95:1 | [post] a [element] | summaries.rb:97:6:97:6 | a [element] | provenance | |
| summaries.rb:95:1:95:1 | [post] a [element] | summaries.rb:98:6:98:6 | a [element] | provenance | |
| summaries.rb:95:1:95:1 | [post] a [element] | summaries.rb:98:6:98:6 | a [element] | provenance | |
| summaries.rb:95:1:95:1 | a [element 2] | summaries.rb:95:1:95:1 | [post] a [element 2] | provenance | MaD:32 |
| summaries.rb:95:1:95:1 | a [element 2] | summaries.rb:95:1:95:1 | [post] a [element 2] | provenance | MaD:32 |
| summaries.rb:95:1:95:1 | a [element] | summaries.rb:95:1:95:1 | [post] a [element] | provenance | MaD:32 |
| summaries.rb:95:1:95:1 | a [element] | summaries.rb:95:1:95:1 | [post] a [element] | provenance | MaD:32 |
| summaries.rb:95:1:95:1 | a [element 2] | summaries.rb:95:1:95:1 | [post] a [element 2] | provenance | MaD:94 |
| summaries.rb:95:1:95:1 | a [element 2] | summaries.rb:95:1:95:1 | [post] a [element 2] | provenance | MaD:94 |
| summaries.rb:95:1:95:1 | a [element] | summaries.rb:95:1:95:1 | [post] a [element] | provenance | MaD:94 |
| summaries.rb:95:1:95:1 | a [element] | summaries.rb:95:1:95:1 | [post] a [element] | provenance | MaD:94 |
| summaries.rb:96:6:96:6 | a [element] | summaries.rb:96:6:96:9 | ...[...] | provenance | |
| summaries.rb:96:6:96:6 | a [element] | summaries.rb:96:6:96:9 | ...[...] | provenance | |
| summaries.rb:97:6:97:6 | a [element] | summaries.rb:97:6:97:9 | ...[...] | provenance | |
@@ -217,8 +217,8 @@ edges
| summaries.rb:98:6:98:6 | a [element] | summaries.rb:98:6:98:9 | ...[...] | provenance | |
| summaries.rb:99:1:99:1 | [post] a [element 2] | summaries.rb:102:6:102:6 | a [element 2] | provenance | |
| summaries.rb:99:1:99:1 | [post] a [element 2] | summaries.rb:102:6:102:6 | a [element 2] | provenance | |
| summaries.rb:99:1:99:1 | a [element 2] | summaries.rb:99:1:99:1 | [post] a [element 2] | provenance | MaD:31 |
| summaries.rb:99:1:99:1 | a [element 2] | summaries.rb:99:1:99:1 | [post] a [element 2] | provenance | MaD:31 |
| summaries.rb:99:1:99:1 | a [element 2] | summaries.rb:99:1:99:1 | [post] a [element 2] | provenance | MaD:93 |
| summaries.rb:99:1:99:1 | a [element 2] | summaries.rb:99:1:99:1 | [post] a [element 2] | provenance | MaD:93 |
| summaries.rb:102:6:102:6 | a [element 2] | summaries.rb:102:6:102:9 | ...[...] | provenance | |
| summaries.rb:102:6:102:6 | a [element 2] | summaries.rb:102:6:102:9 | ...[...] | provenance | |
| summaries.rb:103:1:103:1 | [post] d [element 3] | summaries.rb:104:1:104:1 | d [element 3] | provenance | |
@@ -227,39 +227,39 @@ edges
| summaries.rb:103:8:103:22 | call to source | summaries.rb:103:1:103:1 | [post] d [element 3] | provenance | |
| summaries.rb:104:1:104:1 | [post] d [element 3] | summaries.rb:108:6:108:6 | d [element 3] | provenance | |
| summaries.rb:104:1:104:1 | [post] d [element 3] | summaries.rb:108:6:108:6 | d [element 3] | provenance | |
| summaries.rb:104:1:104:1 | d [element 3] | summaries.rb:104:1:104:1 | [post] d [element 3] | provenance | MaD:30 |
| summaries.rb:104:1:104:1 | d [element 3] | summaries.rb:104:1:104:1 | [post] d [element 3] | provenance | MaD:30 |
| summaries.rb:104:1:104:1 | d [element 3] | summaries.rb:104:1:104:1 | [post] d [element 3] | provenance | MaD:92 |
| summaries.rb:104:1:104:1 | d [element 3] | summaries.rb:104:1:104:1 | [post] d [element 3] | provenance | MaD:92 |
| summaries.rb:108:6:108:6 | d [element 3] | summaries.rb:108:6:108:9 | ...[...] | provenance | |
| summaries.rb:108:6:108:6 | d [element 3] | summaries.rb:108:6:108:9 | ...[...] | provenance | |
| summaries.rb:111:1:111:1 | [post] x [@value] | summaries.rb:112:6:112:6 | x [@value] | provenance | |
| summaries.rb:111:1:111:1 | [post] x [@value] | summaries.rb:112:6:112:6 | x [@value] | provenance | |
| summaries.rb:111:13:111:26 | call to source | summaries.rb:111:1:111:1 | [post] x [@value] | provenance | MaD:27 |
| summaries.rb:111:13:111:26 | call to source | summaries.rb:111:1:111:1 | [post] x [@value] | provenance | MaD:27 |
| summaries.rb:112:6:112:6 | x [@value] | summaries.rb:112:6:112:16 | call to get_value | provenance | MaD:22 |
| summaries.rb:112:6:112:6 | x [@value] | summaries.rb:112:6:112:16 | call to get_value | provenance | MaD:22 |
| summaries.rb:111:13:111:26 | call to source | summaries.rb:111:1:111:1 | [post] x [@value] | provenance | MaD:89 |
| summaries.rb:111:13:111:26 | call to source | summaries.rb:111:1:111:1 | [post] x [@value] | provenance | MaD:89 |
| summaries.rb:112:6:112:6 | x [@value] | summaries.rb:112:6:112:16 | call to get_value | provenance | MaD:84 |
| summaries.rb:112:6:112:6 | x [@value] | summaries.rb:112:6:112:16 | call to get_value | provenance | MaD:84 |
| summaries.rb:122:16:122:22 | [post] tainted | summaries.rb:128:14:128:20 | tainted | provenance | |
| summaries.rb:122:16:122:22 | [post] tainted | summaries.rb:131:16:131:22 | tainted | provenance | |
| summaries.rb:122:16:122:22 | [post] tainted | summaries.rb:131:16:131:22 | tainted | provenance | Sink:MaD:3 |
| summaries.rb:122:16:122:22 | [post] tainted | summaries.rb:132:21:132:27 | tainted | provenance | Sink:MaD:3 |
| summaries.rb:122:16:122:22 | [post] tainted | summaries.rb:135:26:135:32 | tainted | provenance | Sink:MaD:4 |
| summaries.rb:122:16:122:22 | [post] tainted | summaries.rb:137:23:137:29 | tainted | provenance | Sink:MaD:6 |
| summaries.rb:122:16:122:22 | [post] tainted | summaries.rb:140:19:140:25 | tainted | provenance | Sink:MaD:6 |
| summaries.rb:122:16:122:22 | [post] tainted | summaries.rb:141:19:141:25 | tainted | provenance | Sink:MaD:6 |
| summaries.rb:122:16:122:22 | [post] tainted | summaries.rb:145:26:145:32 | tainted | provenance | Sink:MaD:1 |
| summaries.rb:122:16:122:22 | [post] tainted | summaries.rb:147:16:147:22 | tainted | provenance | Sink:MaD:0 |
| summaries.rb:122:16:122:22 | [post] tainted | summaries.rb:150:39:150:45 | tainted | provenance | Sink:MaD:2 |
| summaries.rb:122:16:122:22 | [post] tainted | summaries.rb:154:20:154:26 | tainted | provenance | Sink:MaD:5 |
| summaries.rb:122:16:122:22 | [post] tainted | summaries.rb:155:28:155:34 | tainted | provenance | Sink:MaD:5 |
| summaries.rb:122:16:122:22 | [post] tainted | summaries.rb:156:27:156:33 | tainted | provenance | Sink:MaD:5 |
| summaries.rb:122:16:122:22 | [post] tainted | summaries.rb:131:16:131:22 | tainted | provenance | Sink:MaD:65 |
| summaries.rb:122:16:122:22 | [post] tainted | summaries.rb:132:21:132:27 | tainted | provenance | Sink:MaD:65 |
| summaries.rb:122:16:122:22 | [post] tainted | summaries.rb:135:26:135:32 | tainted | provenance | Sink:MaD:66 |
| summaries.rb:122:16:122:22 | [post] tainted | summaries.rb:137:23:137:29 | tainted | provenance | Sink:MaD:68 |
| summaries.rb:122:16:122:22 | [post] tainted | summaries.rb:140:19:140:25 | tainted | provenance | Sink:MaD:68 |
| summaries.rb:122:16:122:22 | [post] tainted | summaries.rb:141:19:141:25 | tainted | provenance | Sink:MaD:68 |
| summaries.rb:122:16:122:22 | [post] tainted | summaries.rb:145:26:145:32 | tainted | provenance | Sink:MaD:63 |
| summaries.rb:122:16:122:22 | [post] tainted | summaries.rb:147:16:147:22 | tainted | provenance | Sink:MaD:62 |
| summaries.rb:122:16:122:22 | [post] tainted | summaries.rb:150:39:150:45 | tainted | provenance | Sink:MaD:64 |
| summaries.rb:122:16:122:22 | [post] tainted | summaries.rb:154:20:154:26 | tainted | provenance | Sink:MaD:67 |
| summaries.rb:122:16:122:22 | [post] tainted | summaries.rb:155:28:155:34 | tainted | provenance | Sink:MaD:67 |
| summaries.rb:122:16:122:22 | [post] tainted | summaries.rb:156:27:156:33 | tainted | provenance | Sink:MaD:67 |
| summaries.rb:122:16:122:22 | [post] tainted | summaries.rb:157:14:160:3 | do ... end [captured tainted] | provenance | |
| summaries.rb:122:16:122:22 | tainted | summaries.rb:122:16:122:22 | [post] tainted | provenance | MaD:20 |
| summaries.rb:122:16:122:22 | tainted | summaries.rb:122:25:122:25 | [post] y | provenance | MaD:20 |
| summaries.rb:122:16:122:22 | tainted | summaries.rb:122:33:122:33 | [post] z | provenance | MaD:20 |
| summaries.rb:122:16:122:22 | tainted | summaries.rb:122:16:122:22 | [post] tainted | provenance | MaD:82 |
| summaries.rb:122:16:122:22 | tainted | summaries.rb:122:25:122:25 | [post] y | provenance | MaD:82 |
| summaries.rb:122:16:122:22 | tainted | summaries.rb:122:33:122:33 | [post] z | provenance | MaD:82 |
| summaries.rb:122:25:122:25 | [post] y | summaries.rb:124:6:124:6 | y | provenance | |
| summaries.rb:122:33:122:33 | [post] z | summaries.rb:125:6:125:6 | z | provenance | |
| summaries.rb:128:1:128:1 | [post] x | summaries.rb:129:6:129:6 | x | provenance | |
| summaries.rb:128:14:128:20 | tainted | summaries.rb:128:1:128:1 | [post] x | provenance | MaD:21 |
| summaries.rb:131:16:131:22 | tainted | summaries.rb:131:1:131:23 | synthetic splat argument | provenance | Sink:MaD:3 |
| summaries.rb:128:14:128:20 | tainted | summaries.rb:128:1:128:1 | [post] x | provenance | MaD:83 |
| summaries.rb:131:16:131:22 | tainted | summaries.rb:131:1:131:23 | synthetic splat argument | provenance | Sink:MaD:65 |
| summaries.rb:157:14:160:3 | do ... end [captured tainted] | summaries.rb:158:15:158:21 | tainted | provenance | heuristic-callback |
| summaries.rb:157:14:160:3 | do ... end [captured tainted] | summaries.rb:158:15:158:21 | tainted | provenance | heuristic-callback |
nodes

View File

@@ -154,6 +154,10 @@ calls.rb:
# 649| CustomNew2
#-----| super -> Object
element_reference.rb:
# 1| ClassWithElementRef
#-----| super -> Object
hello.rb:
# 1| EnglishWords

View File

@@ -257,6 +257,13 @@ getTarget
| 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 |
| element_reference.rb:3:9:3:19 | yield ... | element_reference.rb:9:6:9:19 | { ... } |
| element_reference.rb:3:9:3:19 | yield ... | element_reference.rb:11:6:13:3 | do ... end |
| element_reference.rb:7:5:7:27 | call to new | calls.rb:117:5:117:16 | new |
| element_reference.rb:9:1:9:19 | ...[...] | element_reference.rb:2:5:4:7 | [] |
| element_reference.rb:9:12:9:17 | call to puts | calls.rb:102:5:102:30 | puts |
| element_reference.rb:11:1:13:3 | ...[...] | element_reference.rb:2:5:4:7 | [] |
| element_reference.rb:12:5:12:10 | call to puts | calls.rb:102:5:102:30 | puts |
| 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 |
@@ -385,6 +392,7 @@ unresolvedCall
| 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 |
| element_reference.rb:3:15:3:19 | ... + ... |
| hello.rb:20:16:20:26 | ... + ... |
| hello.rb:20:16:20:34 | ... + ... |
| hello.rb:20:16:20:40 | ... + ... |
@@ -512,6 +520,7 @@ publicMethod
| calls.rb:642:5:644:7 | new |
| calls.rb:650:5:652:7 | new |
| calls.rb:654:5:656:7 | instance |
| element_reference.rb:2:5:4:7 | [] |
| hello.rb:2:5:4:7 | hello |
| hello.rb:5:5:7:7 | world |
| hello.rb:13:5:15:7 | message |

View File

@@ -0,0 +1,13 @@
class ClassWithElementRef
def [](x)
yield x + 1
end
end
c = ClassWithElementRef.new
c[1] { |x| puts x }
c[1] do |x|
puts x
end

View File

@@ -51,6 +51,7 @@ getMethod
| calls.rb:626:1:632:3 | Included | foo | calls.rb:627:5:629:7 | foo |
| calls.rb:634:1:639:3 | IncludesIncluded | bar | calls.rb:636:5:638:7 | bar |
| calls.rb:649:1:657:3 | CustomNew2 | instance | calls.rb:654:5:656:7 | instance |
| element_reference.rb:1:1:5:3 | ClassWithElementRef | [] | element_reference.rb:2:5:4:7 | [] |
| hello.rb:1:1:8:3 | EnglishWords | hello | hello.rb:2:5:4:7 | hello |
| hello.rb:1:1:8:3 | EnglishWords | world | hello.rb:5:5:7:7 | world |
| hello.rb:11:1:16:3 | Greeting | message | hello.rb:13:5:15:7 | message |
@@ -561,6 +562,10 @@ lookupMethod
| calls.rb:649:1:657:3 | CustomNew2 | private_on_main | calls.rb:185:1:186:3 | private_on_main |
| calls.rb:649:1:657:3 | CustomNew2 | puts | calls.rb:102:5:102:30 | puts |
| calls.rb:649:1:657:3 | CustomNew2 | to_s | calls.rb:172:5:173:7 | to_s |
| element_reference.rb:1:1:5:3 | ClassWithElementRef | [] | element_reference.rb:2:5:4:7 | [] |
| element_reference.rb:1:1:5:3 | ClassWithElementRef | new | calls.rb:117:5:117:16 | new |
| element_reference.rb:1:1:5:3 | ClassWithElementRef | puts | calls.rb:102:5:102:30 | puts |
| element_reference.rb:1:1:5:3 | ClassWithElementRef | to_s | calls.rb:172:5:173:7 | to_s |
| file://:0:0:0:0 | Class | include | calls.rb:108:5:110:7 | include |
| file://:0:0:0:0 | Class | module_eval | calls.rb:107:5:107:24 | module_eval |
| file://:0:0:0:0 | Class | new | calls.rb:117:5:117:16 | new |
@@ -1080,6 +1085,12 @@ enclosingMethod
| calls.rb:662:10:662:10 | 2 | calls.rb:661:1:665:3 | capture_parameter |
| calls.rb:662:18:664:7 | do ... end | calls.rb:661:1:665:3 | capture_parameter |
| calls.rb:663:9:663:9 | x | calls.rb:661:1:665:3 | capture_parameter |
| element_reference.rb:2:12:2:12 | x | element_reference.rb:2:5:4:7 | [] |
| element_reference.rb:2:12:2:12 | x | element_reference.rb:2:5:4:7 | [] |
| element_reference.rb:3:9:3:19 | yield ... | element_reference.rb:2:5:4:7 | [] |
| element_reference.rb:3:15:3:15 | x | element_reference.rb:2:5:4:7 | [] |
| element_reference.rb:3:15:3:19 | ... + ... | element_reference.rb:2:5:4:7 | [] |
| element_reference.rb:3:19:3:19 | 1 | element_reference.rb:2:5:4:7 | [] |
| hello.rb:3:9:3:22 | return | hello.rb:2:5:4:7 | hello |
| hello.rb:3:16:3:22 | "hello" | hello.rb:2:5:4:7 | hello |
| hello.rb:3:17:3:21 | hello | hello.rb:2:5:4:7 | hello |

View File

@@ -36,6 +36,7 @@ getModule
| calls.rb:634:1:639:3 | IncludesIncluded |
| calls.rb:641:1:645:3 | CustomNew1 |
| calls.rb:649:1:657:3 | CustomNew2 |
| element_reference.rb:1:1:5:3 | ClassWithElementRef |
| file://:0:0:0:0 | BasicObject |
| file://:0:0:0:0 | Class |
| file://:0:0:0:0 | Complex |
@@ -113,6 +114,7 @@ getADeclaration
| calls.rb:105:1:113:3 | Module | calls.rb:105:1:113:3 | Module |
| calls.rb:115:1:118:3 | Object | calls.rb:1:1:667:52 | calls.rb |
| calls.rb:115:1:118:3 | Object | calls.rb:115:1:118:3 | Object |
| calls.rb:115:1:118:3 | Object | element_reference.rb:1:1:13:4 | element_reference.rb |
| calls.rb:115:1:118:3 | Object | hello.rb:1:1:22:3 | hello.rb |
| calls.rb:115:1:118:3 | Object | instance_fields.rb:1:1:29:4 | instance_fields.rb |
| calls.rb:115:1:118:3 | Object | modules.rb:1:1:129:4 | modules.rb |
@@ -153,6 +155,7 @@ getADeclaration
| calls.rb:634:1:639:3 | IncludesIncluded | calls.rb:634:1:639:3 | IncludesIncluded |
| calls.rb:641:1:645:3 | CustomNew1 | calls.rb:641:1:645:3 | CustomNew1 |
| calls.rb:649:1:657:3 | CustomNew2 | calls.rb:649:1:657:3 | CustomNew2 |
| element_reference.rb:1:1:5:3 | ClassWithElementRef | element_reference.rb:1:1:5:3 | ClassWithElementRef |
| hello.rb:1:1:8:3 | EnglishWords | hello.rb:1:1:8:3 | EnglishWords |
| hello.rb:11:1:16:3 | Greeting | hello.rb:11:1:16:3 | Greeting |
| hello.rb:18:1:22:3 | HelloWorld | hello.rb:18:1:22:3 | HelloWorld |
@@ -238,6 +241,7 @@ getSuperClass
| calls.rb:634:1:639:3 | IncludesIncluded | calls.rb:115:1:118:3 | Object |
| calls.rb:641:1:645:3 | CustomNew1 | calls.rb:115:1:118:3 | Object |
| calls.rb:649:1:657:3 | CustomNew2 | calls.rb:115:1:118:3 | Object |
| element_reference.rb:1:1:5:3 | ClassWithElementRef | calls.rb:115:1:118:3 | Object |
| file://:0:0:0:0 | Class | calls.rb:105:1:113:3 | Module |
| file://:0:0:0:0 | Complex | file://:0:0:0:0 | Numeric |
| file://:0:0:0:0 | FalseClass | calls.rb:115:1:118:3 | Object |
@@ -389,6 +393,7 @@ resolveConstantReadAccess
| calls.rb:659:1:659:10 | CustomNew2 | CustomNew2 |
| calls.rb:662:5:662:11 | Array | Array |
| calls.rb:667:20:667:21 | C1 | C1 |
| element_reference.rb:7:5:7:23 | ClassWithElementRef | ClassWithElementRef |
| hello.rb:12:13:12:24 | EnglishWords | EnglishWords |
| hello.rb:18:20:18:27 | Greeting | Greeting |
| instance_fields.rb:4:22:4:31 | A_target | A_target |
@@ -495,6 +500,7 @@ resolveConstantWriteAccess
| calls.rb:634:1:639:3 | IncludesIncluded | IncludesIncluded |
| calls.rb:641:1:645:3 | CustomNew1 | CustomNew1 |
| calls.rb:649:1:657:3 | CustomNew2 | CustomNew2 |
| element_reference.rb:1:1:5:3 | ClassWithElementRef | ClassWithElementRef |
| hello.rb:1:1:8:3 | EnglishWords | EnglishWords |
| hello.rb:11:1:16:3 | Greeting | Greeting |
| hello.rb:18:1:22:3 | HelloWorld | HelloWorld |
@@ -1611,6 +1617,36 @@ enclosingModule
| calls.rb:667:2:667:25 | self | calls.rb:1:1:667:52 | calls.rb |
| calls.rb:667:20:667:21 | C1 | calls.rb:1:1:667:52 | calls.rb |
| calls.rb:667:20:667:25 | call to new | calls.rb:1:1:667:52 | calls.rb |
| element_reference.rb:1:1:5:3 | ClassWithElementRef | element_reference.rb:1:1:13:4 | element_reference.rb |
| element_reference.rb:2:5:4:7 | [] | element_reference.rb:1:1:5:3 | ClassWithElementRef |
| element_reference.rb:2:12:2:12 | x | element_reference.rb:1:1:5:3 | ClassWithElementRef |
| element_reference.rb:2:12:2:12 | x | element_reference.rb:1:1:5:3 | ClassWithElementRef |
| element_reference.rb:3:9:3:19 | yield ... | element_reference.rb:1:1:5:3 | ClassWithElementRef |
| element_reference.rb:3:15:3:15 | x | element_reference.rb:1:1:5:3 | ClassWithElementRef |
| element_reference.rb:3:15:3:19 | ... + ... | element_reference.rb:1:1:5:3 | ClassWithElementRef |
| element_reference.rb:3:19:3:19 | 1 | element_reference.rb:1:1:5:3 | ClassWithElementRef |
| element_reference.rb:7:1:7:1 | c | element_reference.rb:1:1:13:4 | element_reference.rb |
| element_reference.rb:7:1:7:27 | ... = ... | element_reference.rb:1:1:13:4 | element_reference.rb |
| element_reference.rb:7:5:7:23 | ClassWithElementRef | element_reference.rb:1:1:13:4 | element_reference.rb |
| element_reference.rb:7:5:7:27 | call to new | element_reference.rb:1:1:13:4 | element_reference.rb |
| element_reference.rb:9:1:9:1 | c | element_reference.rb:1:1:13:4 | element_reference.rb |
| element_reference.rb:9:1:9:19 | ...[...] | element_reference.rb:1:1:13:4 | element_reference.rb |
| element_reference.rb:9:3:9:3 | 1 | element_reference.rb:1:1:13:4 | element_reference.rb |
| element_reference.rb:9:6:9:19 | { ... } | element_reference.rb:1:1:13:4 | element_reference.rb |
| element_reference.rb:9:9:9:9 | x | element_reference.rb:1:1:13:4 | element_reference.rb |
| element_reference.rb:9:9:9:9 | x | element_reference.rb:1:1:13:4 | element_reference.rb |
| element_reference.rb:9:12:9:17 | call to puts | element_reference.rb:1:1:13:4 | element_reference.rb |
| element_reference.rb:9:12:9:17 | self | element_reference.rb:1:1:13:4 | element_reference.rb |
| element_reference.rb:9:17:9:17 | x | element_reference.rb:1:1:13:4 | element_reference.rb |
| element_reference.rb:11:1:11:1 | c | element_reference.rb:1:1:13:4 | element_reference.rb |
| element_reference.rb:11:1:13:3 | ...[...] | element_reference.rb:1:1:13:4 | element_reference.rb |
| element_reference.rb:11:3:11:3 | 1 | element_reference.rb:1:1:13:4 | element_reference.rb |
| element_reference.rb:11:6:13:3 | do ... end | element_reference.rb:1:1:13:4 | element_reference.rb |
| element_reference.rb:11:10:11:10 | x | element_reference.rb:1:1:13:4 | element_reference.rb |
| element_reference.rb:11:10:11:10 | x | element_reference.rb:1:1:13:4 | element_reference.rb |
| element_reference.rb:12:5:12:10 | call to puts | element_reference.rb:1:1:13:4 | element_reference.rb |
| element_reference.rb:12:5:12:10 | self | element_reference.rb:1:1:13:4 | element_reference.rb |
| element_reference.rb:12:10:12:10 | x | element_reference.rb:1:1:13:4 | element_reference.rb |
| hello.rb:1:1:8:3 | EnglishWords | hello.rb:1:1:22:3 | hello.rb |
| hello.rb:2:5:4:7 | hello | hello.rb:1:1:8:3 | EnglishWords |
| hello.rb:3:9:3:22 | return | hello.rb:1:1:8:3 | EnglishWords |

View File

@@ -148,6 +148,10 @@ calls.rb:
# 649| CustomNew2
#-----| -> Object
element_reference.rb:
# 1| ClassWithElementRef
#-----| -> Object
hello.rb:
# 1| EnglishWords

View File

@@ -0,0 +1,28 @@
edges
| weak_hashing.rb:3:1:3:8 | password | weak_hashing.rb:6:1:6:1 | x | provenance | |
| weak_hashing.rb:3:1:3:8 | password | weak_hashing.rb:10:23:10:30 | password | provenance | |
| weak_hashing.rb:3:1:3:8 | password | weak_hashing.rb:11:32:11:39 | password | provenance | |
| weak_hashing.rb:4:1:4:8 | username | weak_hashing.rb:12:23:12:30 | username | provenance | |
| weak_hashing.rb:6:1:6:1 | x | weak_hashing.rb:13:23:13:23 | x | provenance | |
| weak_hashing.rb:30:25:30:38 | password_param | weak_hashing.rb:32:25:32:38 | password_param | provenance | |
nodes
| weak_hashing.rb:3:1:3:8 | password | semmle.label | password |
| weak_hashing.rb:4:1:4:8 | username | semmle.label | username |
| weak_hashing.rb:6:1:6:1 | x | semmle.label | x |
| weak_hashing.rb:10:23:10:30 | password | semmle.label | password |
| weak_hashing.rb:11:32:11:39 | password | semmle.label | password |
| weak_hashing.rb:12:23:12:30 | username | semmle.label | username |
| weak_hashing.rb:13:23:13:23 | x | semmle.label | x |
| weak_hashing.rb:24:23:24:36 | call to get_password | semmle.label | call to get_password |
| weak_hashing.rb:28:23:28:42 | ...[...] | semmle.label | ...[...] |
| weak_hashing.rb:30:25:30:38 | password_param | semmle.label | password_param |
| weak_hashing.rb:32:25:32:38 | password_param | semmle.label | password_param |
subpaths
#select
| weak_hashing.rb:10:23:10:30 | password | weak_hashing.rb:3:1:3:8 | password | weak_hashing.rb:10:23:10:30 | password | $@ is used in a hashing algorithm (MD5) that is insecure for password hashing, since it is not a computationally expensive hash function. | weak_hashing.rb:3:1:3:8 | password | Sensitive data (password) |
| weak_hashing.rb:11:32:11:39 | password | weak_hashing.rb:3:1:3:8 | password | weak_hashing.rb:11:32:11:39 | password | $@ is used in a hashing algorithm (SHA1) that is insecure for password hashing, since it is not a computationally expensive hash function. | weak_hashing.rb:3:1:3:8 | password | Sensitive data (password) |
| weak_hashing.rb:12:23:12:30 | username | weak_hashing.rb:4:1:4:8 | username | weak_hashing.rb:12:23:12:30 | username | $@ is used in a hashing algorithm (MD5) that is insecure. | weak_hashing.rb:4:1:4:8 | username | Sensitive data (id) |
| weak_hashing.rb:13:23:13:23 | x | weak_hashing.rb:3:1:3:8 | password | weak_hashing.rb:13:23:13:23 | x | $@ is used in a hashing algorithm (MD5) that is insecure for password hashing, since it is not a computationally expensive hash function. | weak_hashing.rb:3:1:3:8 | password | Sensitive data (password) |
| weak_hashing.rb:24:23:24:36 | call to get_password | weak_hashing.rb:24:23:24:36 | call to get_password | weak_hashing.rb:24:23:24:36 | call to get_password | $@ is used in a hashing algorithm (MD5) that is insecure for password hashing, since it is not a computationally expensive hash function. | weak_hashing.rb:24:23:24:36 | call to get_password | Sensitive data (password) |
| weak_hashing.rb:28:23:28:42 | ...[...] | weak_hashing.rb:28:23:28:42 | ...[...] | weak_hashing.rb:28:23:28:42 | ...[...] | $@ is used in a hashing algorithm (MD5) that is insecure for password hashing, since it is not a computationally expensive hash function. | weak_hashing.rb:28:23:28:42 | ...[...] | Sensitive data (password) |
| weak_hashing.rb:32:25:32:38 | password_param | weak_hashing.rb:30:25:30:38 | password_param | weak_hashing.rb:32:25:32:38 | password_param | $@ is used in a hashing algorithm (MD5) that is insecure for password hashing, since it is not a computationally expensive hash function. | weak_hashing.rb:30:25:30:38 | password_param | Sensitive data (password) |

View File

@@ -0,0 +1 @@
queries/security/cwe-327/WeakSensitiveDataHashing.ql

View File

@@ -0,0 +1,33 @@
require 'openssl'
password = "abcde"
username = "some_user"
some_data = "foo"
x = password
Digest::MD5.hexdigest(some_data) # OK: input is not sensitive
Digest::SHA256.hexdigest(password) # OK: strong hash algorithm
Digest::MD5.hexdigest(password) # BAD: weak hash function used for sensitive data
OpenSSL::Digest.digest('SHA1', password) # BAD: weak hash function used for sensitive data
Digest::MD5.hexdigest(username) # BAD: weak hash function used for sensitive data
Digest::MD5.hexdigest(x) # BAD: weak hash function used for sensitive data
def get_safe_data()
return "hello"
end
def get_password()
return "changeme"
end
Digest::MD5.hexdigest(get_safe_data()) # OK: input is not sensitive
Digest::MD5.hexdigest(get_password()) # BAD: weak hash function used for sensitive data
some_hash = {password: "changeme", foo: "bar"}
Digest::MD5.hexdigest(some_hash[:foo]) # OK: input is not sensitive
Digest::MD5.hexdigest(some_hash[:password]) # BAD: weak hash function used for sensitive data
def a_method(safe_data, password_param)
Digest::MD5.hexdigest(safe_data) # OK: input is not sensitive
Digest::MD5.hexdigest(password_param) # BAD: weak hash function used for sensitive data
end

View File

@@ -5,9 +5,9 @@ edges
| tst.rb:5:1:5:23 | totally_harmless_string | tst.rb:7:8:7:30 | totally_harmless_string | provenance | |
| tst.rb:5:27:5:72 | "707574732822636f646520696e6a6..." | tst.rb:5:1:5:23 | totally_harmless_string | provenance | |
| tst.rb:7:8:7:30 | totally_harmless_string | tst.rb:1:7:1:7 | r | provenance | |
| tst.rb:7:8:7:30 | totally_harmless_string | tst.rb:7:6:7:31 | call to e | provenance | |
| tst.rb:7:8:7:30 | totally_harmless_string | tst.rb:7:6:7:31 | call to e | provenance | Config |
| tst.rb:10:11:10:24 | "666f6f626172" | tst.rb:1:7:1:7 | r | provenance | |
| tst.rb:10:11:10:24 | "666f6f626172" | tst.rb:10:9:10:25 | call to e | provenance | |
| tst.rb:10:11:10:24 | "666f6f626172" | tst.rb:10:9:10:25 | call to e | provenance | Config |
| tst.rb:16:1:16:27 | another_questionable_string | tst.rb:17:6:17:32 | another_questionable_string | provenance | |
| tst.rb:16:31:16:84 | "\\x70\\x75\\x74\\x73\\x28\\x27\\x68\\..." | tst.rb:16:1:16:27 | another_questionable_string | provenance | |
| tst.rb:17:6:17:32 | another_questionable_string | tst.rb:17:6:17:38 | call to strip | provenance | Config |