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

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 |