Merge branch 'main' into henrymercer/mergeback-3.8

This commit is contained in:
Henry Mercer
2022-12-13 18:40:53 +00:00
2013 changed files with 99146 additions and 101594 deletions

View File

@@ -0,0 +1,7 @@
---
category: minorAnalysis
---
* Deleted the deprecated `importNode` predicate from the `DataFlowUtil.qll` file.
* Deleted the deprecated features from `PEP249.qll` that were not inside the `PEP249` module.
* Deleted the deprecated `werkzeug` from the `Werkzeug` module in `Werkzeug.qll`.
* Deleted the deprecated `methodResult` predicate from `PEP249::Cursor`.

View File

@@ -0,0 +1,4 @@
---
category: majorAnalysis
---
* The _PAM authorization bypass due to incorrect usage_ (`py/pam-auth-bypass`) query has been converted to a taint-tracking query, resulting in significantly fewer false positives.

View File

@@ -0,0 +1,5 @@
---
category: minorAnalysis
---
* The data-flow library has been rewritten to no longer rely on the points-to analysis in order to
resolve references to modules. Improvements in the module resolution can lead to more results.

View File

@@ -0,0 +1,5 @@
---
category: minorAnalysis
---
- Added `subprocess.getoutput` and `subprocess.getoutputstatus` as new command injection sinks for the StdLib.

View File

@@ -6,4 +6,6 @@ extractor: python
library: true
upgrades: upgrades
dependencies:
codeql/regex: ${workspace}
codeql/regex: ${workspace}
dataExtensions:
- semmle/python/frameworks/**/model.yml

View File

@@ -9,6 +9,7 @@ private import semmle.python.dataflow.new.DataFlow
private import semmle.python.dataflow.new.RemoteFlowSources
private import semmle.python.dataflow.new.TaintTracking
private import semmle.python.Frameworks
private import semmle.python.security.internal.EncryptionKeySizes
/**
* A data-flow node that executes an operating system command,
@@ -1141,21 +1142,21 @@ module Cryptography {
abstract class RsaRange extends Range {
final override string getName() { result = "RSA" }
final override int minimumSecureKeySize() { result = 2048 }
final override int minimumSecureKeySize() { result = minSecureKeySizeRsa() }
}
/** A data-flow node that generates a new DSA key-pair. */
abstract class DsaRange extends Range {
final override string getName() { result = "DSA" }
final override int minimumSecureKeySize() { result = 2048 }
final override int minimumSecureKeySize() { result = minSecureKeySizeDsa() }
}
/** A data-flow node that generates a new ECC key-pair. */
abstract class EccRange extends Range {
final override string getName() { result = "ECC" }
final override int minimumSecureKeySize() { result = 224 }
final override int minimumSecureKeySize() { result = minSecureKeySizeEcc() }
}
}
}

View File

@@ -454,7 +454,7 @@ module Impl implements RegexTreeViewSig {
override string getPrimaryQLClass() { result = "RegExpAlt" }
}
class RegExpCharEscape = RegExpEscape;
additional class RegExpCharEscape = RegExpEscape;
/**
* An escaped regular expression term, that is, a regular expression
@@ -684,7 +684,7 @@ module Impl implements RegexTreeViewSig {
* \t
* ```
*/
class RegExpNormalChar extends RegExpTerm, TRegExpNormalChar {
additional class RegExpNormalChar extends RegExpTerm, TRegExpNormalChar {
RegExpNormalChar() { this = TRegExpNormalChar(re, start, end) }
/**
@@ -792,7 +792,7 @@ module Impl implements RegexTreeViewSig {
* .
* ```
*/
class RegExpSpecialChar extends RegExpTerm, TRegExpSpecialChar {
additional class RegExpSpecialChar extends RegExpTerm, TRegExpSpecialChar {
string char;
RegExpSpecialChar() {
@@ -868,7 +868,7 @@ module Impl implements RegexTreeViewSig {
* (?=\w)
* ```
*/
class RegExpZeroWidthMatch extends RegExpGroup {
additional class RegExpZeroWidthMatch extends RegExpGroup {
RegExpZeroWidthMatch() { re.zeroWidthMatch(start, end) }
override RegExpTerm getChild(int i) { none() }
@@ -937,7 +937,7 @@ module Impl implements RegexTreeViewSig {
* (?!\n)
* ```
*/
class RegExpNegativeLookahead extends RegExpLookahead {
additional class RegExpNegativeLookahead extends RegExpLookahead {
RegExpNegativeLookahead() { re.negativeLookaheadAssertionGroup(start, end) }
override string getPrimaryQLClass() { result = "RegExpNegativeLookahead" }
@@ -979,7 +979,7 @@ module Impl implements RegexTreeViewSig {
* (?<!\\)
* ```
*/
class RegExpNegativeLookbehind extends RegExpLookbehind {
additional class RegExpNegativeLookbehind extends RegExpLookbehind {
RegExpNegativeLookbehind() { re.negativeLookbehindAssertionGroup(start, end) }
override string getPrimaryQLClass() { result = "RegExpNegativeLookbehind" }

View File

@@ -1,4 +1,5 @@
/* This file contains test-related utility functions */
/** This file contains test-related utility functions */
import python
/** Removes everything up to the occurrence of `sub` in the string `str` */

View File

@@ -8,6 +8,7 @@ private import semmle.python.dataflow.new.DataFlow
// Need to import `semmle.python.Frameworks` since frameworks can extend `SensitiveDataSource::Range`
private import semmle.python.Frameworks
private import semmle.python.security.internal.SensitiveDataHeuristics as SensitiveDataHeuristics
private import semmle.python.ApiGraphs
// We export these explicitly, so we don't also export the `HeuristicNames` module.
class SensitiveDataClassification = SensitiveDataHeuristics::SensitiveDataClassification;
@@ -23,7 +24,17 @@ module SensitiveDataClassification = SensitiveDataHeuristics::SensitiveDataClass
class SensitiveDataSource extends DataFlow::Node {
SensitiveDataSource::Range range;
SensitiveDataSource() { this = range }
SensitiveDataSource() {
this = range and
// ignore sensitive password sources in getpass.py, that can escape through `getpass.getpass()` return value,
// since `getpass.getpass()` is considered a source itself.
not exists(Module getpass |
getpass.getName() = "getpass" and
this.getScope().getEnclosingModule() = getpass and
// do allow this call if we're analyzing getpass.py as part of CPython though
not exists(getpass.getFile().getRelativePath())
)
}
/**
* Gets the classification of the sensitive data.
@@ -312,6 +323,17 @@ private module SensitiveDataModeling {
override SensitiveDataClassification getClassification() { result = classification }
}
/**
* A call to `getpass.getpass`, see https://docs.python.org/3.10/library/getpass.html#getpass.getpass
*/
class GetPassCall extends SensitiveDataSource::Range, API::CallNode {
GetPassCall() { this = API::moduleImport("getpass").getMember("getpass").getACall() }
override SensitiveDataClassification getClassification() {
result = SensitiveDataClassification::password()
}
}
}
predicate sensitiveDataExtraStepForCalls = SensitiveDataModeling::extraStepForCalls/2;

View File

@@ -70,7 +70,7 @@ abstract class Configuration extends string {
/**
* Holds if `sink` is a relevant data flow sink accepting `state`.
*/
predicate isSink(Node source, FlowState state) { none() }
predicate isSink(Node sink, FlowState state) { none() }
/**
* Holds if data flow through `node` is prohibited. This completely removes
@@ -319,8 +319,6 @@ private class ParamNodeEx extends NodeEx {
}
ParameterPosition getPosition() { this.isParameterOf(_, result) }
predicate allowParameterReturnInSelf() { allowParameterReturnInSelfCached(this.asNode()) }
}
private class RetNodeEx extends NodeEx {
@@ -608,6 +606,38 @@ private predicate hasSinkCallCtx(Configuration config) {
)
}
/**
* Holds if flow from `p` to a return node of kind `kind` is allowed.
*
* We don't expect a parameter to return stored in itself, unless
* explicitly allowed
*/
bindingset[p, kind]
private predicate parameterFlowThroughAllowed(ParamNodeEx p, ReturnKindExt kind) {
exists(ParameterPosition pos | p.isParameterOf(_, pos) |
not kind.(ParamUpdateReturnKind).getPosition() = pos
or
allowParameterReturnInSelfCached(p.asNode())
)
}
/**
* Holds if flow from a parameter at position `pos` inside `c` to a return node of
* kind `kind` is allowed.
*
* We don't expect a parameter to return stored in itself, unless
* explicitly allowed
*/
bindingset[c, pos, kind]
private predicate parameterFlowThroughAllowed(
DataFlowCallable c, ParameterPosition pos, ReturnKindExt kind
) {
exists(ParamNodeEx p |
p.isParameterOf(c, pos) and
parameterFlowThroughAllowed(p, kind)
)
}
private module Stage1 implements StageSig {
class Ap = Unit;
@@ -981,21 +1011,22 @@ private module Stage1 implements StageSig {
* candidate for the origin of a summary.
*/
pragma[nomagic]
predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) {
exists(ReturnKindExt kind |
predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) {
exists(DataFlowCallable c, ReturnKindExt kind |
throughFlowNodeCand(p, config) and
returnFlowCallableNodeCand(c, kind, config) and
p.getEnclosingCallable() = c and
exists(ap) and
// we don't expect a parameter to return stored in itself, unless explicitly allowed
(
not kind.(ParamUpdateReturnKind).getPosition() = p.getPosition()
or
p.allowParameterReturnInSelf()
)
parameterFlowThroughAllowed(p, kind)
)
}
pragma[nomagic]
predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) {
throughFlowNodeCand(ret, config) and
kind = ret.getKind()
}
pragma[nomagic]
predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) {
exists(ArgNodeEx arg, boolean toReturn |
@@ -1052,12 +1083,16 @@ private predicate viableReturnPosOutNodeCand1(
*/
pragma[nomagic]
private predicate flowOutOfCallNodeCand1(
DataFlowCall call, RetNodeEx ret, NodeEx out, Configuration config
DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, Configuration config
) {
viableReturnPosOutNodeCand1(call, ret.getReturnPosition(), out, config) and
Stage1::revFlow(ret, config) and
not outBarrier(ret, config) and
not inBarrier(out, config)
exists(ReturnPosition pos |
viableReturnPosOutNodeCand1(call, pos, out, config) and
pos = ret.getReturnPosition() and
kind = pos.getKind() and
Stage1::revFlow(ret, config) and
not outBarrier(ret, config) and
not inBarrier(out, config)
)
}
pragma[nomagic]
@@ -1087,10 +1122,11 @@ private predicate flowIntoCallNodeCand1(
* edge in the graph of paths between sources and sinks that ignores call
* contexts.
*/
pragma[nomagic]
private int branch(NodeEx n1, Configuration conf) {
result =
strictcount(NodeEx n |
flowOutOfCallNodeCand1(_, n1, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf)
flowOutOfCallNodeCand1(_, n1, _, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf)
)
}
@@ -1099,10 +1135,11 @@ private int branch(NodeEx n1, Configuration conf) {
* edge in the graph of paths between sources and sinks that ignores call
* contexts.
*/
pragma[nomagic]
private int join(NodeEx n2, Configuration conf) {
result =
strictcount(NodeEx n |
flowOutOfCallNodeCand1(_, n, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf)
flowOutOfCallNodeCand1(_, n, _, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf)
)
}
@@ -1115,12 +1152,13 @@ private int join(NodeEx n2, Configuration conf) {
*/
pragma[nomagic]
private predicate flowOutOfCallNodeCand1(
DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config
DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow,
Configuration config
) {
flowOutOfCallNodeCand1(call, ret, out, config) and
flowOutOfCallNodeCand1(call, ret, kind, out, pragma[only_bind_into](config)) and
exists(int b, int j |
b = branch(ret, config) and
j = join(out, config) and
b = branch(ret, pragma[only_bind_into](config)) and
j = join(out, pragma[only_bind_into](config)) and
if b.minimum(j) <= config.fieldFlowBranchLimit()
then allowsFieldFlow = true
else allowsFieldFlow = false
@@ -1136,10 +1174,10 @@ pragma[nomagic]
private predicate flowIntoCallNodeCand1(
DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
) {
flowIntoCallNodeCand1(call, arg, p, config) and
flowIntoCallNodeCand1(call, arg, p, pragma[only_bind_into](config)) and
exists(int b, int j |
b = branch(arg, config) and
j = join(p, config) and
b = branch(arg, pragma[only_bind_into](config)) and
j = join(p, pragma[only_bind_into](config)) and
if b.minimum(j) <= config.fieldFlowBranchLimit()
then allowsFieldFlow = true
else allowsFieldFlow = false
@@ -1156,7 +1194,9 @@ private signature module StageSig {
predicate callMayFlowThroughRev(DataFlowCall call, Configuration config);
predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config);
predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config);
predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config);
predicate storeStepCand(
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType,
@@ -1222,7 +1262,8 @@ private module MkStage<StageSig PrevStage> {
);
predicate flowOutOfCall(
DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config
DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow,
Configuration config
);
predicate flowIntoCall(
@@ -1247,14 +1288,14 @@ private module MkStage<StageSig PrevStage> {
pragma[nomagic]
private predicate flowThroughOutOfCall(
DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow,
Configuration config
DataFlowCall call, DataFlowCallable c, CcCall ccc, RetNodeEx ret, ReturnKindExt kind,
NodeEx out, boolean allowsFieldFlow, Configuration config
) {
flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and
flowOutOfCall(call, ret, kind, out, allowsFieldFlow, pragma[only_bind_into](config)) and
PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and
PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _,
pragma[only_bind_into](config)) and
matchesCall(ccc, call)
PrevStage::returnMayFlowThrough(ret, kind, pragma[only_bind_into](config)) and
matchesCall(ccc, call) and
c = ret.getEnclosingCallable()
}
/**
@@ -1262,29 +1303,32 @@ private module MkStage<StageSig PrevStage> {
* configuration `config`.
*
* The call context `cc` records whether the node is reached through an
* argument in a call, and if so, `argAp` records the access path of that
* argument.
* argument in a call, and if so, `summaryCtx` and `argAp` record the
* corresponding parameter position and access path of that argument, respectively.
*/
pragma[nomagic]
additional predicate fwdFlow(
NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config
NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
Ap ap, Configuration config
) {
fwdFlow0(node, state, cc, argAp, ap, config) and
fwdFlow0(node, state, cc, summaryCtx, argAp, ap, config) and
PrevStage::revFlow(node, state, unbindApa(getApprox(ap)), config) and
filter(node, state, ap, config)
}
pragma[nomagic]
private predicate fwdFlow0(
NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config
NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
Ap ap, Configuration config
) {
sourceNode(node, state, config) and
(if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
argAp = apNone() and
summaryCtx = TParameterPositionNone() and
ap = getApNil(node)
or
exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc |
fwdFlow(mid, state0, cc, argAp, ap0, config) and
fwdFlow(mid, state0, cc, summaryCtx, argAp, ap0, config) and
localCc = getLocalCc(mid, cc)
|
localStep(mid, state0, node, state, true, _, config, localCc) and
@@ -1295,65 +1339,82 @@ private module MkStage<StageSig PrevStage> {
)
or
exists(NodeEx mid |
fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and
fwdFlow(mid, pragma[only_bind_into](state), _, _, _, ap, pragma[only_bind_into](config)) and
jumpStep(mid, node, config) and
cc = ccNone() and
summaryCtx = TParameterPositionNone() and
argAp = apNone()
)
or
exists(NodeEx mid, ApNil nil |
fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and
fwdFlow(mid, state, _, _, _, nil, pragma[only_bind_into](config)) and
additionalJumpStep(mid, node, config) and
cc = ccNone() and
summaryCtx = TParameterPositionNone() and
argAp = apNone() and
ap = getApNil(node)
)
or
exists(NodeEx mid, FlowState state0, ApNil nil |
fwdFlow(mid, state0, _, _, nil, pragma[only_bind_into](config)) and
fwdFlow(mid, state0, _, _, _, nil, pragma[only_bind_into](config)) and
additionalJumpStateStep(mid, state0, node, state, config) and
cc = ccNone() and
summaryCtx = TParameterPositionNone() and
argAp = apNone() and
ap = getApNil(node)
)
or
// store
exists(TypedContent tc, Ap ap0 |
fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and
fwdFlowStore(_, ap0, tc, node, state, cc, summaryCtx, argAp, config) and
ap = apCons(tc, ap0)
)
or
// read
exists(Ap ap0, Content c |
fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and
fwdFlowRead(ap0, c, _, node, state, cc, summaryCtx, argAp, config) and
fwdFlowConsCand(ap0, c, ap, config)
)
or
// flow into a callable
exists(ApApprox apa |
fwdFlowIn(_, node, state, _, cc, _, ap, config) and
fwdFlowIn(_, node, state, _, cc, _, _, ap, config) and
apa = getApprox(ap) and
if PrevStage::parameterMayFlowThrough(node, _, apa, config)
then argAp = apSome(ap)
else argAp = apNone()
if PrevStage::parameterMayFlowThrough(node, apa, config)
then (
summaryCtx = TParameterPositionSome(node.(ParamNodeEx).getPosition()) and
argAp = apSome(ap)
) else (
summaryCtx = TParameterPositionNone() and argAp = apNone()
)
)
or
// flow out of a callable
fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config)
exists(
DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc,
DataFlowCallable inner
|
fwdFlow(ret, state, innercc, summaryCtx, argAp, ap, config) and
flowOutOfCall(call, ret, _, node, allowsFieldFlow, config) and
inner = ret.getEnclosingCallable() and
cc = getCallContextReturn(inner, call, innercc) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
)
or
exists(DataFlowCall call, Ap argAp0 |
fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and
fwdFlowIsEntered(call, cc, argAp, argAp0, config)
// flow through a callable
exists(DataFlowCall call, ParameterPosition summaryCtx0, Ap argAp0 |
fwdFlowOutFromArg(call, node, state, summaryCtx0, argAp0, ap, config) and
fwdFlowIsEntered(call, cc, summaryCtx, argAp, summaryCtx0, argAp0, config)
)
}
pragma[nomagic]
private predicate fwdFlowStore(
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
Configuration config
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc,
ParameterPositionOption summaryCtx, ApOption argAp, Configuration config
) {
exists(DataFlowType contentType |
fwdFlow(node1, state, cc, argAp, ap1, config) and
fwdFlow(node1, state, cc, summaryCtx, argAp, ap1, config) and
PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and
typecheckStore(ap1, contentType)
)
@@ -1366,7 +1427,7 @@ private module MkStage<StageSig PrevStage> {
pragma[nomagic]
private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) {
exists(TypedContent tc |
fwdFlowStore(_, tail, tc, _, _, _, _, config) and
fwdFlowStore(_, tail, tc, _, _, _, _, _, config) and
tc.getContent() = c and
cons = apCons(tc, tail)
)
@@ -1374,21 +1435,21 @@ private module MkStage<StageSig PrevStage> {
pragma[nomagic]
private predicate fwdFlowRead(
Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
Configuration config
Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc,
ParameterPositionOption summaryCtx, ApOption argAp, Configuration config
) {
fwdFlow(node1, state, cc, argAp, ap, config) and
fwdFlow(node1, state, cc, summaryCtx, argAp, ap, config) and
PrevStage::readStepCand(node1, c, node2, config) and
getHeadContent(ap) = c
}
pragma[nomagic]
private predicate fwdFlowIn(
DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, ApOption argAp,
Ap ap, Configuration config
DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc,
ParameterPositionOption summaryCtx, ApOption argAp, Ap ap, Configuration config
) {
exists(ArgNodeEx arg, boolean allowsFieldFlow |
fwdFlow(arg, state, outercc, argAp, ap, config) and
fwdFlow(arg, state, outercc, summaryCtx, argAp, ap, config) and
flowIntoCall(call, arg, p, allowsFieldFlow, config) and
innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
@@ -1396,29 +1457,19 @@ private module MkStage<StageSig PrevStage> {
}
pragma[nomagic]
private predicate fwdFlowOutNotFromArg(
NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config
private predicate fwdFlowOutFromArg(
DataFlowCall call, NodeEx out, FlowState state, ParameterPosition summaryCtx, Ap argAp, Ap ap,
Configuration config
) {
exists(
DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc,
DataFlowCallable inner
DataFlowCallable c, RetNodeEx ret, ReturnKindExt kind, boolean allowsFieldFlow, CcCall ccc
|
fwdFlow(ret, state, innercc, argAp, ap, config) and
flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
inner = ret.getEnclosingCallable() and
ccOut = getCallContextReturn(inner, call, innercc) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
)
}
pragma[nomagic]
private predicate fwdFlowOutFromArg(
DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config
) {
exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc |
fwdFlow(ret, state, ccc, apSome(argAp), ap, config) and
flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
fwdFlow(pragma[only_bind_into](ret), state, pragma[only_bind_into](ccc),
TParameterPositionSome(pragma[only_bind_into](summaryCtx)), apSome(argAp), ap, config) and
flowThroughOutOfCall(call, pragma[only_bind_into](c), ccc, ret, kind, out, allowsFieldFlow,
config) and
(if allowsFieldFlow = false then ap instanceof ApNil else any()) and
parameterFlowThroughAllowed(c, pragma[only_bind_into](summaryCtx), kind)
)
}
@@ -1428,11 +1479,13 @@ private module MkStage<StageSig PrevStage> {
*/
pragma[nomagic]
private predicate fwdFlowIsEntered(
DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config
DataFlowCall call, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
ParameterPosition pos, Ap ap, Configuration config
) {
exists(ParamNodeEx p |
fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and
PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config)
exists(ParamNodeEx param |
fwdFlowIn(call, param, _, cc, _, summaryCtx, argAp, ap, config) and
PrevStage::parameterMayFlowThrough(param, unbindApa(getApprox(ap)), config) and
pos = param.getPosition()
)
}
@@ -1440,27 +1493,40 @@ private module MkStage<StageSig PrevStage> {
private predicate storeStepFwd(
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config
) {
fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and
fwdFlowStore(node1, ap1, tc, node2, _, _, _, _, config) and
ap2 = apCons(tc, ap1) and
fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, config)
fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, _, config)
}
private predicate readStepFwd(
NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config
) {
fwdFlowRead(ap1, c, n1, n2, _, _, _, config) and
fwdFlowRead(ap1, c, n1, n2, _, _, _, _, config) and
fwdFlowConsCand(ap1, c, ap2, config)
}
pragma[nomagic]
private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) {
exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap |
fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
private predicate returnFlowsThrough0(
RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, DataFlowCallable c,
ParameterPosition ppos, Ap argAp, Ap ap, Configuration config
) {
exists(boolean allowsFieldFlow |
fwdFlow(ret, state, ccc, TParameterPositionSome(ppos), apSome(argAp), ap, config) and
flowThroughOutOfCall(_, c, _, pragma[only_bind_into](ret), kind, _, allowsFieldFlow,
pragma[only_bind_into](config)) and
fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and
fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc),
pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0),
pragma[only_bind_into](config))
if allowsFieldFlow = false then ap instanceof ApNil else any()
)
}
pragma[nomagic]
private predicate returnFlowsThrough(
RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, ParamNodeEx p, Ap argAp,
Ap ap, Configuration config
) {
exists(DataFlowCallable c, ParameterPosition ppos |
returnFlowsThrough0(ret, kind, state, ccc, c, ppos, argAp, ap, config) and
p.isParameterOf(c, ppos) and
parameterFlowThroughAllowed(p, kind)
)
}
@@ -1468,118 +1534,130 @@ private module MkStage<StageSig PrevStage> {
private predicate flowThroughIntoCall(
DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
) {
flowIntoCall(call, arg, p, allowsFieldFlow, config) and
fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and
PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and
callMayFlowThroughFwd(call, pragma[only_bind_into](config))
}
pragma[nomagic]
private predicate returnNodeMayFlowThrough(
RetNodeEx ret, FlowState state, Ap ap, Configuration config
) {
fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, config)
exists(Ap argAp |
flowIntoCall(call, pragma[only_bind_into](arg), pragma[only_bind_into](p), allowsFieldFlow,
pragma[only_bind_into](config)) and
fwdFlow(arg, _, _, _, _, pragma[only_bind_into](argAp), pragma[only_bind_into](config)) and
returnFlowsThrough(_, _, _, _, p, pragma[only_bind_into](argAp), _,
pragma[only_bind_into](config))
)
}
/**
* Holds if `node` with access path `ap` is part of a path from a source to a
* sink in the configuration `config`.
*
* The Boolean `toReturn` records whether the node must be returned from the
* enclosing callable in order to reach a sink, and if so, `returnAp` records
* the access path of the returned value.
* The parameter `returnCtx` records whether (and how) the node must be returned
* from the enclosing callable in order to reach a sink, and if so, `returnAp`
* records the access path of the returned value.
*/
pragma[nomagic]
additional predicate revFlow(
NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
NodeEx node, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap,
Configuration config
) {
revFlow0(node, state, toReturn, returnAp, ap, config) and
fwdFlow(node, state, _, _, ap, config)
revFlow0(node, state, returnCtx, returnAp, ap, config) and
fwdFlow(node, state, _, _, _, ap, config)
}
pragma[nomagic]
private predicate revFlow0(
NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
NodeEx node, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap,
Configuration config
) {
fwdFlow(node, state, _, _, ap, config) and
fwdFlow(node, state, _, _, _, ap, config) and
sinkNode(node, state, config) and
(if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and
(
if hasSinkCallCtx(config)
then returnCtx = TReturnCtxNoFlowThrough()
else returnCtx = TReturnCtxNone()
) and
returnAp = apNone() and
ap instanceof ApNil
or
exists(NodeEx mid, FlowState state0 |
localStep(node, state, mid, state0, true, _, config, _) and
revFlow(mid, state0, toReturn, returnAp, ap, config)
revFlow(mid, state0, returnCtx, returnAp, ap, config)
)
or
exists(NodeEx mid, FlowState state0, ApNil nil |
fwdFlow(node, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and
fwdFlow(node, pragma[only_bind_into](state), _, _, _, ap, pragma[only_bind_into](config)) and
localStep(node, pragma[only_bind_into](state), mid, state0, false, _, config, _) and
revFlow(mid, state0, toReturn, returnAp, nil, pragma[only_bind_into](config)) and
revFlow(mid, state0, returnCtx, returnAp, nil, pragma[only_bind_into](config)) and
ap instanceof ApNil
)
or
exists(NodeEx mid |
jumpStep(node, mid, config) and
revFlow(mid, state, _, _, ap, config) and
toReturn = false and
returnCtx = TReturnCtxNone() and
returnAp = apNone()
)
or
exists(NodeEx mid, ApNil nil |
fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and
fwdFlow(node, _, _, _, _, ap, pragma[only_bind_into](config)) and
additionalJumpStep(node, mid, config) and
revFlow(pragma[only_bind_into](mid), state, _, _, nil, pragma[only_bind_into](config)) and
toReturn = false and
returnCtx = TReturnCtxNone() and
returnAp = apNone() and
ap instanceof ApNil
)
or
exists(NodeEx mid, FlowState state0, ApNil nil |
fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and
fwdFlow(node, _, _, _, _, ap, pragma[only_bind_into](config)) and
additionalJumpStateStep(node, state, mid, state0, config) and
revFlow(pragma[only_bind_into](mid), pragma[only_bind_into](state0), _, _, nil,
pragma[only_bind_into](config)) and
toReturn = false and
returnCtx = TReturnCtxNone() and
returnAp = apNone() and
ap instanceof ApNil
)
or
// store
exists(Ap ap0, Content c |
revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and
revFlowStore(ap0, c, ap, node, state, _, _, returnCtx, returnAp, config) and
revFlowConsCand(ap0, c, ap, config)
)
or
// read
exists(NodeEx mid, Ap ap0 |
revFlow(mid, state, toReturn, returnAp, ap0, config) and
revFlow(mid, state, returnCtx, returnAp, ap0, config) and
readStepFwd(node, ap, _, mid, ap0, config)
)
or
// flow into a callable
revFlowInNotToReturn(node, state, returnAp, ap, config) and
toReturn = false
exists(ParamNodeEx p, boolean allowsFieldFlow |
revFlow(p, state, TReturnCtxNone(), returnAp, ap, config) and
flowIntoCall(_, node, p, allowsFieldFlow, config) and
(if allowsFieldFlow = false then ap instanceof ApNil else any()) and
returnCtx = TReturnCtxNone()
)
or
exists(DataFlowCall call, Ap returnAp0 |
revFlowInToReturn(call, node, state, returnAp0, ap, config) and
revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
// flow through a callable
exists(DataFlowCall call, ReturnKindExt returnKind0, Ap returnAp0 |
revFlowInToReturn(call, node, state, returnKind0, returnAp0, ap, config) and
revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config)
)
or
// flow out of a callable
revFlowOut(_, node, state, _, _, ap, config) and
toReturn = true and
if returnNodeMayFlowThrough(node, state, ap, config)
then returnAp = apSome(ap)
else returnAp = apNone()
exists(ReturnKindExt kind |
revFlowOut(_, node, kind, state, _, _, ap, config) and
if returnFlowsThrough(node, kind, state, _, _, _, ap, config)
then (
returnCtx = TReturnCtxMaybeFlowThrough(kind) and
returnAp = apSome(ap)
) else (
returnCtx = TReturnCtxNoFlowThrough() and returnAp = apNone()
)
)
}
pragma[nomagic]
private predicate revFlowStore(
Ap ap0, Content c, Ap ap, NodeEx node, FlowState state, TypedContent tc, NodeEx mid,
boolean toReturn, ApOption returnAp, Configuration config
ReturnCtx returnCtx, ApOption returnAp, Configuration config
) {
revFlow(mid, state, toReturn, returnAp, ap0, config) and
revFlow(mid, state, returnCtx, returnAp, ap0, config) and
storeStepFwd(node, ap, tc, mid, ap0, config) and
tc.getContent() = c
}
@@ -1599,35 +1677,27 @@ private module MkStage<StageSig PrevStage> {
pragma[nomagic]
private predicate revFlowOut(
DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, ApOption returnAp, Ap ap,
Configuration config
DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, FlowState state, ReturnCtx returnCtx,
ApOption returnAp, Ap ap, Configuration config
) {
exists(NodeEx out, boolean allowsFieldFlow |
revFlow(out, state, toReturn, returnAp, ap, config) and
flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
)
}
pragma[nomagic]
private predicate revFlowInNotToReturn(
ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config
) {
exists(ParamNodeEx p, boolean allowsFieldFlow |
revFlow(p, state, false, returnAp, ap, config) and
flowIntoCall(_, arg, p, allowsFieldFlow, config) and
revFlow(out, state, returnCtx, returnAp, ap, config) and
flowOutOfCall(call, ret, kind, out, allowsFieldFlow, config) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
)
}
pragma[nomagic]
private predicate revFlowInToReturn(
DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config
DataFlowCall call, ArgNodeEx arg, FlowState state, ReturnKindExt kind, Ap returnAp, Ap ap,
Configuration config
) {
exists(ParamNodeEx p, boolean allowsFieldFlow |
revFlow(p, state, true, apSome(returnAp), ap, config) and
revFlow(pragma[only_bind_into](p), state,
TReturnCtxMaybeFlowThrough(pragma[only_bind_into](kind)), apSome(returnAp), ap, config) and
flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
(if allowsFieldFlow = false then ap instanceof ApNil else any()) and
parameterFlowThroughAllowed(p, kind)
)
}
@@ -1638,11 +1708,12 @@ private module MkStage<StageSig PrevStage> {
*/
pragma[nomagic]
private predicate revFlowIsReturned(
DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
DataFlowCall call, ReturnCtx returnCtx, ApOption returnAp, ReturnKindExt kind, Ap ap,
Configuration config
) {
exists(RetNodeEx ret, FlowState state, CcCall ccc |
revFlowOut(call, ret, state, toReturn, returnAp, ap, config) and
fwdFlow(ret, state, ccc, apSome(_), ap, config) and
revFlowOut(call, ret, kind, state, returnCtx, returnAp, ap, config) and
returnFlowsThrough(ret, kind, state, ccc, _, _, ap, config) and
matchesCall(ccc, call)
)
}
@@ -1713,40 +1784,39 @@ private module MkStage<StageSig PrevStage> {
validAp(ap, config)
}
pragma[noinline]
private predicate parameterFlow(
ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
pragma[nomagic]
private predicate parameterFlowsThroughRev(
ParamNodeEx p, Ap ap, ReturnKindExt kind, Configuration config
) {
revFlow(p, _, true, apSome(ap0), ap, config) and
c = p.getEnclosingCallable()
revFlow(p, _, TReturnCtxMaybeFlowThrough(kind), apSome(_), ap, config) and
parameterFlowThroughAllowed(p, kind)
}
predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) {
exists(RetNodeEx ret, FlowState state, Ap ap0, ReturnKindExt kind, ParameterPosition pos |
parameterFlow(p, ap, ap0, c, config) and
c = ret.getEnclosingCallable() and
revFlow(pragma[only_bind_into](ret), pragma[only_bind_into](state), true, apSome(_),
pragma[only_bind_into](ap0), pragma[only_bind_into](config)) and
fwdFlow(ret, state, any(CcCall ccc), apSome(ap), ap0, config) and
kind = ret.getKind() and
p.getPosition() = pos and
// we don't expect a parameter to return stored in itself, unless explicitly allowed
(
not kind.(ParamUpdateReturnKind).getPosition() = pos
or
p.allowParameterReturnInSelf()
)
pragma[nomagic]
predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) {
exists(RetNodeEx ret, ReturnKindExt kind |
returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and
parameterFlowsThroughRev(p, ap, kind, config)
)
}
pragma[nomagic]
predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) {
exists(ParamNodeEx p, Ap ap |
returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and
parameterFlowsThroughRev(p, ap, kind, config)
)
}
pragma[nomagic]
predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) {
exists(
Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap
ReturnKindExt returnKind0, Ap returnAp0, ArgNodeEx arg, FlowState state,
ReturnCtx returnCtx, ApOption returnAp, Ap ap
|
revFlow(arg, state, toReturn, returnAp, ap, config) and
revFlowInToReturn(call, arg, state, returnAp0, ap, config) and
revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
revFlow(arg, state, returnCtx, returnAp, ap, config) and
revFlowInToReturn(call, arg, state, returnKind0, returnAp0, ap, config) and
revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config)
)
}
@@ -1754,14 +1824,13 @@ private module MkStage<StageSig PrevStage> {
boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config
) {
fwd = true and
nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, config)) and
nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, _, config)) and
fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and
conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and
states = count(FlowState state | fwdFlow(_, state, _, _, _, config)) and
states = count(FlowState state | fwdFlow(_, state, _, _, _, _, config)) and
tuples =
count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap |
fwdFlow(n, state, cc, argAp, ap, config)
)
count(NodeEx n, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
Ap ap | fwdFlow(n, state, cc, summaryCtx, argAp, ap, config))
or
fwd = false and
nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and
@@ -1769,8 +1838,8 @@ private module MkStage<StageSig PrevStage> {
conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and
states = count(FlowState state | revFlow(_, state, _, _, _, config)) and
tuples =
count(NodeEx n, FlowState state, boolean b, ApOption retAp, Ap ap |
revFlow(n, state, b, retAp, ap, config)
count(NodeEx n, FlowState state, ReturnCtx returnCtx, ApOption retAp, Ap ap |
revFlow(n, state, returnCtx, retAp, ap, config)
)
}
/* End: Stage logic. */
@@ -1915,7 +1984,7 @@ private module Stage2Param implements MkStage<Stage1>::StageParam {
exists(lcc)
}
predicate flowOutOfCall = flowOutOfCallNodeCand1/5;
predicate flowOutOfCall = flowOutOfCallNodeCand1/6;
predicate flowIntoCall = flowIntoCallNodeCand1/5;
@@ -1951,9 +2020,10 @@ private module Stage2 implements StageSig {
pragma[nomagic]
private predicate flowOutOfCallNodeCand2(
DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config
DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow,
Configuration config
) {
flowOutOfCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and
flowOutOfCallNodeCand1(call, node1, kind, node2, allowsFieldFlow, config) and
Stage2::revFlow(node2, pragma[only_bind_into](config)) and
Stage2::revFlowAlias(node1, pragma[only_bind_into](config))
}
@@ -2021,8 +2091,8 @@ private module LocalFlowBigStep {
exists(NodeEx next | Stage2::revFlow(next, state, config) |
jumpStep(node, next, config) or
additionalJumpStep(node, next, config) or
flowIntoCallNodeCand1(_, node, next, config) or
flowOutOfCallNodeCand1(_, node, next, config) or
flowIntoCallNodeCand2(_, node, next, _, config) or
flowOutOfCallNodeCand2(_, node, _, next, _, config) or
Stage2::storeStepCand(node, _, _, next, _, config) or
Stage2::readStepCand(node, _, next, config)
)
@@ -2163,7 +2233,7 @@ private module Stage3Param implements MkStage<Stage2>::StageParam {
localFlowBigStep(node1, state1, node2, state2, preservesValue, ap, config, _) and exists(lcc)
}
predicate flowOutOfCall = flowOutOfCallNodeCand2/5;
predicate flowOutOfCall = flowOutOfCallNodeCand2/6;
predicate flowIntoCall = flowIntoCallNodeCand2/5;
@@ -2233,8 +2303,9 @@ private predicate flowCandSummaryCtx(
NodeEx node, FlowState state, AccessPathFront argApf, Configuration config
) {
exists(AccessPathFront apf |
Stage3::revFlow(node, state, true, _, apf, config) and
Stage3::fwdFlow(node, state, any(Stage3::CcCall ccc), TAccessPathFrontSome(argApf), apf, config)
Stage3::revFlow(node, state, TReturnCtxMaybeFlowThrough(_), _, apf, config) and
Stage3::fwdFlow(node, state, any(Stage3::CcCall ccc), _, TAccessPathFrontSome(argApf), apf,
config)
)
}
@@ -2468,10 +2539,11 @@ private module Stage4Param implements MkStage<Stage3>::StageParam {
pragma[nomagic]
predicate flowOutOfCall(
DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config
DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow,
Configuration config
) {
exists(FlowState state |
flowOutOfCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and
flowOutOfCallNodeCand2(call, node1, kind, node2, allowsFieldFlow, config) and
PrevStage::revFlow(node2, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) and
PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _,
pragma[only_bind_into](config))
@@ -2508,13 +2580,14 @@ private Configuration unbindConf(Configuration conf) {
pragma[nomagic]
private predicate nodeMayUseSummary0(
NodeEx n, DataFlowCallable c, FlowState state, AccessPathApprox apa, Configuration config
NodeEx n, DataFlowCallable c, ParameterPosition pos, FlowState state, AccessPathApprox apa,
Configuration config
) {
exists(AccessPathApprox apa0 |
Stage4::parameterMayFlowThrough(_, c, _, _) and
Stage4::revFlow(n, state, true, _, apa0, config) and
Stage4::fwdFlow(n, state, any(CallContextCall ccc), TAccessPathApproxSome(apa), apa0, config) and
n.getEnclosingCallable() = c
c = n.getEnclosingCallable() and
Stage4::revFlow(n, state, TReturnCtxMaybeFlowThrough(_), _, apa0, config) and
Stage4::fwdFlow(n, state, any(CallContextCall ccc), TParameterPositionSome(pos),
TAccessPathApproxSome(apa), apa0, config)
)
}
@@ -2522,9 +2595,10 @@ pragma[nomagic]
private predicate nodeMayUseSummary(
NodeEx n, FlowState state, AccessPathApprox apa, Configuration config
) {
exists(DataFlowCallable c |
Stage4::parameterMayFlowThrough(_, c, apa, config) and
nodeMayUseSummary0(n, c, state, apa, config)
exists(DataFlowCallable c, ParameterPosition pos, ParamNodeEx p |
Stage4::parameterMayFlowThrough(p, apa, config) and
nodeMayUseSummary0(n, c, pos, state, apa, config) and
p.isParameterOf(c, pos)
)
}
@@ -2532,7 +2606,7 @@ private newtype TSummaryCtx =
TSummaryCtxNone() or
TSummaryCtxSome(ParamNodeEx p, FlowState state, AccessPath ap) {
exists(Configuration config |
Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), config) and
Stage4::parameterMayFlowThrough(p, ap.getApprox(), config) and
Stage4::revFlow(p, state, _, config)
)
}
@@ -3453,17 +3527,11 @@ private predicate paramFlowsThrough(
ReturnKindExt kind, FlowState state, CallContextCall cc, SummaryCtxSome sc, AccessPath ap,
AccessPathApprox apa, Configuration config
) {
exists(PathNodeMid mid, RetNodeEx ret, ParameterPosition pos |
exists(PathNodeMid mid, RetNodeEx ret |
pathNode(mid, ret, state, cc, sc, ap, config, _) and
kind = ret.getKind() and
apa = ap.getApprox() and
pos = sc.getParameterPos() and
// we don't expect a parameter to return stored in itself, unless explicitly allowed
(
not kind.(ParamUpdateReturnKind).getPosition() = pos
or
sc.getParamNode().allowParameterReturnInSelf()
)
parameterFlowThroughAllowed(sc.getParamNode(), kind)
)
}

View File

@@ -70,7 +70,7 @@ abstract class Configuration extends string {
/**
* Holds if `sink` is a relevant data flow sink accepting `state`.
*/
predicate isSink(Node source, FlowState state) { none() }
predicate isSink(Node sink, FlowState state) { none() }
/**
* Holds if data flow through `node` is prohibited. This completely removes
@@ -319,8 +319,6 @@ private class ParamNodeEx extends NodeEx {
}
ParameterPosition getPosition() { this.isParameterOf(_, result) }
predicate allowParameterReturnInSelf() { allowParameterReturnInSelfCached(this.asNode()) }
}
private class RetNodeEx extends NodeEx {
@@ -608,6 +606,38 @@ private predicate hasSinkCallCtx(Configuration config) {
)
}
/**
* Holds if flow from `p` to a return node of kind `kind` is allowed.
*
* We don't expect a parameter to return stored in itself, unless
* explicitly allowed
*/
bindingset[p, kind]
private predicate parameterFlowThroughAllowed(ParamNodeEx p, ReturnKindExt kind) {
exists(ParameterPosition pos | p.isParameterOf(_, pos) |
not kind.(ParamUpdateReturnKind).getPosition() = pos
or
allowParameterReturnInSelfCached(p.asNode())
)
}
/**
* Holds if flow from a parameter at position `pos` inside `c` to a return node of
* kind `kind` is allowed.
*
* We don't expect a parameter to return stored in itself, unless
* explicitly allowed
*/
bindingset[c, pos, kind]
private predicate parameterFlowThroughAllowed(
DataFlowCallable c, ParameterPosition pos, ReturnKindExt kind
) {
exists(ParamNodeEx p |
p.isParameterOf(c, pos) and
parameterFlowThroughAllowed(p, kind)
)
}
private module Stage1 implements StageSig {
class Ap = Unit;
@@ -981,21 +1011,22 @@ private module Stage1 implements StageSig {
* candidate for the origin of a summary.
*/
pragma[nomagic]
predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) {
exists(ReturnKindExt kind |
predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) {
exists(DataFlowCallable c, ReturnKindExt kind |
throughFlowNodeCand(p, config) and
returnFlowCallableNodeCand(c, kind, config) and
p.getEnclosingCallable() = c and
exists(ap) and
// we don't expect a parameter to return stored in itself, unless explicitly allowed
(
not kind.(ParamUpdateReturnKind).getPosition() = p.getPosition()
or
p.allowParameterReturnInSelf()
)
parameterFlowThroughAllowed(p, kind)
)
}
pragma[nomagic]
predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) {
throughFlowNodeCand(ret, config) and
kind = ret.getKind()
}
pragma[nomagic]
predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) {
exists(ArgNodeEx arg, boolean toReturn |
@@ -1052,12 +1083,16 @@ private predicate viableReturnPosOutNodeCand1(
*/
pragma[nomagic]
private predicate flowOutOfCallNodeCand1(
DataFlowCall call, RetNodeEx ret, NodeEx out, Configuration config
DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, Configuration config
) {
viableReturnPosOutNodeCand1(call, ret.getReturnPosition(), out, config) and
Stage1::revFlow(ret, config) and
not outBarrier(ret, config) and
not inBarrier(out, config)
exists(ReturnPosition pos |
viableReturnPosOutNodeCand1(call, pos, out, config) and
pos = ret.getReturnPosition() and
kind = pos.getKind() and
Stage1::revFlow(ret, config) and
not outBarrier(ret, config) and
not inBarrier(out, config)
)
}
pragma[nomagic]
@@ -1087,10 +1122,11 @@ private predicate flowIntoCallNodeCand1(
* edge in the graph of paths between sources and sinks that ignores call
* contexts.
*/
pragma[nomagic]
private int branch(NodeEx n1, Configuration conf) {
result =
strictcount(NodeEx n |
flowOutOfCallNodeCand1(_, n1, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf)
flowOutOfCallNodeCand1(_, n1, _, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf)
)
}
@@ -1099,10 +1135,11 @@ private int branch(NodeEx n1, Configuration conf) {
* edge in the graph of paths between sources and sinks that ignores call
* contexts.
*/
pragma[nomagic]
private int join(NodeEx n2, Configuration conf) {
result =
strictcount(NodeEx n |
flowOutOfCallNodeCand1(_, n, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf)
flowOutOfCallNodeCand1(_, n, _, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf)
)
}
@@ -1115,12 +1152,13 @@ private int join(NodeEx n2, Configuration conf) {
*/
pragma[nomagic]
private predicate flowOutOfCallNodeCand1(
DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config
DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow,
Configuration config
) {
flowOutOfCallNodeCand1(call, ret, out, config) and
flowOutOfCallNodeCand1(call, ret, kind, out, pragma[only_bind_into](config)) and
exists(int b, int j |
b = branch(ret, config) and
j = join(out, config) and
b = branch(ret, pragma[only_bind_into](config)) and
j = join(out, pragma[only_bind_into](config)) and
if b.minimum(j) <= config.fieldFlowBranchLimit()
then allowsFieldFlow = true
else allowsFieldFlow = false
@@ -1136,10 +1174,10 @@ pragma[nomagic]
private predicate flowIntoCallNodeCand1(
DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
) {
flowIntoCallNodeCand1(call, arg, p, config) and
flowIntoCallNodeCand1(call, arg, p, pragma[only_bind_into](config)) and
exists(int b, int j |
b = branch(arg, config) and
j = join(p, config) and
b = branch(arg, pragma[only_bind_into](config)) and
j = join(p, pragma[only_bind_into](config)) and
if b.minimum(j) <= config.fieldFlowBranchLimit()
then allowsFieldFlow = true
else allowsFieldFlow = false
@@ -1156,7 +1194,9 @@ private signature module StageSig {
predicate callMayFlowThroughRev(DataFlowCall call, Configuration config);
predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config);
predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config);
predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config);
predicate storeStepCand(
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType,
@@ -1222,7 +1262,8 @@ private module MkStage<StageSig PrevStage> {
);
predicate flowOutOfCall(
DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config
DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow,
Configuration config
);
predicate flowIntoCall(
@@ -1247,14 +1288,14 @@ private module MkStage<StageSig PrevStage> {
pragma[nomagic]
private predicate flowThroughOutOfCall(
DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow,
Configuration config
DataFlowCall call, DataFlowCallable c, CcCall ccc, RetNodeEx ret, ReturnKindExt kind,
NodeEx out, boolean allowsFieldFlow, Configuration config
) {
flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and
flowOutOfCall(call, ret, kind, out, allowsFieldFlow, pragma[only_bind_into](config)) and
PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and
PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _,
pragma[only_bind_into](config)) and
matchesCall(ccc, call)
PrevStage::returnMayFlowThrough(ret, kind, pragma[only_bind_into](config)) and
matchesCall(ccc, call) and
c = ret.getEnclosingCallable()
}
/**
@@ -1262,29 +1303,32 @@ private module MkStage<StageSig PrevStage> {
* configuration `config`.
*
* The call context `cc` records whether the node is reached through an
* argument in a call, and if so, `argAp` records the access path of that
* argument.
* argument in a call, and if so, `summaryCtx` and `argAp` record the
* corresponding parameter position and access path of that argument, respectively.
*/
pragma[nomagic]
additional predicate fwdFlow(
NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config
NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
Ap ap, Configuration config
) {
fwdFlow0(node, state, cc, argAp, ap, config) and
fwdFlow0(node, state, cc, summaryCtx, argAp, ap, config) and
PrevStage::revFlow(node, state, unbindApa(getApprox(ap)), config) and
filter(node, state, ap, config)
}
pragma[nomagic]
private predicate fwdFlow0(
NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config
NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
Ap ap, Configuration config
) {
sourceNode(node, state, config) and
(if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
argAp = apNone() and
summaryCtx = TParameterPositionNone() and
ap = getApNil(node)
or
exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc |
fwdFlow(mid, state0, cc, argAp, ap0, config) and
fwdFlow(mid, state0, cc, summaryCtx, argAp, ap0, config) and
localCc = getLocalCc(mid, cc)
|
localStep(mid, state0, node, state, true, _, config, localCc) and
@@ -1295,65 +1339,82 @@ private module MkStage<StageSig PrevStage> {
)
or
exists(NodeEx mid |
fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and
fwdFlow(mid, pragma[only_bind_into](state), _, _, _, ap, pragma[only_bind_into](config)) and
jumpStep(mid, node, config) and
cc = ccNone() and
summaryCtx = TParameterPositionNone() and
argAp = apNone()
)
or
exists(NodeEx mid, ApNil nil |
fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and
fwdFlow(mid, state, _, _, _, nil, pragma[only_bind_into](config)) and
additionalJumpStep(mid, node, config) and
cc = ccNone() and
summaryCtx = TParameterPositionNone() and
argAp = apNone() and
ap = getApNil(node)
)
or
exists(NodeEx mid, FlowState state0, ApNil nil |
fwdFlow(mid, state0, _, _, nil, pragma[only_bind_into](config)) and
fwdFlow(mid, state0, _, _, _, nil, pragma[only_bind_into](config)) and
additionalJumpStateStep(mid, state0, node, state, config) and
cc = ccNone() and
summaryCtx = TParameterPositionNone() and
argAp = apNone() and
ap = getApNil(node)
)
or
// store
exists(TypedContent tc, Ap ap0 |
fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and
fwdFlowStore(_, ap0, tc, node, state, cc, summaryCtx, argAp, config) and
ap = apCons(tc, ap0)
)
or
// read
exists(Ap ap0, Content c |
fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and
fwdFlowRead(ap0, c, _, node, state, cc, summaryCtx, argAp, config) and
fwdFlowConsCand(ap0, c, ap, config)
)
or
// flow into a callable
exists(ApApprox apa |
fwdFlowIn(_, node, state, _, cc, _, ap, config) and
fwdFlowIn(_, node, state, _, cc, _, _, ap, config) and
apa = getApprox(ap) and
if PrevStage::parameterMayFlowThrough(node, _, apa, config)
then argAp = apSome(ap)
else argAp = apNone()
if PrevStage::parameterMayFlowThrough(node, apa, config)
then (
summaryCtx = TParameterPositionSome(node.(ParamNodeEx).getPosition()) and
argAp = apSome(ap)
) else (
summaryCtx = TParameterPositionNone() and argAp = apNone()
)
)
or
// flow out of a callable
fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config)
exists(
DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc,
DataFlowCallable inner
|
fwdFlow(ret, state, innercc, summaryCtx, argAp, ap, config) and
flowOutOfCall(call, ret, _, node, allowsFieldFlow, config) and
inner = ret.getEnclosingCallable() and
cc = getCallContextReturn(inner, call, innercc) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
)
or
exists(DataFlowCall call, Ap argAp0 |
fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and
fwdFlowIsEntered(call, cc, argAp, argAp0, config)
// flow through a callable
exists(DataFlowCall call, ParameterPosition summaryCtx0, Ap argAp0 |
fwdFlowOutFromArg(call, node, state, summaryCtx0, argAp0, ap, config) and
fwdFlowIsEntered(call, cc, summaryCtx, argAp, summaryCtx0, argAp0, config)
)
}
pragma[nomagic]
private predicate fwdFlowStore(
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
Configuration config
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc,
ParameterPositionOption summaryCtx, ApOption argAp, Configuration config
) {
exists(DataFlowType contentType |
fwdFlow(node1, state, cc, argAp, ap1, config) and
fwdFlow(node1, state, cc, summaryCtx, argAp, ap1, config) and
PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and
typecheckStore(ap1, contentType)
)
@@ -1366,7 +1427,7 @@ private module MkStage<StageSig PrevStage> {
pragma[nomagic]
private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) {
exists(TypedContent tc |
fwdFlowStore(_, tail, tc, _, _, _, _, config) and
fwdFlowStore(_, tail, tc, _, _, _, _, _, config) and
tc.getContent() = c and
cons = apCons(tc, tail)
)
@@ -1374,21 +1435,21 @@ private module MkStage<StageSig PrevStage> {
pragma[nomagic]
private predicate fwdFlowRead(
Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
Configuration config
Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc,
ParameterPositionOption summaryCtx, ApOption argAp, Configuration config
) {
fwdFlow(node1, state, cc, argAp, ap, config) and
fwdFlow(node1, state, cc, summaryCtx, argAp, ap, config) and
PrevStage::readStepCand(node1, c, node2, config) and
getHeadContent(ap) = c
}
pragma[nomagic]
private predicate fwdFlowIn(
DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, ApOption argAp,
Ap ap, Configuration config
DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc,
ParameterPositionOption summaryCtx, ApOption argAp, Ap ap, Configuration config
) {
exists(ArgNodeEx arg, boolean allowsFieldFlow |
fwdFlow(arg, state, outercc, argAp, ap, config) and
fwdFlow(arg, state, outercc, summaryCtx, argAp, ap, config) and
flowIntoCall(call, arg, p, allowsFieldFlow, config) and
innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
@@ -1396,29 +1457,19 @@ private module MkStage<StageSig PrevStage> {
}
pragma[nomagic]
private predicate fwdFlowOutNotFromArg(
NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config
private predicate fwdFlowOutFromArg(
DataFlowCall call, NodeEx out, FlowState state, ParameterPosition summaryCtx, Ap argAp, Ap ap,
Configuration config
) {
exists(
DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc,
DataFlowCallable inner
DataFlowCallable c, RetNodeEx ret, ReturnKindExt kind, boolean allowsFieldFlow, CcCall ccc
|
fwdFlow(ret, state, innercc, argAp, ap, config) and
flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
inner = ret.getEnclosingCallable() and
ccOut = getCallContextReturn(inner, call, innercc) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
)
}
pragma[nomagic]
private predicate fwdFlowOutFromArg(
DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config
) {
exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc |
fwdFlow(ret, state, ccc, apSome(argAp), ap, config) and
flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
fwdFlow(pragma[only_bind_into](ret), state, pragma[only_bind_into](ccc),
TParameterPositionSome(pragma[only_bind_into](summaryCtx)), apSome(argAp), ap, config) and
flowThroughOutOfCall(call, pragma[only_bind_into](c), ccc, ret, kind, out, allowsFieldFlow,
config) and
(if allowsFieldFlow = false then ap instanceof ApNil else any()) and
parameterFlowThroughAllowed(c, pragma[only_bind_into](summaryCtx), kind)
)
}
@@ -1428,11 +1479,13 @@ private module MkStage<StageSig PrevStage> {
*/
pragma[nomagic]
private predicate fwdFlowIsEntered(
DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config
DataFlowCall call, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
ParameterPosition pos, Ap ap, Configuration config
) {
exists(ParamNodeEx p |
fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and
PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config)
exists(ParamNodeEx param |
fwdFlowIn(call, param, _, cc, _, summaryCtx, argAp, ap, config) and
PrevStage::parameterMayFlowThrough(param, unbindApa(getApprox(ap)), config) and
pos = param.getPosition()
)
}
@@ -1440,27 +1493,40 @@ private module MkStage<StageSig PrevStage> {
private predicate storeStepFwd(
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config
) {
fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and
fwdFlowStore(node1, ap1, tc, node2, _, _, _, _, config) and
ap2 = apCons(tc, ap1) and
fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, config)
fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, _, config)
}
private predicate readStepFwd(
NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config
) {
fwdFlowRead(ap1, c, n1, n2, _, _, _, config) and
fwdFlowRead(ap1, c, n1, n2, _, _, _, _, config) and
fwdFlowConsCand(ap1, c, ap2, config)
}
pragma[nomagic]
private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) {
exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap |
fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
private predicate returnFlowsThrough0(
RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, DataFlowCallable c,
ParameterPosition ppos, Ap argAp, Ap ap, Configuration config
) {
exists(boolean allowsFieldFlow |
fwdFlow(ret, state, ccc, TParameterPositionSome(ppos), apSome(argAp), ap, config) and
flowThroughOutOfCall(_, c, _, pragma[only_bind_into](ret), kind, _, allowsFieldFlow,
pragma[only_bind_into](config)) and
fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and
fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc),
pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0),
pragma[only_bind_into](config))
if allowsFieldFlow = false then ap instanceof ApNil else any()
)
}
pragma[nomagic]
private predicate returnFlowsThrough(
RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, ParamNodeEx p, Ap argAp,
Ap ap, Configuration config
) {
exists(DataFlowCallable c, ParameterPosition ppos |
returnFlowsThrough0(ret, kind, state, ccc, c, ppos, argAp, ap, config) and
p.isParameterOf(c, ppos) and
parameterFlowThroughAllowed(p, kind)
)
}
@@ -1468,118 +1534,130 @@ private module MkStage<StageSig PrevStage> {
private predicate flowThroughIntoCall(
DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
) {
flowIntoCall(call, arg, p, allowsFieldFlow, config) and
fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and
PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and
callMayFlowThroughFwd(call, pragma[only_bind_into](config))
}
pragma[nomagic]
private predicate returnNodeMayFlowThrough(
RetNodeEx ret, FlowState state, Ap ap, Configuration config
) {
fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, config)
exists(Ap argAp |
flowIntoCall(call, pragma[only_bind_into](arg), pragma[only_bind_into](p), allowsFieldFlow,
pragma[only_bind_into](config)) and
fwdFlow(arg, _, _, _, _, pragma[only_bind_into](argAp), pragma[only_bind_into](config)) and
returnFlowsThrough(_, _, _, _, p, pragma[only_bind_into](argAp), _,
pragma[only_bind_into](config))
)
}
/**
* Holds if `node` with access path `ap` is part of a path from a source to a
* sink in the configuration `config`.
*
* The Boolean `toReturn` records whether the node must be returned from the
* enclosing callable in order to reach a sink, and if so, `returnAp` records
* the access path of the returned value.
* The parameter `returnCtx` records whether (and how) the node must be returned
* from the enclosing callable in order to reach a sink, and if so, `returnAp`
* records the access path of the returned value.
*/
pragma[nomagic]
additional predicate revFlow(
NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
NodeEx node, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap,
Configuration config
) {
revFlow0(node, state, toReturn, returnAp, ap, config) and
fwdFlow(node, state, _, _, ap, config)
revFlow0(node, state, returnCtx, returnAp, ap, config) and
fwdFlow(node, state, _, _, _, ap, config)
}
pragma[nomagic]
private predicate revFlow0(
NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
NodeEx node, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap,
Configuration config
) {
fwdFlow(node, state, _, _, ap, config) and
fwdFlow(node, state, _, _, _, ap, config) and
sinkNode(node, state, config) and
(if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and
(
if hasSinkCallCtx(config)
then returnCtx = TReturnCtxNoFlowThrough()
else returnCtx = TReturnCtxNone()
) and
returnAp = apNone() and
ap instanceof ApNil
or
exists(NodeEx mid, FlowState state0 |
localStep(node, state, mid, state0, true, _, config, _) and
revFlow(mid, state0, toReturn, returnAp, ap, config)
revFlow(mid, state0, returnCtx, returnAp, ap, config)
)
or
exists(NodeEx mid, FlowState state0, ApNil nil |
fwdFlow(node, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and
fwdFlow(node, pragma[only_bind_into](state), _, _, _, ap, pragma[only_bind_into](config)) and
localStep(node, pragma[only_bind_into](state), mid, state0, false, _, config, _) and
revFlow(mid, state0, toReturn, returnAp, nil, pragma[only_bind_into](config)) and
revFlow(mid, state0, returnCtx, returnAp, nil, pragma[only_bind_into](config)) and
ap instanceof ApNil
)
or
exists(NodeEx mid |
jumpStep(node, mid, config) and
revFlow(mid, state, _, _, ap, config) and
toReturn = false and
returnCtx = TReturnCtxNone() and
returnAp = apNone()
)
or
exists(NodeEx mid, ApNil nil |
fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and
fwdFlow(node, _, _, _, _, ap, pragma[only_bind_into](config)) and
additionalJumpStep(node, mid, config) and
revFlow(pragma[only_bind_into](mid), state, _, _, nil, pragma[only_bind_into](config)) and
toReturn = false and
returnCtx = TReturnCtxNone() and
returnAp = apNone() and
ap instanceof ApNil
)
or
exists(NodeEx mid, FlowState state0, ApNil nil |
fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and
fwdFlow(node, _, _, _, _, ap, pragma[only_bind_into](config)) and
additionalJumpStateStep(node, state, mid, state0, config) and
revFlow(pragma[only_bind_into](mid), pragma[only_bind_into](state0), _, _, nil,
pragma[only_bind_into](config)) and
toReturn = false and
returnCtx = TReturnCtxNone() and
returnAp = apNone() and
ap instanceof ApNil
)
or
// store
exists(Ap ap0, Content c |
revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and
revFlowStore(ap0, c, ap, node, state, _, _, returnCtx, returnAp, config) and
revFlowConsCand(ap0, c, ap, config)
)
or
// read
exists(NodeEx mid, Ap ap0 |
revFlow(mid, state, toReturn, returnAp, ap0, config) and
revFlow(mid, state, returnCtx, returnAp, ap0, config) and
readStepFwd(node, ap, _, mid, ap0, config)
)
or
// flow into a callable
revFlowInNotToReturn(node, state, returnAp, ap, config) and
toReturn = false
exists(ParamNodeEx p, boolean allowsFieldFlow |
revFlow(p, state, TReturnCtxNone(), returnAp, ap, config) and
flowIntoCall(_, node, p, allowsFieldFlow, config) and
(if allowsFieldFlow = false then ap instanceof ApNil else any()) and
returnCtx = TReturnCtxNone()
)
or
exists(DataFlowCall call, Ap returnAp0 |
revFlowInToReturn(call, node, state, returnAp0, ap, config) and
revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
// flow through a callable
exists(DataFlowCall call, ReturnKindExt returnKind0, Ap returnAp0 |
revFlowInToReturn(call, node, state, returnKind0, returnAp0, ap, config) and
revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config)
)
or
// flow out of a callable
revFlowOut(_, node, state, _, _, ap, config) and
toReturn = true and
if returnNodeMayFlowThrough(node, state, ap, config)
then returnAp = apSome(ap)
else returnAp = apNone()
exists(ReturnKindExt kind |
revFlowOut(_, node, kind, state, _, _, ap, config) and
if returnFlowsThrough(node, kind, state, _, _, _, ap, config)
then (
returnCtx = TReturnCtxMaybeFlowThrough(kind) and
returnAp = apSome(ap)
) else (
returnCtx = TReturnCtxNoFlowThrough() and returnAp = apNone()
)
)
}
pragma[nomagic]
private predicate revFlowStore(
Ap ap0, Content c, Ap ap, NodeEx node, FlowState state, TypedContent tc, NodeEx mid,
boolean toReturn, ApOption returnAp, Configuration config
ReturnCtx returnCtx, ApOption returnAp, Configuration config
) {
revFlow(mid, state, toReturn, returnAp, ap0, config) and
revFlow(mid, state, returnCtx, returnAp, ap0, config) and
storeStepFwd(node, ap, tc, mid, ap0, config) and
tc.getContent() = c
}
@@ -1599,35 +1677,27 @@ private module MkStage<StageSig PrevStage> {
pragma[nomagic]
private predicate revFlowOut(
DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, ApOption returnAp, Ap ap,
Configuration config
DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, FlowState state, ReturnCtx returnCtx,
ApOption returnAp, Ap ap, Configuration config
) {
exists(NodeEx out, boolean allowsFieldFlow |
revFlow(out, state, toReturn, returnAp, ap, config) and
flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
)
}
pragma[nomagic]
private predicate revFlowInNotToReturn(
ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config
) {
exists(ParamNodeEx p, boolean allowsFieldFlow |
revFlow(p, state, false, returnAp, ap, config) and
flowIntoCall(_, arg, p, allowsFieldFlow, config) and
revFlow(out, state, returnCtx, returnAp, ap, config) and
flowOutOfCall(call, ret, kind, out, allowsFieldFlow, config) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
)
}
pragma[nomagic]
private predicate revFlowInToReturn(
DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config
DataFlowCall call, ArgNodeEx arg, FlowState state, ReturnKindExt kind, Ap returnAp, Ap ap,
Configuration config
) {
exists(ParamNodeEx p, boolean allowsFieldFlow |
revFlow(p, state, true, apSome(returnAp), ap, config) and
revFlow(pragma[only_bind_into](p), state,
TReturnCtxMaybeFlowThrough(pragma[only_bind_into](kind)), apSome(returnAp), ap, config) and
flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
(if allowsFieldFlow = false then ap instanceof ApNil else any()) and
parameterFlowThroughAllowed(p, kind)
)
}
@@ -1638,11 +1708,12 @@ private module MkStage<StageSig PrevStage> {
*/
pragma[nomagic]
private predicate revFlowIsReturned(
DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
DataFlowCall call, ReturnCtx returnCtx, ApOption returnAp, ReturnKindExt kind, Ap ap,
Configuration config
) {
exists(RetNodeEx ret, FlowState state, CcCall ccc |
revFlowOut(call, ret, state, toReturn, returnAp, ap, config) and
fwdFlow(ret, state, ccc, apSome(_), ap, config) and
revFlowOut(call, ret, kind, state, returnCtx, returnAp, ap, config) and
returnFlowsThrough(ret, kind, state, ccc, _, _, ap, config) and
matchesCall(ccc, call)
)
}
@@ -1713,40 +1784,39 @@ private module MkStage<StageSig PrevStage> {
validAp(ap, config)
}
pragma[noinline]
private predicate parameterFlow(
ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
pragma[nomagic]
private predicate parameterFlowsThroughRev(
ParamNodeEx p, Ap ap, ReturnKindExt kind, Configuration config
) {
revFlow(p, _, true, apSome(ap0), ap, config) and
c = p.getEnclosingCallable()
revFlow(p, _, TReturnCtxMaybeFlowThrough(kind), apSome(_), ap, config) and
parameterFlowThroughAllowed(p, kind)
}
predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) {
exists(RetNodeEx ret, FlowState state, Ap ap0, ReturnKindExt kind, ParameterPosition pos |
parameterFlow(p, ap, ap0, c, config) and
c = ret.getEnclosingCallable() and
revFlow(pragma[only_bind_into](ret), pragma[only_bind_into](state), true, apSome(_),
pragma[only_bind_into](ap0), pragma[only_bind_into](config)) and
fwdFlow(ret, state, any(CcCall ccc), apSome(ap), ap0, config) and
kind = ret.getKind() and
p.getPosition() = pos and
// we don't expect a parameter to return stored in itself, unless explicitly allowed
(
not kind.(ParamUpdateReturnKind).getPosition() = pos
or
p.allowParameterReturnInSelf()
)
pragma[nomagic]
predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) {
exists(RetNodeEx ret, ReturnKindExt kind |
returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and
parameterFlowsThroughRev(p, ap, kind, config)
)
}
pragma[nomagic]
predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) {
exists(ParamNodeEx p, Ap ap |
returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and
parameterFlowsThroughRev(p, ap, kind, config)
)
}
pragma[nomagic]
predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) {
exists(
Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap
ReturnKindExt returnKind0, Ap returnAp0, ArgNodeEx arg, FlowState state,
ReturnCtx returnCtx, ApOption returnAp, Ap ap
|
revFlow(arg, state, toReturn, returnAp, ap, config) and
revFlowInToReturn(call, arg, state, returnAp0, ap, config) and
revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
revFlow(arg, state, returnCtx, returnAp, ap, config) and
revFlowInToReturn(call, arg, state, returnKind0, returnAp0, ap, config) and
revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config)
)
}
@@ -1754,14 +1824,13 @@ private module MkStage<StageSig PrevStage> {
boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config
) {
fwd = true and
nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, config)) and
nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, _, config)) and
fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and
conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and
states = count(FlowState state | fwdFlow(_, state, _, _, _, config)) and
states = count(FlowState state | fwdFlow(_, state, _, _, _, _, config)) and
tuples =
count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap |
fwdFlow(n, state, cc, argAp, ap, config)
)
count(NodeEx n, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
Ap ap | fwdFlow(n, state, cc, summaryCtx, argAp, ap, config))
or
fwd = false and
nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and
@@ -1769,8 +1838,8 @@ private module MkStage<StageSig PrevStage> {
conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and
states = count(FlowState state | revFlow(_, state, _, _, _, config)) and
tuples =
count(NodeEx n, FlowState state, boolean b, ApOption retAp, Ap ap |
revFlow(n, state, b, retAp, ap, config)
count(NodeEx n, FlowState state, ReturnCtx returnCtx, ApOption retAp, Ap ap |
revFlow(n, state, returnCtx, retAp, ap, config)
)
}
/* End: Stage logic. */
@@ -1915,7 +1984,7 @@ private module Stage2Param implements MkStage<Stage1>::StageParam {
exists(lcc)
}
predicate flowOutOfCall = flowOutOfCallNodeCand1/5;
predicate flowOutOfCall = flowOutOfCallNodeCand1/6;
predicate flowIntoCall = flowIntoCallNodeCand1/5;
@@ -1951,9 +2020,10 @@ private module Stage2 implements StageSig {
pragma[nomagic]
private predicate flowOutOfCallNodeCand2(
DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config
DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow,
Configuration config
) {
flowOutOfCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and
flowOutOfCallNodeCand1(call, node1, kind, node2, allowsFieldFlow, config) and
Stage2::revFlow(node2, pragma[only_bind_into](config)) and
Stage2::revFlowAlias(node1, pragma[only_bind_into](config))
}
@@ -2021,8 +2091,8 @@ private module LocalFlowBigStep {
exists(NodeEx next | Stage2::revFlow(next, state, config) |
jumpStep(node, next, config) or
additionalJumpStep(node, next, config) or
flowIntoCallNodeCand1(_, node, next, config) or
flowOutOfCallNodeCand1(_, node, next, config) or
flowIntoCallNodeCand2(_, node, next, _, config) or
flowOutOfCallNodeCand2(_, node, _, next, _, config) or
Stage2::storeStepCand(node, _, _, next, _, config) or
Stage2::readStepCand(node, _, next, config)
)
@@ -2163,7 +2233,7 @@ private module Stage3Param implements MkStage<Stage2>::StageParam {
localFlowBigStep(node1, state1, node2, state2, preservesValue, ap, config, _) and exists(lcc)
}
predicate flowOutOfCall = flowOutOfCallNodeCand2/5;
predicate flowOutOfCall = flowOutOfCallNodeCand2/6;
predicate flowIntoCall = flowIntoCallNodeCand2/5;
@@ -2233,8 +2303,9 @@ private predicate flowCandSummaryCtx(
NodeEx node, FlowState state, AccessPathFront argApf, Configuration config
) {
exists(AccessPathFront apf |
Stage3::revFlow(node, state, true, _, apf, config) and
Stage3::fwdFlow(node, state, any(Stage3::CcCall ccc), TAccessPathFrontSome(argApf), apf, config)
Stage3::revFlow(node, state, TReturnCtxMaybeFlowThrough(_), _, apf, config) and
Stage3::fwdFlow(node, state, any(Stage3::CcCall ccc), _, TAccessPathFrontSome(argApf), apf,
config)
)
}
@@ -2468,10 +2539,11 @@ private module Stage4Param implements MkStage<Stage3>::StageParam {
pragma[nomagic]
predicate flowOutOfCall(
DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config
DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow,
Configuration config
) {
exists(FlowState state |
flowOutOfCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and
flowOutOfCallNodeCand2(call, node1, kind, node2, allowsFieldFlow, config) and
PrevStage::revFlow(node2, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) and
PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _,
pragma[only_bind_into](config))
@@ -2508,13 +2580,14 @@ private Configuration unbindConf(Configuration conf) {
pragma[nomagic]
private predicate nodeMayUseSummary0(
NodeEx n, DataFlowCallable c, FlowState state, AccessPathApprox apa, Configuration config
NodeEx n, DataFlowCallable c, ParameterPosition pos, FlowState state, AccessPathApprox apa,
Configuration config
) {
exists(AccessPathApprox apa0 |
Stage4::parameterMayFlowThrough(_, c, _, _) and
Stage4::revFlow(n, state, true, _, apa0, config) and
Stage4::fwdFlow(n, state, any(CallContextCall ccc), TAccessPathApproxSome(apa), apa0, config) and
n.getEnclosingCallable() = c
c = n.getEnclosingCallable() and
Stage4::revFlow(n, state, TReturnCtxMaybeFlowThrough(_), _, apa0, config) and
Stage4::fwdFlow(n, state, any(CallContextCall ccc), TParameterPositionSome(pos),
TAccessPathApproxSome(apa), apa0, config)
)
}
@@ -2522,9 +2595,10 @@ pragma[nomagic]
private predicate nodeMayUseSummary(
NodeEx n, FlowState state, AccessPathApprox apa, Configuration config
) {
exists(DataFlowCallable c |
Stage4::parameterMayFlowThrough(_, c, apa, config) and
nodeMayUseSummary0(n, c, state, apa, config)
exists(DataFlowCallable c, ParameterPosition pos, ParamNodeEx p |
Stage4::parameterMayFlowThrough(p, apa, config) and
nodeMayUseSummary0(n, c, pos, state, apa, config) and
p.isParameterOf(c, pos)
)
}
@@ -2532,7 +2606,7 @@ private newtype TSummaryCtx =
TSummaryCtxNone() or
TSummaryCtxSome(ParamNodeEx p, FlowState state, AccessPath ap) {
exists(Configuration config |
Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), config) and
Stage4::parameterMayFlowThrough(p, ap.getApprox(), config) and
Stage4::revFlow(p, state, _, config)
)
}
@@ -3453,17 +3527,11 @@ private predicate paramFlowsThrough(
ReturnKindExt kind, FlowState state, CallContextCall cc, SummaryCtxSome sc, AccessPath ap,
AccessPathApprox apa, Configuration config
) {
exists(PathNodeMid mid, RetNodeEx ret, ParameterPosition pos |
exists(PathNodeMid mid, RetNodeEx ret |
pathNode(mid, ret, state, cc, sc, ap, config, _) and
kind = ret.getKind() and
apa = ap.getApprox() and
pos = sc.getParameterPos() and
// we don't expect a parameter to return stored in itself, unless explicitly allowed
(
not kind.(ParamUpdateReturnKind).getPosition() = pos
or
sc.getParamNode().allowParameterReturnInSelf()
)
parameterFlowThroughAllowed(sc.getParamNode(), kind)
)
}

View File

@@ -70,7 +70,7 @@ abstract class Configuration extends string {
/**
* Holds if `sink` is a relevant data flow sink accepting `state`.
*/
predicate isSink(Node source, FlowState state) { none() }
predicate isSink(Node sink, FlowState state) { none() }
/**
* Holds if data flow through `node` is prohibited. This completely removes
@@ -319,8 +319,6 @@ private class ParamNodeEx extends NodeEx {
}
ParameterPosition getPosition() { this.isParameterOf(_, result) }
predicate allowParameterReturnInSelf() { allowParameterReturnInSelfCached(this.asNode()) }
}
private class RetNodeEx extends NodeEx {
@@ -608,6 +606,38 @@ private predicate hasSinkCallCtx(Configuration config) {
)
}
/**
* Holds if flow from `p` to a return node of kind `kind` is allowed.
*
* We don't expect a parameter to return stored in itself, unless
* explicitly allowed
*/
bindingset[p, kind]
private predicate parameterFlowThroughAllowed(ParamNodeEx p, ReturnKindExt kind) {
exists(ParameterPosition pos | p.isParameterOf(_, pos) |
not kind.(ParamUpdateReturnKind).getPosition() = pos
or
allowParameterReturnInSelfCached(p.asNode())
)
}
/**
* Holds if flow from a parameter at position `pos` inside `c` to a return node of
* kind `kind` is allowed.
*
* We don't expect a parameter to return stored in itself, unless
* explicitly allowed
*/
bindingset[c, pos, kind]
private predicate parameterFlowThroughAllowed(
DataFlowCallable c, ParameterPosition pos, ReturnKindExt kind
) {
exists(ParamNodeEx p |
p.isParameterOf(c, pos) and
parameterFlowThroughAllowed(p, kind)
)
}
private module Stage1 implements StageSig {
class Ap = Unit;
@@ -981,21 +1011,22 @@ private module Stage1 implements StageSig {
* candidate for the origin of a summary.
*/
pragma[nomagic]
predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) {
exists(ReturnKindExt kind |
predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) {
exists(DataFlowCallable c, ReturnKindExt kind |
throughFlowNodeCand(p, config) and
returnFlowCallableNodeCand(c, kind, config) and
p.getEnclosingCallable() = c and
exists(ap) and
// we don't expect a parameter to return stored in itself, unless explicitly allowed
(
not kind.(ParamUpdateReturnKind).getPosition() = p.getPosition()
or
p.allowParameterReturnInSelf()
)
parameterFlowThroughAllowed(p, kind)
)
}
pragma[nomagic]
predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) {
throughFlowNodeCand(ret, config) and
kind = ret.getKind()
}
pragma[nomagic]
predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) {
exists(ArgNodeEx arg, boolean toReturn |
@@ -1052,12 +1083,16 @@ private predicate viableReturnPosOutNodeCand1(
*/
pragma[nomagic]
private predicate flowOutOfCallNodeCand1(
DataFlowCall call, RetNodeEx ret, NodeEx out, Configuration config
DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, Configuration config
) {
viableReturnPosOutNodeCand1(call, ret.getReturnPosition(), out, config) and
Stage1::revFlow(ret, config) and
not outBarrier(ret, config) and
not inBarrier(out, config)
exists(ReturnPosition pos |
viableReturnPosOutNodeCand1(call, pos, out, config) and
pos = ret.getReturnPosition() and
kind = pos.getKind() and
Stage1::revFlow(ret, config) and
not outBarrier(ret, config) and
not inBarrier(out, config)
)
}
pragma[nomagic]
@@ -1087,10 +1122,11 @@ private predicate flowIntoCallNodeCand1(
* edge in the graph of paths between sources and sinks that ignores call
* contexts.
*/
pragma[nomagic]
private int branch(NodeEx n1, Configuration conf) {
result =
strictcount(NodeEx n |
flowOutOfCallNodeCand1(_, n1, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf)
flowOutOfCallNodeCand1(_, n1, _, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf)
)
}
@@ -1099,10 +1135,11 @@ private int branch(NodeEx n1, Configuration conf) {
* edge in the graph of paths between sources and sinks that ignores call
* contexts.
*/
pragma[nomagic]
private int join(NodeEx n2, Configuration conf) {
result =
strictcount(NodeEx n |
flowOutOfCallNodeCand1(_, n, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf)
flowOutOfCallNodeCand1(_, n, _, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf)
)
}
@@ -1115,12 +1152,13 @@ private int join(NodeEx n2, Configuration conf) {
*/
pragma[nomagic]
private predicate flowOutOfCallNodeCand1(
DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config
DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow,
Configuration config
) {
flowOutOfCallNodeCand1(call, ret, out, config) and
flowOutOfCallNodeCand1(call, ret, kind, out, pragma[only_bind_into](config)) and
exists(int b, int j |
b = branch(ret, config) and
j = join(out, config) and
b = branch(ret, pragma[only_bind_into](config)) and
j = join(out, pragma[only_bind_into](config)) and
if b.minimum(j) <= config.fieldFlowBranchLimit()
then allowsFieldFlow = true
else allowsFieldFlow = false
@@ -1136,10 +1174,10 @@ pragma[nomagic]
private predicate flowIntoCallNodeCand1(
DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
) {
flowIntoCallNodeCand1(call, arg, p, config) and
flowIntoCallNodeCand1(call, arg, p, pragma[only_bind_into](config)) and
exists(int b, int j |
b = branch(arg, config) and
j = join(p, config) and
b = branch(arg, pragma[only_bind_into](config)) and
j = join(p, pragma[only_bind_into](config)) and
if b.minimum(j) <= config.fieldFlowBranchLimit()
then allowsFieldFlow = true
else allowsFieldFlow = false
@@ -1156,7 +1194,9 @@ private signature module StageSig {
predicate callMayFlowThroughRev(DataFlowCall call, Configuration config);
predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config);
predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config);
predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config);
predicate storeStepCand(
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType,
@@ -1222,7 +1262,8 @@ private module MkStage<StageSig PrevStage> {
);
predicate flowOutOfCall(
DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config
DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow,
Configuration config
);
predicate flowIntoCall(
@@ -1247,14 +1288,14 @@ private module MkStage<StageSig PrevStage> {
pragma[nomagic]
private predicate flowThroughOutOfCall(
DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow,
Configuration config
DataFlowCall call, DataFlowCallable c, CcCall ccc, RetNodeEx ret, ReturnKindExt kind,
NodeEx out, boolean allowsFieldFlow, Configuration config
) {
flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and
flowOutOfCall(call, ret, kind, out, allowsFieldFlow, pragma[only_bind_into](config)) and
PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and
PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _,
pragma[only_bind_into](config)) and
matchesCall(ccc, call)
PrevStage::returnMayFlowThrough(ret, kind, pragma[only_bind_into](config)) and
matchesCall(ccc, call) and
c = ret.getEnclosingCallable()
}
/**
@@ -1262,29 +1303,32 @@ private module MkStage<StageSig PrevStage> {
* configuration `config`.
*
* The call context `cc` records whether the node is reached through an
* argument in a call, and if so, `argAp` records the access path of that
* argument.
* argument in a call, and if so, `summaryCtx` and `argAp` record the
* corresponding parameter position and access path of that argument, respectively.
*/
pragma[nomagic]
additional predicate fwdFlow(
NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config
NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
Ap ap, Configuration config
) {
fwdFlow0(node, state, cc, argAp, ap, config) and
fwdFlow0(node, state, cc, summaryCtx, argAp, ap, config) and
PrevStage::revFlow(node, state, unbindApa(getApprox(ap)), config) and
filter(node, state, ap, config)
}
pragma[nomagic]
private predicate fwdFlow0(
NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config
NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
Ap ap, Configuration config
) {
sourceNode(node, state, config) and
(if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
argAp = apNone() and
summaryCtx = TParameterPositionNone() and
ap = getApNil(node)
or
exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc |
fwdFlow(mid, state0, cc, argAp, ap0, config) and
fwdFlow(mid, state0, cc, summaryCtx, argAp, ap0, config) and
localCc = getLocalCc(mid, cc)
|
localStep(mid, state0, node, state, true, _, config, localCc) and
@@ -1295,65 +1339,82 @@ private module MkStage<StageSig PrevStage> {
)
or
exists(NodeEx mid |
fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and
fwdFlow(mid, pragma[only_bind_into](state), _, _, _, ap, pragma[only_bind_into](config)) and
jumpStep(mid, node, config) and
cc = ccNone() and
summaryCtx = TParameterPositionNone() and
argAp = apNone()
)
or
exists(NodeEx mid, ApNil nil |
fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and
fwdFlow(mid, state, _, _, _, nil, pragma[only_bind_into](config)) and
additionalJumpStep(mid, node, config) and
cc = ccNone() and
summaryCtx = TParameterPositionNone() and
argAp = apNone() and
ap = getApNil(node)
)
or
exists(NodeEx mid, FlowState state0, ApNil nil |
fwdFlow(mid, state0, _, _, nil, pragma[only_bind_into](config)) and
fwdFlow(mid, state0, _, _, _, nil, pragma[only_bind_into](config)) and
additionalJumpStateStep(mid, state0, node, state, config) and
cc = ccNone() and
summaryCtx = TParameterPositionNone() and
argAp = apNone() and
ap = getApNil(node)
)
or
// store
exists(TypedContent tc, Ap ap0 |
fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and
fwdFlowStore(_, ap0, tc, node, state, cc, summaryCtx, argAp, config) and
ap = apCons(tc, ap0)
)
or
// read
exists(Ap ap0, Content c |
fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and
fwdFlowRead(ap0, c, _, node, state, cc, summaryCtx, argAp, config) and
fwdFlowConsCand(ap0, c, ap, config)
)
or
// flow into a callable
exists(ApApprox apa |
fwdFlowIn(_, node, state, _, cc, _, ap, config) and
fwdFlowIn(_, node, state, _, cc, _, _, ap, config) and
apa = getApprox(ap) and
if PrevStage::parameterMayFlowThrough(node, _, apa, config)
then argAp = apSome(ap)
else argAp = apNone()
if PrevStage::parameterMayFlowThrough(node, apa, config)
then (
summaryCtx = TParameterPositionSome(node.(ParamNodeEx).getPosition()) and
argAp = apSome(ap)
) else (
summaryCtx = TParameterPositionNone() and argAp = apNone()
)
)
or
// flow out of a callable
fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config)
exists(
DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc,
DataFlowCallable inner
|
fwdFlow(ret, state, innercc, summaryCtx, argAp, ap, config) and
flowOutOfCall(call, ret, _, node, allowsFieldFlow, config) and
inner = ret.getEnclosingCallable() and
cc = getCallContextReturn(inner, call, innercc) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
)
or
exists(DataFlowCall call, Ap argAp0 |
fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and
fwdFlowIsEntered(call, cc, argAp, argAp0, config)
// flow through a callable
exists(DataFlowCall call, ParameterPosition summaryCtx0, Ap argAp0 |
fwdFlowOutFromArg(call, node, state, summaryCtx0, argAp0, ap, config) and
fwdFlowIsEntered(call, cc, summaryCtx, argAp, summaryCtx0, argAp0, config)
)
}
pragma[nomagic]
private predicate fwdFlowStore(
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
Configuration config
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc,
ParameterPositionOption summaryCtx, ApOption argAp, Configuration config
) {
exists(DataFlowType contentType |
fwdFlow(node1, state, cc, argAp, ap1, config) and
fwdFlow(node1, state, cc, summaryCtx, argAp, ap1, config) and
PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and
typecheckStore(ap1, contentType)
)
@@ -1366,7 +1427,7 @@ private module MkStage<StageSig PrevStage> {
pragma[nomagic]
private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) {
exists(TypedContent tc |
fwdFlowStore(_, tail, tc, _, _, _, _, config) and
fwdFlowStore(_, tail, tc, _, _, _, _, _, config) and
tc.getContent() = c and
cons = apCons(tc, tail)
)
@@ -1374,21 +1435,21 @@ private module MkStage<StageSig PrevStage> {
pragma[nomagic]
private predicate fwdFlowRead(
Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
Configuration config
Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc,
ParameterPositionOption summaryCtx, ApOption argAp, Configuration config
) {
fwdFlow(node1, state, cc, argAp, ap, config) and
fwdFlow(node1, state, cc, summaryCtx, argAp, ap, config) and
PrevStage::readStepCand(node1, c, node2, config) and
getHeadContent(ap) = c
}
pragma[nomagic]
private predicate fwdFlowIn(
DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, ApOption argAp,
Ap ap, Configuration config
DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc,
ParameterPositionOption summaryCtx, ApOption argAp, Ap ap, Configuration config
) {
exists(ArgNodeEx arg, boolean allowsFieldFlow |
fwdFlow(arg, state, outercc, argAp, ap, config) and
fwdFlow(arg, state, outercc, summaryCtx, argAp, ap, config) and
flowIntoCall(call, arg, p, allowsFieldFlow, config) and
innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
@@ -1396,29 +1457,19 @@ private module MkStage<StageSig PrevStage> {
}
pragma[nomagic]
private predicate fwdFlowOutNotFromArg(
NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config
private predicate fwdFlowOutFromArg(
DataFlowCall call, NodeEx out, FlowState state, ParameterPosition summaryCtx, Ap argAp, Ap ap,
Configuration config
) {
exists(
DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc,
DataFlowCallable inner
DataFlowCallable c, RetNodeEx ret, ReturnKindExt kind, boolean allowsFieldFlow, CcCall ccc
|
fwdFlow(ret, state, innercc, argAp, ap, config) and
flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
inner = ret.getEnclosingCallable() and
ccOut = getCallContextReturn(inner, call, innercc) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
)
}
pragma[nomagic]
private predicate fwdFlowOutFromArg(
DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config
) {
exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc |
fwdFlow(ret, state, ccc, apSome(argAp), ap, config) and
flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
fwdFlow(pragma[only_bind_into](ret), state, pragma[only_bind_into](ccc),
TParameterPositionSome(pragma[only_bind_into](summaryCtx)), apSome(argAp), ap, config) and
flowThroughOutOfCall(call, pragma[only_bind_into](c), ccc, ret, kind, out, allowsFieldFlow,
config) and
(if allowsFieldFlow = false then ap instanceof ApNil else any()) and
parameterFlowThroughAllowed(c, pragma[only_bind_into](summaryCtx), kind)
)
}
@@ -1428,11 +1479,13 @@ private module MkStage<StageSig PrevStage> {
*/
pragma[nomagic]
private predicate fwdFlowIsEntered(
DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config
DataFlowCall call, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
ParameterPosition pos, Ap ap, Configuration config
) {
exists(ParamNodeEx p |
fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and
PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config)
exists(ParamNodeEx param |
fwdFlowIn(call, param, _, cc, _, summaryCtx, argAp, ap, config) and
PrevStage::parameterMayFlowThrough(param, unbindApa(getApprox(ap)), config) and
pos = param.getPosition()
)
}
@@ -1440,27 +1493,40 @@ private module MkStage<StageSig PrevStage> {
private predicate storeStepFwd(
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config
) {
fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and
fwdFlowStore(node1, ap1, tc, node2, _, _, _, _, config) and
ap2 = apCons(tc, ap1) and
fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, config)
fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, _, config)
}
private predicate readStepFwd(
NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config
) {
fwdFlowRead(ap1, c, n1, n2, _, _, _, config) and
fwdFlowRead(ap1, c, n1, n2, _, _, _, _, config) and
fwdFlowConsCand(ap1, c, ap2, config)
}
pragma[nomagic]
private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) {
exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap |
fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
private predicate returnFlowsThrough0(
RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, DataFlowCallable c,
ParameterPosition ppos, Ap argAp, Ap ap, Configuration config
) {
exists(boolean allowsFieldFlow |
fwdFlow(ret, state, ccc, TParameterPositionSome(ppos), apSome(argAp), ap, config) and
flowThroughOutOfCall(_, c, _, pragma[only_bind_into](ret), kind, _, allowsFieldFlow,
pragma[only_bind_into](config)) and
fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and
fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc),
pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0),
pragma[only_bind_into](config))
if allowsFieldFlow = false then ap instanceof ApNil else any()
)
}
pragma[nomagic]
private predicate returnFlowsThrough(
RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, ParamNodeEx p, Ap argAp,
Ap ap, Configuration config
) {
exists(DataFlowCallable c, ParameterPosition ppos |
returnFlowsThrough0(ret, kind, state, ccc, c, ppos, argAp, ap, config) and
p.isParameterOf(c, ppos) and
parameterFlowThroughAllowed(p, kind)
)
}
@@ -1468,118 +1534,130 @@ private module MkStage<StageSig PrevStage> {
private predicate flowThroughIntoCall(
DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
) {
flowIntoCall(call, arg, p, allowsFieldFlow, config) and
fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and
PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and
callMayFlowThroughFwd(call, pragma[only_bind_into](config))
}
pragma[nomagic]
private predicate returnNodeMayFlowThrough(
RetNodeEx ret, FlowState state, Ap ap, Configuration config
) {
fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, config)
exists(Ap argAp |
flowIntoCall(call, pragma[only_bind_into](arg), pragma[only_bind_into](p), allowsFieldFlow,
pragma[only_bind_into](config)) and
fwdFlow(arg, _, _, _, _, pragma[only_bind_into](argAp), pragma[only_bind_into](config)) and
returnFlowsThrough(_, _, _, _, p, pragma[only_bind_into](argAp), _,
pragma[only_bind_into](config))
)
}
/**
* Holds if `node` with access path `ap` is part of a path from a source to a
* sink in the configuration `config`.
*
* The Boolean `toReturn` records whether the node must be returned from the
* enclosing callable in order to reach a sink, and if so, `returnAp` records
* the access path of the returned value.
* The parameter `returnCtx` records whether (and how) the node must be returned
* from the enclosing callable in order to reach a sink, and if so, `returnAp`
* records the access path of the returned value.
*/
pragma[nomagic]
additional predicate revFlow(
NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
NodeEx node, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap,
Configuration config
) {
revFlow0(node, state, toReturn, returnAp, ap, config) and
fwdFlow(node, state, _, _, ap, config)
revFlow0(node, state, returnCtx, returnAp, ap, config) and
fwdFlow(node, state, _, _, _, ap, config)
}
pragma[nomagic]
private predicate revFlow0(
NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
NodeEx node, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap,
Configuration config
) {
fwdFlow(node, state, _, _, ap, config) and
fwdFlow(node, state, _, _, _, ap, config) and
sinkNode(node, state, config) and
(if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and
(
if hasSinkCallCtx(config)
then returnCtx = TReturnCtxNoFlowThrough()
else returnCtx = TReturnCtxNone()
) and
returnAp = apNone() and
ap instanceof ApNil
or
exists(NodeEx mid, FlowState state0 |
localStep(node, state, mid, state0, true, _, config, _) and
revFlow(mid, state0, toReturn, returnAp, ap, config)
revFlow(mid, state0, returnCtx, returnAp, ap, config)
)
or
exists(NodeEx mid, FlowState state0, ApNil nil |
fwdFlow(node, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and
fwdFlow(node, pragma[only_bind_into](state), _, _, _, ap, pragma[only_bind_into](config)) and
localStep(node, pragma[only_bind_into](state), mid, state0, false, _, config, _) and
revFlow(mid, state0, toReturn, returnAp, nil, pragma[only_bind_into](config)) and
revFlow(mid, state0, returnCtx, returnAp, nil, pragma[only_bind_into](config)) and
ap instanceof ApNil
)
or
exists(NodeEx mid |
jumpStep(node, mid, config) and
revFlow(mid, state, _, _, ap, config) and
toReturn = false and
returnCtx = TReturnCtxNone() and
returnAp = apNone()
)
or
exists(NodeEx mid, ApNil nil |
fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and
fwdFlow(node, _, _, _, _, ap, pragma[only_bind_into](config)) and
additionalJumpStep(node, mid, config) and
revFlow(pragma[only_bind_into](mid), state, _, _, nil, pragma[only_bind_into](config)) and
toReturn = false and
returnCtx = TReturnCtxNone() and
returnAp = apNone() and
ap instanceof ApNil
)
or
exists(NodeEx mid, FlowState state0, ApNil nil |
fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and
fwdFlow(node, _, _, _, _, ap, pragma[only_bind_into](config)) and
additionalJumpStateStep(node, state, mid, state0, config) and
revFlow(pragma[only_bind_into](mid), pragma[only_bind_into](state0), _, _, nil,
pragma[only_bind_into](config)) and
toReturn = false and
returnCtx = TReturnCtxNone() and
returnAp = apNone() and
ap instanceof ApNil
)
or
// store
exists(Ap ap0, Content c |
revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and
revFlowStore(ap0, c, ap, node, state, _, _, returnCtx, returnAp, config) and
revFlowConsCand(ap0, c, ap, config)
)
or
// read
exists(NodeEx mid, Ap ap0 |
revFlow(mid, state, toReturn, returnAp, ap0, config) and
revFlow(mid, state, returnCtx, returnAp, ap0, config) and
readStepFwd(node, ap, _, mid, ap0, config)
)
or
// flow into a callable
revFlowInNotToReturn(node, state, returnAp, ap, config) and
toReturn = false
exists(ParamNodeEx p, boolean allowsFieldFlow |
revFlow(p, state, TReturnCtxNone(), returnAp, ap, config) and
flowIntoCall(_, node, p, allowsFieldFlow, config) and
(if allowsFieldFlow = false then ap instanceof ApNil else any()) and
returnCtx = TReturnCtxNone()
)
or
exists(DataFlowCall call, Ap returnAp0 |
revFlowInToReturn(call, node, state, returnAp0, ap, config) and
revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
// flow through a callable
exists(DataFlowCall call, ReturnKindExt returnKind0, Ap returnAp0 |
revFlowInToReturn(call, node, state, returnKind0, returnAp0, ap, config) and
revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config)
)
or
// flow out of a callable
revFlowOut(_, node, state, _, _, ap, config) and
toReturn = true and
if returnNodeMayFlowThrough(node, state, ap, config)
then returnAp = apSome(ap)
else returnAp = apNone()
exists(ReturnKindExt kind |
revFlowOut(_, node, kind, state, _, _, ap, config) and
if returnFlowsThrough(node, kind, state, _, _, _, ap, config)
then (
returnCtx = TReturnCtxMaybeFlowThrough(kind) and
returnAp = apSome(ap)
) else (
returnCtx = TReturnCtxNoFlowThrough() and returnAp = apNone()
)
)
}
pragma[nomagic]
private predicate revFlowStore(
Ap ap0, Content c, Ap ap, NodeEx node, FlowState state, TypedContent tc, NodeEx mid,
boolean toReturn, ApOption returnAp, Configuration config
ReturnCtx returnCtx, ApOption returnAp, Configuration config
) {
revFlow(mid, state, toReturn, returnAp, ap0, config) and
revFlow(mid, state, returnCtx, returnAp, ap0, config) and
storeStepFwd(node, ap, tc, mid, ap0, config) and
tc.getContent() = c
}
@@ -1599,35 +1677,27 @@ private module MkStage<StageSig PrevStage> {
pragma[nomagic]
private predicate revFlowOut(
DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, ApOption returnAp, Ap ap,
Configuration config
DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, FlowState state, ReturnCtx returnCtx,
ApOption returnAp, Ap ap, Configuration config
) {
exists(NodeEx out, boolean allowsFieldFlow |
revFlow(out, state, toReturn, returnAp, ap, config) and
flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
)
}
pragma[nomagic]
private predicate revFlowInNotToReturn(
ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config
) {
exists(ParamNodeEx p, boolean allowsFieldFlow |
revFlow(p, state, false, returnAp, ap, config) and
flowIntoCall(_, arg, p, allowsFieldFlow, config) and
revFlow(out, state, returnCtx, returnAp, ap, config) and
flowOutOfCall(call, ret, kind, out, allowsFieldFlow, config) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
)
}
pragma[nomagic]
private predicate revFlowInToReturn(
DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config
DataFlowCall call, ArgNodeEx arg, FlowState state, ReturnKindExt kind, Ap returnAp, Ap ap,
Configuration config
) {
exists(ParamNodeEx p, boolean allowsFieldFlow |
revFlow(p, state, true, apSome(returnAp), ap, config) and
revFlow(pragma[only_bind_into](p), state,
TReturnCtxMaybeFlowThrough(pragma[only_bind_into](kind)), apSome(returnAp), ap, config) and
flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
(if allowsFieldFlow = false then ap instanceof ApNil else any()) and
parameterFlowThroughAllowed(p, kind)
)
}
@@ -1638,11 +1708,12 @@ private module MkStage<StageSig PrevStage> {
*/
pragma[nomagic]
private predicate revFlowIsReturned(
DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
DataFlowCall call, ReturnCtx returnCtx, ApOption returnAp, ReturnKindExt kind, Ap ap,
Configuration config
) {
exists(RetNodeEx ret, FlowState state, CcCall ccc |
revFlowOut(call, ret, state, toReturn, returnAp, ap, config) and
fwdFlow(ret, state, ccc, apSome(_), ap, config) and
revFlowOut(call, ret, kind, state, returnCtx, returnAp, ap, config) and
returnFlowsThrough(ret, kind, state, ccc, _, _, ap, config) and
matchesCall(ccc, call)
)
}
@@ -1713,40 +1784,39 @@ private module MkStage<StageSig PrevStage> {
validAp(ap, config)
}
pragma[noinline]
private predicate parameterFlow(
ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
pragma[nomagic]
private predicate parameterFlowsThroughRev(
ParamNodeEx p, Ap ap, ReturnKindExt kind, Configuration config
) {
revFlow(p, _, true, apSome(ap0), ap, config) and
c = p.getEnclosingCallable()
revFlow(p, _, TReturnCtxMaybeFlowThrough(kind), apSome(_), ap, config) and
parameterFlowThroughAllowed(p, kind)
}
predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) {
exists(RetNodeEx ret, FlowState state, Ap ap0, ReturnKindExt kind, ParameterPosition pos |
parameterFlow(p, ap, ap0, c, config) and
c = ret.getEnclosingCallable() and
revFlow(pragma[only_bind_into](ret), pragma[only_bind_into](state), true, apSome(_),
pragma[only_bind_into](ap0), pragma[only_bind_into](config)) and
fwdFlow(ret, state, any(CcCall ccc), apSome(ap), ap0, config) and
kind = ret.getKind() and
p.getPosition() = pos and
// we don't expect a parameter to return stored in itself, unless explicitly allowed
(
not kind.(ParamUpdateReturnKind).getPosition() = pos
or
p.allowParameterReturnInSelf()
)
pragma[nomagic]
predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) {
exists(RetNodeEx ret, ReturnKindExt kind |
returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and
parameterFlowsThroughRev(p, ap, kind, config)
)
}
pragma[nomagic]
predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) {
exists(ParamNodeEx p, Ap ap |
returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and
parameterFlowsThroughRev(p, ap, kind, config)
)
}
pragma[nomagic]
predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) {
exists(
Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap
ReturnKindExt returnKind0, Ap returnAp0, ArgNodeEx arg, FlowState state,
ReturnCtx returnCtx, ApOption returnAp, Ap ap
|
revFlow(arg, state, toReturn, returnAp, ap, config) and
revFlowInToReturn(call, arg, state, returnAp0, ap, config) and
revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
revFlow(arg, state, returnCtx, returnAp, ap, config) and
revFlowInToReturn(call, arg, state, returnKind0, returnAp0, ap, config) and
revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config)
)
}
@@ -1754,14 +1824,13 @@ private module MkStage<StageSig PrevStage> {
boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config
) {
fwd = true and
nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, config)) and
nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, _, config)) and
fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and
conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and
states = count(FlowState state | fwdFlow(_, state, _, _, _, config)) and
states = count(FlowState state | fwdFlow(_, state, _, _, _, _, config)) and
tuples =
count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap |
fwdFlow(n, state, cc, argAp, ap, config)
)
count(NodeEx n, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
Ap ap | fwdFlow(n, state, cc, summaryCtx, argAp, ap, config))
or
fwd = false and
nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and
@@ -1769,8 +1838,8 @@ private module MkStage<StageSig PrevStage> {
conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and
states = count(FlowState state | revFlow(_, state, _, _, _, config)) and
tuples =
count(NodeEx n, FlowState state, boolean b, ApOption retAp, Ap ap |
revFlow(n, state, b, retAp, ap, config)
count(NodeEx n, FlowState state, ReturnCtx returnCtx, ApOption retAp, Ap ap |
revFlow(n, state, returnCtx, retAp, ap, config)
)
}
/* End: Stage logic. */
@@ -1915,7 +1984,7 @@ private module Stage2Param implements MkStage<Stage1>::StageParam {
exists(lcc)
}
predicate flowOutOfCall = flowOutOfCallNodeCand1/5;
predicate flowOutOfCall = flowOutOfCallNodeCand1/6;
predicate flowIntoCall = flowIntoCallNodeCand1/5;
@@ -1951,9 +2020,10 @@ private module Stage2 implements StageSig {
pragma[nomagic]
private predicate flowOutOfCallNodeCand2(
DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config
DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow,
Configuration config
) {
flowOutOfCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and
flowOutOfCallNodeCand1(call, node1, kind, node2, allowsFieldFlow, config) and
Stage2::revFlow(node2, pragma[only_bind_into](config)) and
Stage2::revFlowAlias(node1, pragma[only_bind_into](config))
}
@@ -2021,8 +2091,8 @@ private module LocalFlowBigStep {
exists(NodeEx next | Stage2::revFlow(next, state, config) |
jumpStep(node, next, config) or
additionalJumpStep(node, next, config) or
flowIntoCallNodeCand1(_, node, next, config) or
flowOutOfCallNodeCand1(_, node, next, config) or
flowIntoCallNodeCand2(_, node, next, _, config) or
flowOutOfCallNodeCand2(_, node, _, next, _, config) or
Stage2::storeStepCand(node, _, _, next, _, config) or
Stage2::readStepCand(node, _, next, config)
)
@@ -2163,7 +2233,7 @@ private module Stage3Param implements MkStage<Stage2>::StageParam {
localFlowBigStep(node1, state1, node2, state2, preservesValue, ap, config, _) and exists(lcc)
}
predicate flowOutOfCall = flowOutOfCallNodeCand2/5;
predicate flowOutOfCall = flowOutOfCallNodeCand2/6;
predicate flowIntoCall = flowIntoCallNodeCand2/5;
@@ -2233,8 +2303,9 @@ private predicate flowCandSummaryCtx(
NodeEx node, FlowState state, AccessPathFront argApf, Configuration config
) {
exists(AccessPathFront apf |
Stage3::revFlow(node, state, true, _, apf, config) and
Stage3::fwdFlow(node, state, any(Stage3::CcCall ccc), TAccessPathFrontSome(argApf), apf, config)
Stage3::revFlow(node, state, TReturnCtxMaybeFlowThrough(_), _, apf, config) and
Stage3::fwdFlow(node, state, any(Stage3::CcCall ccc), _, TAccessPathFrontSome(argApf), apf,
config)
)
}
@@ -2468,10 +2539,11 @@ private module Stage4Param implements MkStage<Stage3>::StageParam {
pragma[nomagic]
predicate flowOutOfCall(
DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config
DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow,
Configuration config
) {
exists(FlowState state |
flowOutOfCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and
flowOutOfCallNodeCand2(call, node1, kind, node2, allowsFieldFlow, config) and
PrevStage::revFlow(node2, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) and
PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _,
pragma[only_bind_into](config))
@@ -2508,13 +2580,14 @@ private Configuration unbindConf(Configuration conf) {
pragma[nomagic]
private predicate nodeMayUseSummary0(
NodeEx n, DataFlowCallable c, FlowState state, AccessPathApprox apa, Configuration config
NodeEx n, DataFlowCallable c, ParameterPosition pos, FlowState state, AccessPathApprox apa,
Configuration config
) {
exists(AccessPathApprox apa0 |
Stage4::parameterMayFlowThrough(_, c, _, _) and
Stage4::revFlow(n, state, true, _, apa0, config) and
Stage4::fwdFlow(n, state, any(CallContextCall ccc), TAccessPathApproxSome(apa), apa0, config) and
n.getEnclosingCallable() = c
c = n.getEnclosingCallable() and
Stage4::revFlow(n, state, TReturnCtxMaybeFlowThrough(_), _, apa0, config) and
Stage4::fwdFlow(n, state, any(CallContextCall ccc), TParameterPositionSome(pos),
TAccessPathApproxSome(apa), apa0, config)
)
}
@@ -2522,9 +2595,10 @@ pragma[nomagic]
private predicate nodeMayUseSummary(
NodeEx n, FlowState state, AccessPathApprox apa, Configuration config
) {
exists(DataFlowCallable c |
Stage4::parameterMayFlowThrough(_, c, apa, config) and
nodeMayUseSummary0(n, c, state, apa, config)
exists(DataFlowCallable c, ParameterPosition pos, ParamNodeEx p |
Stage4::parameterMayFlowThrough(p, apa, config) and
nodeMayUseSummary0(n, c, pos, state, apa, config) and
p.isParameterOf(c, pos)
)
}
@@ -2532,7 +2606,7 @@ private newtype TSummaryCtx =
TSummaryCtxNone() or
TSummaryCtxSome(ParamNodeEx p, FlowState state, AccessPath ap) {
exists(Configuration config |
Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), config) and
Stage4::parameterMayFlowThrough(p, ap.getApprox(), config) and
Stage4::revFlow(p, state, _, config)
)
}
@@ -3453,17 +3527,11 @@ private predicate paramFlowsThrough(
ReturnKindExt kind, FlowState state, CallContextCall cc, SummaryCtxSome sc, AccessPath ap,
AccessPathApprox apa, Configuration config
) {
exists(PathNodeMid mid, RetNodeEx ret, ParameterPosition pos |
exists(PathNodeMid mid, RetNodeEx ret |
pathNode(mid, ret, state, cc, sc, ap, config, _) and
kind = ret.getKind() and
apa = ap.getApprox() and
pos = sc.getParameterPos() and
// we don't expect a parameter to return stored in itself, unless explicitly allowed
(
not kind.(ParamUpdateReturnKind).getPosition() = pos
or
sc.getParamNode().allowParameterReturnInSelf()
)
parameterFlowThroughAllowed(sc.getParamNode(), kind)
)
}

View File

@@ -70,7 +70,7 @@ abstract class Configuration extends string {
/**
* Holds if `sink` is a relevant data flow sink accepting `state`.
*/
predicate isSink(Node source, FlowState state) { none() }
predicate isSink(Node sink, FlowState state) { none() }
/**
* Holds if data flow through `node` is prohibited. This completely removes
@@ -319,8 +319,6 @@ private class ParamNodeEx extends NodeEx {
}
ParameterPosition getPosition() { this.isParameterOf(_, result) }
predicate allowParameterReturnInSelf() { allowParameterReturnInSelfCached(this.asNode()) }
}
private class RetNodeEx extends NodeEx {
@@ -608,6 +606,38 @@ private predicate hasSinkCallCtx(Configuration config) {
)
}
/**
* Holds if flow from `p` to a return node of kind `kind` is allowed.
*
* We don't expect a parameter to return stored in itself, unless
* explicitly allowed
*/
bindingset[p, kind]
private predicate parameterFlowThroughAllowed(ParamNodeEx p, ReturnKindExt kind) {
exists(ParameterPosition pos | p.isParameterOf(_, pos) |
not kind.(ParamUpdateReturnKind).getPosition() = pos
or
allowParameterReturnInSelfCached(p.asNode())
)
}
/**
* Holds if flow from a parameter at position `pos` inside `c` to a return node of
* kind `kind` is allowed.
*
* We don't expect a parameter to return stored in itself, unless
* explicitly allowed
*/
bindingset[c, pos, kind]
private predicate parameterFlowThroughAllowed(
DataFlowCallable c, ParameterPosition pos, ReturnKindExt kind
) {
exists(ParamNodeEx p |
p.isParameterOf(c, pos) and
parameterFlowThroughAllowed(p, kind)
)
}
private module Stage1 implements StageSig {
class Ap = Unit;
@@ -981,21 +1011,22 @@ private module Stage1 implements StageSig {
* candidate for the origin of a summary.
*/
pragma[nomagic]
predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) {
exists(ReturnKindExt kind |
predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) {
exists(DataFlowCallable c, ReturnKindExt kind |
throughFlowNodeCand(p, config) and
returnFlowCallableNodeCand(c, kind, config) and
p.getEnclosingCallable() = c and
exists(ap) and
// we don't expect a parameter to return stored in itself, unless explicitly allowed
(
not kind.(ParamUpdateReturnKind).getPosition() = p.getPosition()
or
p.allowParameterReturnInSelf()
)
parameterFlowThroughAllowed(p, kind)
)
}
pragma[nomagic]
predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) {
throughFlowNodeCand(ret, config) and
kind = ret.getKind()
}
pragma[nomagic]
predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) {
exists(ArgNodeEx arg, boolean toReturn |
@@ -1052,12 +1083,16 @@ private predicate viableReturnPosOutNodeCand1(
*/
pragma[nomagic]
private predicate flowOutOfCallNodeCand1(
DataFlowCall call, RetNodeEx ret, NodeEx out, Configuration config
DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, Configuration config
) {
viableReturnPosOutNodeCand1(call, ret.getReturnPosition(), out, config) and
Stage1::revFlow(ret, config) and
not outBarrier(ret, config) and
not inBarrier(out, config)
exists(ReturnPosition pos |
viableReturnPosOutNodeCand1(call, pos, out, config) and
pos = ret.getReturnPosition() and
kind = pos.getKind() and
Stage1::revFlow(ret, config) and
not outBarrier(ret, config) and
not inBarrier(out, config)
)
}
pragma[nomagic]
@@ -1087,10 +1122,11 @@ private predicate flowIntoCallNodeCand1(
* edge in the graph of paths between sources and sinks that ignores call
* contexts.
*/
pragma[nomagic]
private int branch(NodeEx n1, Configuration conf) {
result =
strictcount(NodeEx n |
flowOutOfCallNodeCand1(_, n1, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf)
flowOutOfCallNodeCand1(_, n1, _, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf)
)
}
@@ -1099,10 +1135,11 @@ private int branch(NodeEx n1, Configuration conf) {
* edge in the graph of paths between sources and sinks that ignores call
* contexts.
*/
pragma[nomagic]
private int join(NodeEx n2, Configuration conf) {
result =
strictcount(NodeEx n |
flowOutOfCallNodeCand1(_, n, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf)
flowOutOfCallNodeCand1(_, n, _, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf)
)
}
@@ -1115,12 +1152,13 @@ private int join(NodeEx n2, Configuration conf) {
*/
pragma[nomagic]
private predicate flowOutOfCallNodeCand1(
DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config
DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow,
Configuration config
) {
flowOutOfCallNodeCand1(call, ret, out, config) and
flowOutOfCallNodeCand1(call, ret, kind, out, pragma[only_bind_into](config)) and
exists(int b, int j |
b = branch(ret, config) and
j = join(out, config) and
b = branch(ret, pragma[only_bind_into](config)) and
j = join(out, pragma[only_bind_into](config)) and
if b.minimum(j) <= config.fieldFlowBranchLimit()
then allowsFieldFlow = true
else allowsFieldFlow = false
@@ -1136,10 +1174,10 @@ pragma[nomagic]
private predicate flowIntoCallNodeCand1(
DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
) {
flowIntoCallNodeCand1(call, arg, p, config) and
flowIntoCallNodeCand1(call, arg, p, pragma[only_bind_into](config)) and
exists(int b, int j |
b = branch(arg, config) and
j = join(p, config) and
b = branch(arg, pragma[only_bind_into](config)) and
j = join(p, pragma[only_bind_into](config)) and
if b.minimum(j) <= config.fieldFlowBranchLimit()
then allowsFieldFlow = true
else allowsFieldFlow = false
@@ -1156,7 +1194,9 @@ private signature module StageSig {
predicate callMayFlowThroughRev(DataFlowCall call, Configuration config);
predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config);
predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config);
predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config);
predicate storeStepCand(
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType,
@@ -1222,7 +1262,8 @@ private module MkStage<StageSig PrevStage> {
);
predicate flowOutOfCall(
DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config
DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow,
Configuration config
);
predicate flowIntoCall(
@@ -1247,14 +1288,14 @@ private module MkStage<StageSig PrevStage> {
pragma[nomagic]
private predicate flowThroughOutOfCall(
DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow,
Configuration config
DataFlowCall call, DataFlowCallable c, CcCall ccc, RetNodeEx ret, ReturnKindExt kind,
NodeEx out, boolean allowsFieldFlow, Configuration config
) {
flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and
flowOutOfCall(call, ret, kind, out, allowsFieldFlow, pragma[only_bind_into](config)) and
PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and
PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _,
pragma[only_bind_into](config)) and
matchesCall(ccc, call)
PrevStage::returnMayFlowThrough(ret, kind, pragma[only_bind_into](config)) and
matchesCall(ccc, call) and
c = ret.getEnclosingCallable()
}
/**
@@ -1262,29 +1303,32 @@ private module MkStage<StageSig PrevStage> {
* configuration `config`.
*
* The call context `cc` records whether the node is reached through an
* argument in a call, and if so, `argAp` records the access path of that
* argument.
* argument in a call, and if so, `summaryCtx` and `argAp` record the
* corresponding parameter position and access path of that argument, respectively.
*/
pragma[nomagic]
additional predicate fwdFlow(
NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config
NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
Ap ap, Configuration config
) {
fwdFlow0(node, state, cc, argAp, ap, config) and
fwdFlow0(node, state, cc, summaryCtx, argAp, ap, config) and
PrevStage::revFlow(node, state, unbindApa(getApprox(ap)), config) and
filter(node, state, ap, config)
}
pragma[nomagic]
private predicate fwdFlow0(
NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config
NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
Ap ap, Configuration config
) {
sourceNode(node, state, config) and
(if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
argAp = apNone() and
summaryCtx = TParameterPositionNone() and
ap = getApNil(node)
or
exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc |
fwdFlow(mid, state0, cc, argAp, ap0, config) and
fwdFlow(mid, state0, cc, summaryCtx, argAp, ap0, config) and
localCc = getLocalCc(mid, cc)
|
localStep(mid, state0, node, state, true, _, config, localCc) and
@@ -1295,65 +1339,82 @@ private module MkStage<StageSig PrevStage> {
)
or
exists(NodeEx mid |
fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and
fwdFlow(mid, pragma[only_bind_into](state), _, _, _, ap, pragma[only_bind_into](config)) and
jumpStep(mid, node, config) and
cc = ccNone() and
summaryCtx = TParameterPositionNone() and
argAp = apNone()
)
or
exists(NodeEx mid, ApNil nil |
fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and
fwdFlow(mid, state, _, _, _, nil, pragma[only_bind_into](config)) and
additionalJumpStep(mid, node, config) and
cc = ccNone() and
summaryCtx = TParameterPositionNone() and
argAp = apNone() and
ap = getApNil(node)
)
or
exists(NodeEx mid, FlowState state0, ApNil nil |
fwdFlow(mid, state0, _, _, nil, pragma[only_bind_into](config)) and
fwdFlow(mid, state0, _, _, _, nil, pragma[only_bind_into](config)) and
additionalJumpStateStep(mid, state0, node, state, config) and
cc = ccNone() and
summaryCtx = TParameterPositionNone() and
argAp = apNone() and
ap = getApNil(node)
)
or
// store
exists(TypedContent tc, Ap ap0 |
fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and
fwdFlowStore(_, ap0, tc, node, state, cc, summaryCtx, argAp, config) and
ap = apCons(tc, ap0)
)
or
// read
exists(Ap ap0, Content c |
fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and
fwdFlowRead(ap0, c, _, node, state, cc, summaryCtx, argAp, config) and
fwdFlowConsCand(ap0, c, ap, config)
)
or
// flow into a callable
exists(ApApprox apa |
fwdFlowIn(_, node, state, _, cc, _, ap, config) and
fwdFlowIn(_, node, state, _, cc, _, _, ap, config) and
apa = getApprox(ap) and
if PrevStage::parameterMayFlowThrough(node, _, apa, config)
then argAp = apSome(ap)
else argAp = apNone()
if PrevStage::parameterMayFlowThrough(node, apa, config)
then (
summaryCtx = TParameterPositionSome(node.(ParamNodeEx).getPosition()) and
argAp = apSome(ap)
) else (
summaryCtx = TParameterPositionNone() and argAp = apNone()
)
)
or
// flow out of a callable
fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config)
exists(
DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc,
DataFlowCallable inner
|
fwdFlow(ret, state, innercc, summaryCtx, argAp, ap, config) and
flowOutOfCall(call, ret, _, node, allowsFieldFlow, config) and
inner = ret.getEnclosingCallable() and
cc = getCallContextReturn(inner, call, innercc) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
)
or
exists(DataFlowCall call, Ap argAp0 |
fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and
fwdFlowIsEntered(call, cc, argAp, argAp0, config)
// flow through a callable
exists(DataFlowCall call, ParameterPosition summaryCtx0, Ap argAp0 |
fwdFlowOutFromArg(call, node, state, summaryCtx0, argAp0, ap, config) and
fwdFlowIsEntered(call, cc, summaryCtx, argAp, summaryCtx0, argAp0, config)
)
}
pragma[nomagic]
private predicate fwdFlowStore(
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
Configuration config
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc,
ParameterPositionOption summaryCtx, ApOption argAp, Configuration config
) {
exists(DataFlowType contentType |
fwdFlow(node1, state, cc, argAp, ap1, config) and
fwdFlow(node1, state, cc, summaryCtx, argAp, ap1, config) and
PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and
typecheckStore(ap1, contentType)
)
@@ -1366,7 +1427,7 @@ private module MkStage<StageSig PrevStage> {
pragma[nomagic]
private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) {
exists(TypedContent tc |
fwdFlowStore(_, tail, tc, _, _, _, _, config) and
fwdFlowStore(_, tail, tc, _, _, _, _, _, config) and
tc.getContent() = c and
cons = apCons(tc, tail)
)
@@ -1374,21 +1435,21 @@ private module MkStage<StageSig PrevStage> {
pragma[nomagic]
private predicate fwdFlowRead(
Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
Configuration config
Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc,
ParameterPositionOption summaryCtx, ApOption argAp, Configuration config
) {
fwdFlow(node1, state, cc, argAp, ap, config) and
fwdFlow(node1, state, cc, summaryCtx, argAp, ap, config) and
PrevStage::readStepCand(node1, c, node2, config) and
getHeadContent(ap) = c
}
pragma[nomagic]
private predicate fwdFlowIn(
DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, ApOption argAp,
Ap ap, Configuration config
DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc,
ParameterPositionOption summaryCtx, ApOption argAp, Ap ap, Configuration config
) {
exists(ArgNodeEx arg, boolean allowsFieldFlow |
fwdFlow(arg, state, outercc, argAp, ap, config) and
fwdFlow(arg, state, outercc, summaryCtx, argAp, ap, config) and
flowIntoCall(call, arg, p, allowsFieldFlow, config) and
innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
@@ -1396,29 +1457,19 @@ private module MkStage<StageSig PrevStage> {
}
pragma[nomagic]
private predicate fwdFlowOutNotFromArg(
NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config
private predicate fwdFlowOutFromArg(
DataFlowCall call, NodeEx out, FlowState state, ParameterPosition summaryCtx, Ap argAp, Ap ap,
Configuration config
) {
exists(
DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc,
DataFlowCallable inner
DataFlowCallable c, RetNodeEx ret, ReturnKindExt kind, boolean allowsFieldFlow, CcCall ccc
|
fwdFlow(ret, state, innercc, argAp, ap, config) and
flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
inner = ret.getEnclosingCallable() and
ccOut = getCallContextReturn(inner, call, innercc) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
)
}
pragma[nomagic]
private predicate fwdFlowOutFromArg(
DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config
) {
exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc |
fwdFlow(ret, state, ccc, apSome(argAp), ap, config) and
flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
fwdFlow(pragma[only_bind_into](ret), state, pragma[only_bind_into](ccc),
TParameterPositionSome(pragma[only_bind_into](summaryCtx)), apSome(argAp), ap, config) and
flowThroughOutOfCall(call, pragma[only_bind_into](c), ccc, ret, kind, out, allowsFieldFlow,
config) and
(if allowsFieldFlow = false then ap instanceof ApNil else any()) and
parameterFlowThroughAllowed(c, pragma[only_bind_into](summaryCtx), kind)
)
}
@@ -1428,11 +1479,13 @@ private module MkStage<StageSig PrevStage> {
*/
pragma[nomagic]
private predicate fwdFlowIsEntered(
DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config
DataFlowCall call, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
ParameterPosition pos, Ap ap, Configuration config
) {
exists(ParamNodeEx p |
fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and
PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config)
exists(ParamNodeEx param |
fwdFlowIn(call, param, _, cc, _, summaryCtx, argAp, ap, config) and
PrevStage::parameterMayFlowThrough(param, unbindApa(getApprox(ap)), config) and
pos = param.getPosition()
)
}
@@ -1440,27 +1493,40 @@ private module MkStage<StageSig PrevStage> {
private predicate storeStepFwd(
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config
) {
fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and
fwdFlowStore(node1, ap1, tc, node2, _, _, _, _, config) and
ap2 = apCons(tc, ap1) and
fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, config)
fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, _, config)
}
private predicate readStepFwd(
NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config
) {
fwdFlowRead(ap1, c, n1, n2, _, _, _, config) and
fwdFlowRead(ap1, c, n1, n2, _, _, _, _, config) and
fwdFlowConsCand(ap1, c, ap2, config)
}
pragma[nomagic]
private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) {
exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap |
fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
private predicate returnFlowsThrough0(
RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, DataFlowCallable c,
ParameterPosition ppos, Ap argAp, Ap ap, Configuration config
) {
exists(boolean allowsFieldFlow |
fwdFlow(ret, state, ccc, TParameterPositionSome(ppos), apSome(argAp), ap, config) and
flowThroughOutOfCall(_, c, _, pragma[only_bind_into](ret), kind, _, allowsFieldFlow,
pragma[only_bind_into](config)) and
fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and
fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc),
pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0),
pragma[only_bind_into](config))
if allowsFieldFlow = false then ap instanceof ApNil else any()
)
}
pragma[nomagic]
private predicate returnFlowsThrough(
RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, ParamNodeEx p, Ap argAp,
Ap ap, Configuration config
) {
exists(DataFlowCallable c, ParameterPosition ppos |
returnFlowsThrough0(ret, kind, state, ccc, c, ppos, argAp, ap, config) and
p.isParameterOf(c, ppos) and
parameterFlowThroughAllowed(p, kind)
)
}
@@ -1468,118 +1534,130 @@ private module MkStage<StageSig PrevStage> {
private predicate flowThroughIntoCall(
DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
) {
flowIntoCall(call, arg, p, allowsFieldFlow, config) and
fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and
PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and
callMayFlowThroughFwd(call, pragma[only_bind_into](config))
}
pragma[nomagic]
private predicate returnNodeMayFlowThrough(
RetNodeEx ret, FlowState state, Ap ap, Configuration config
) {
fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, config)
exists(Ap argAp |
flowIntoCall(call, pragma[only_bind_into](arg), pragma[only_bind_into](p), allowsFieldFlow,
pragma[only_bind_into](config)) and
fwdFlow(arg, _, _, _, _, pragma[only_bind_into](argAp), pragma[only_bind_into](config)) and
returnFlowsThrough(_, _, _, _, p, pragma[only_bind_into](argAp), _,
pragma[only_bind_into](config))
)
}
/**
* Holds if `node` with access path `ap` is part of a path from a source to a
* sink in the configuration `config`.
*
* The Boolean `toReturn` records whether the node must be returned from the
* enclosing callable in order to reach a sink, and if so, `returnAp` records
* the access path of the returned value.
* The parameter `returnCtx` records whether (and how) the node must be returned
* from the enclosing callable in order to reach a sink, and if so, `returnAp`
* records the access path of the returned value.
*/
pragma[nomagic]
additional predicate revFlow(
NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
NodeEx node, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap,
Configuration config
) {
revFlow0(node, state, toReturn, returnAp, ap, config) and
fwdFlow(node, state, _, _, ap, config)
revFlow0(node, state, returnCtx, returnAp, ap, config) and
fwdFlow(node, state, _, _, _, ap, config)
}
pragma[nomagic]
private predicate revFlow0(
NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
NodeEx node, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap,
Configuration config
) {
fwdFlow(node, state, _, _, ap, config) and
fwdFlow(node, state, _, _, _, ap, config) and
sinkNode(node, state, config) and
(if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and
(
if hasSinkCallCtx(config)
then returnCtx = TReturnCtxNoFlowThrough()
else returnCtx = TReturnCtxNone()
) and
returnAp = apNone() and
ap instanceof ApNil
or
exists(NodeEx mid, FlowState state0 |
localStep(node, state, mid, state0, true, _, config, _) and
revFlow(mid, state0, toReturn, returnAp, ap, config)
revFlow(mid, state0, returnCtx, returnAp, ap, config)
)
or
exists(NodeEx mid, FlowState state0, ApNil nil |
fwdFlow(node, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and
fwdFlow(node, pragma[only_bind_into](state), _, _, _, ap, pragma[only_bind_into](config)) and
localStep(node, pragma[only_bind_into](state), mid, state0, false, _, config, _) and
revFlow(mid, state0, toReturn, returnAp, nil, pragma[only_bind_into](config)) and
revFlow(mid, state0, returnCtx, returnAp, nil, pragma[only_bind_into](config)) and
ap instanceof ApNil
)
or
exists(NodeEx mid |
jumpStep(node, mid, config) and
revFlow(mid, state, _, _, ap, config) and
toReturn = false and
returnCtx = TReturnCtxNone() and
returnAp = apNone()
)
or
exists(NodeEx mid, ApNil nil |
fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and
fwdFlow(node, _, _, _, _, ap, pragma[only_bind_into](config)) and
additionalJumpStep(node, mid, config) and
revFlow(pragma[only_bind_into](mid), state, _, _, nil, pragma[only_bind_into](config)) and
toReturn = false and
returnCtx = TReturnCtxNone() and
returnAp = apNone() and
ap instanceof ApNil
)
or
exists(NodeEx mid, FlowState state0, ApNil nil |
fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and
fwdFlow(node, _, _, _, _, ap, pragma[only_bind_into](config)) and
additionalJumpStateStep(node, state, mid, state0, config) and
revFlow(pragma[only_bind_into](mid), pragma[only_bind_into](state0), _, _, nil,
pragma[only_bind_into](config)) and
toReturn = false and
returnCtx = TReturnCtxNone() and
returnAp = apNone() and
ap instanceof ApNil
)
or
// store
exists(Ap ap0, Content c |
revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and
revFlowStore(ap0, c, ap, node, state, _, _, returnCtx, returnAp, config) and
revFlowConsCand(ap0, c, ap, config)
)
or
// read
exists(NodeEx mid, Ap ap0 |
revFlow(mid, state, toReturn, returnAp, ap0, config) and
revFlow(mid, state, returnCtx, returnAp, ap0, config) and
readStepFwd(node, ap, _, mid, ap0, config)
)
or
// flow into a callable
revFlowInNotToReturn(node, state, returnAp, ap, config) and
toReturn = false
exists(ParamNodeEx p, boolean allowsFieldFlow |
revFlow(p, state, TReturnCtxNone(), returnAp, ap, config) and
flowIntoCall(_, node, p, allowsFieldFlow, config) and
(if allowsFieldFlow = false then ap instanceof ApNil else any()) and
returnCtx = TReturnCtxNone()
)
or
exists(DataFlowCall call, Ap returnAp0 |
revFlowInToReturn(call, node, state, returnAp0, ap, config) and
revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
// flow through a callable
exists(DataFlowCall call, ReturnKindExt returnKind0, Ap returnAp0 |
revFlowInToReturn(call, node, state, returnKind0, returnAp0, ap, config) and
revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config)
)
or
// flow out of a callable
revFlowOut(_, node, state, _, _, ap, config) and
toReturn = true and
if returnNodeMayFlowThrough(node, state, ap, config)
then returnAp = apSome(ap)
else returnAp = apNone()
exists(ReturnKindExt kind |
revFlowOut(_, node, kind, state, _, _, ap, config) and
if returnFlowsThrough(node, kind, state, _, _, _, ap, config)
then (
returnCtx = TReturnCtxMaybeFlowThrough(kind) and
returnAp = apSome(ap)
) else (
returnCtx = TReturnCtxNoFlowThrough() and returnAp = apNone()
)
)
}
pragma[nomagic]
private predicate revFlowStore(
Ap ap0, Content c, Ap ap, NodeEx node, FlowState state, TypedContent tc, NodeEx mid,
boolean toReturn, ApOption returnAp, Configuration config
ReturnCtx returnCtx, ApOption returnAp, Configuration config
) {
revFlow(mid, state, toReturn, returnAp, ap0, config) and
revFlow(mid, state, returnCtx, returnAp, ap0, config) and
storeStepFwd(node, ap, tc, mid, ap0, config) and
tc.getContent() = c
}
@@ -1599,35 +1677,27 @@ private module MkStage<StageSig PrevStage> {
pragma[nomagic]
private predicate revFlowOut(
DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, ApOption returnAp, Ap ap,
Configuration config
DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, FlowState state, ReturnCtx returnCtx,
ApOption returnAp, Ap ap, Configuration config
) {
exists(NodeEx out, boolean allowsFieldFlow |
revFlow(out, state, toReturn, returnAp, ap, config) and
flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
)
}
pragma[nomagic]
private predicate revFlowInNotToReturn(
ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config
) {
exists(ParamNodeEx p, boolean allowsFieldFlow |
revFlow(p, state, false, returnAp, ap, config) and
flowIntoCall(_, arg, p, allowsFieldFlow, config) and
revFlow(out, state, returnCtx, returnAp, ap, config) and
flowOutOfCall(call, ret, kind, out, allowsFieldFlow, config) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
)
}
pragma[nomagic]
private predicate revFlowInToReturn(
DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config
DataFlowCall call, ArgNodeEx arg, FlowState state, ReturnKindExt kind, Ap returnAp, Ap ap,
Configuration config
) {
exists(ParamNodeEx p, boolean allowsFieldFlow |
revFlow(p, state, true, apSome(returnAp), ap, config) and
revFlow(pragma[only_bind_into](p), state,
TReturnCtxMaybeFlowThrough(pragma[only_bind_into](kind)), apSome(returnAp), ap, config) and
flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
(if allowsFieldFlow = false then ap instanceof ApNil else any()) and
parameterFlowThroughAllowed(p, kind)
)
}
@@ -1638,11 +1708,12 @@ private module MkStage<StageSig PrevStage> {
*/
pragma[nomagic]
private predicate revFlowIsReturned(
DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
DataFlowCall call, ReturnCtx returnCtx, ApOption returnAp, ReturnKindExt kind, Ap ap,
Configuration config
) {
exists(RetNodeEx ret, FlowState state, CcCall ccc |
revFlowOut(call, ret, state, toReturn, returnAp, ap, config) and
fwdFlow(ret, state, ccc, apSome(_), ap, config) and
revFlowOut(call, ret, kind, state, returnCtx, returnAp, ap, config) and
returnFlowsThrough(ret, kind, state, ccc, _, _, ap, config) and
matchesCall(ccc, call)
)
}
@@ -1713,40 +1784,39 @@ private module MkStage<StageSig PrevStage> {
validAp(ap, config)
}
pragma[noinline]
private predicate parameterFlow(
ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
pragma[nomagic]
private predicate parameterFlowsThroughRev(
ParamNodeEx p, Ap ap, ReturnKindExt kind, Configuration config
) {
revFlow(p, _, true, apSome(ap0), ap, config) and
c = p.getEnclosingCallable()
revFlow(p, _, TReturnCtxMaybeFlowThrough(kind), apSome(_), ap, config) and
parameterFlowThroughAllowed(p, kind)
}
predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) {
exists(RetNodeEx ret, FlowState state, Ap ap0, ReturnKindExt kind, ParameterPosition pos |
parameterFlow(p, ap, ap0, c, config) and
c = ret.getEnclosingCallable() and
revFlow(pragma[only_bind_into](ret), pragma[only_bind_into](state), true, apSome(_),
pragma[only_bind_into](ap0), pragma[only_bind_into](config)) and
fwdFlow(ret, state, any(CcCall ccc), apSome(ap), ap0, config) and
kind = ret.getKind() and
p.getPosition() = pos and
// we don't expect a parameter to return stored in itself, unless explicitly allowed
(
not kind.(ParamUpdateReturnKind).getPosition() = pos
or
p.allowParameterReturnInSelf()
)
pragma[nomagic]
predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) {
exists(RetNodeEx ret, ReturnKindExt kind |
returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and
parameterFlowsThroughRev(p, ap, kind, config)
)
}
pragma[nomagic]
predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) {
exists(ParamNodeEx p, Ap ap |
returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and
parameterFlowsThroughRev(p, ap, kind, config)
)
}
pragma[nomagic]
predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) {
exists(
Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap
ReturnKindExt returnKind0, Ap returnAp0, ArgNodeEx arg, FlowState state,
ReturnCtx returnCtx, ApOption returnAp, Ap ap
|
revFlow(arg, state, toReturn, returnAp, ap, config) and
revFlowInToReturn(call, arg, state, returnAp0, ap, config) and
revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
revFlow(arg, state, returnCtx, returnAp, ap, config) and
revFlowInToReturn(call, arg, state, returnKind0, returnAp0, ap, config) and
revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config)
)
}
@@ -1754,14 +1824,13 @@ private module MkStage<StageSig PrevStage> {
boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config
) {
fwd = true and
nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, config)) and
nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, _, config)) and
fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and
conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and
states = count(FlowState state | fwdFlow(_, state, _, _, _, config)) and
states = count(FlowState state | fwdFlow(_, state, _, _, _, _, config)) and
tuples =
count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap |
fwdFlow(n, state, cc, argAp, ap, config)
)
count(NodeEx n, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
Ap ap | fwdFlow(n, state, cc, summaryCtx, argAp, ap, config))
or
fwd = false and
nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and
@@ -1769,8 +1838,8 @@ private module MkStage<StageSig PrevStage> {
conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and
states = count(FlowState state | revFlow(_, state, _, _, _, config)) and
tuples =
count(NodeEx n, FlowState state, boolean b, ApOption retAp, Ap ap |
revFlow(n, state, b, retAp, ap, config)
count(NodeEx n, FlowState state, ReturnCtx returnCtx, ApOption retAp, Ap ap |
revFlow(n, state, returnCtx, retAp, ap, config)
)
}
/* End: Stage logic. */
@@ -1915,7 +1984,7 @@ private module Stage2Param implements MkStage<Stage1>::StageParam {
exists(lcc)
}
predicate flowOutOfCall = flowOutOfCallNodeCand1/5;
predicate flowOutOfCall = flowOutOfCallNodeCand1/6;
predicate flowIntoCall = flowIntoCallNodeCand1/5;
@@ -1951,9 +2020,10 @@ private module Stage2 implements StageSig {
pragma[nomagic]
private predicate flowOutOfCallNodeCand2(
DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config
DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow,
Configuration config
) {
flowOutOfCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and
flowOutOfCallNodeCand1(call, node1, kind, node2, allowsFieldFlow, config) and
Stage2::revFlow(node2, pragma[only_bind_into](config)) and
Stage2::revFlowAlias(node1, pragma[only_bind_into](config))
}
@@ -2021,8 +2091,8 @@ private module LocalFlowBigStep {
exists(NodeEx next | Stage2::revFlow(next, state, config) |
jumpStep(node, next, config) or
additionalJumpStep(node, next, config) or
flowIntoCallNodeCand1(_, node, next, config) or
flowOutOfCallNodeCand1(_, node, next, config) or
flowIntoCallNodeCand2(_, node, next, _, config) or
flowOutOfCallNodeCand2(_, node, _, next, _, config) or
Stage2::storeStepCand(node, _, _, next, _, config) or
Stage2::readStepCand(node, _, next, config)
)
@@ -2163,7 +2233,7 @@ private module Stage3Param implements MkStage<Stage2>::StageParam {
localFlowBigStep(node1, state1, node2, state2, preservesValue, ap, config, _) and exists(lcc)
}
predicate flowOutOfCall = flowOutOfCallNodeCand2/5;
predicate flowOutOfCall = flowOutOfCallNodeCand2/6;
predicate flowIntoCall = flowIntoCallNodeCand2/5;
@@ -2233,8 +2303,9 @@ private predicate flowCandSummaryCtx(
NodeEx node, FlowState state, AccessPathFront argApf, Configuration config
) {
exists(AccessPathFront apf |
Stage3::revFlow(node, state, true, _, apf, config) and
Stage3::fwdFlow(node, state, any(Stage3::CcCall ccc), TAccessPathFrontSome(argApf), apf, config)
Stage3::revFlow(node, state, TReturnCtxMaybeFlowThrough(_), _, apf, config) and
Stage3::fwdFlow(node, state, any(Stage3::CcCall ccc), _, TAccessPathFrontSome(argApf), apf,
config)
)
}
@@ -2468,10 +2539,11 @@ private module Stage4Param implements MkStage<Stage3>::StageParam {
pragma[nomagic]
predicate flowOutOfCall(
DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config
DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow,
Configuration config
) {
exists(FlowState state |
flowOutOfCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and
flowOutOfCallNodeCand2(call, node1, kind, node2, allowsFieldFlow, config) and
PrevStage::revFlow(node2, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) and
PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _,
pragma[only_bind_into](config))
@@ -2508,13 +2580,14 @@ private Configuration unbindConf(Configuration conf) {
pragma[nomagic]
private predicate nodeMayUseSummary0(
NodeEx n, DataFlowCallable c, FlowState state, AccessPathApprox apa, Configuration config
NodeEx n, DataFlowCallable c, ParameterPosition pos, FlowState state, AccessPathApprox apa,
Configuration config
) {
exists(AccessPathApprox apa0 |
Stage4::parameterMayFlowThrough(_, c, _, _) and
Stage4::revFlow(n, state, true, _, apa0, config) and
Stage4::fwdFlow(n, state, any(CallContextCall ccc), TAccessPathApproxSome(apa), apa0, config) and
n.getEnclosingCallable() = c
c = n.getEnclosingCallable() and
Stage4::revFlow(n, state, TReturnCtxMaybeFlowThrough(_), _, apa0, config) and
Stage4::fwdFlow(n, state, any(CallContextCall ccc), TParameterPositionSome(pos),
TAccessPathApproxSome(apa), apa0, config)
)
}
@@ -2522,9 +2595,10 @@ pragma[nomagic]
private predicate nodeMayUseSummary(
NodeEx n, FlowState state, AccessPathApprox apa, Configuration config
) {
exists(DataFlowCallable c |
Stage4::parameterMayFlowThrough(_, c, apa, config) and
nodeMayUseSummary0(n, c, state, apa, config)
exists(DataFlowCallable c, ParameterPosition pos, ParamNodeEx p |
Stage4::parameterMayFlowThrough(p, apa, config) and
nodeMayUseSummary0(n, c, pos, state, apa, config) and
p.isParameterOf(c, pos)
)
}
@@ -2532,7 +2606,7 @@ private newtype TSummaryCtx =
TSummaryCtxNone() or
TSummaryCtxSome(ParamNodeEx p, FlowState state, AccessPath ap) {
exists(Configuration config |
Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), config) and
Stage4::parameterMayFlowThrough(p, ap.getApprox(), config) and
Stage4::revFlow(p, state, _, config)
)
}
@@ -3453,17 +3527,11 @@ private predicate paramFlowsThrough(
ReturnKindExt kind, FlowState state, CallContextCall cc, SummaryCtxSome sc, AccessPath ap,
AccessPathApprox apa, Configuration config
) {
exists(PathNodeMid mid, RetNodeEx ret, ParameterPosition pos |
exists(PathNodeMid mid, RetNodeEx ret |
pathNode(mid, ret, state, cc, sc, ap, config, _) and
kind = ret.getKind() and
apa = ap.getApprox() and
pos = sc.getParameterPos() and
// we don't expect a parameter to return stored in itself, unless explicitly allowed
(
not kind.(ParamUpdateReturnKind).getPosition() = pos
or
sc.getParamNode().allowParameterReturnInSelf()
)
parameterFlowThroughAllowed(sc.getParamNode(), kind)
)
}

View File

@@ -915,6 +915,17 @@ private module Cached {
TDataFlowCallNone() or
TDataFlowCallSome(DataFlowCall call)
cached
newtype TParameterPositionOption =
TParameterPositionNone() or
TParameterPositionSome(ParameterPosition pos)
cached
newtype TReturnCtx =
TReturnCtxNone() or
TReturnCtxNoFlowThrough() or
TReturnCtxMaybeFlowThrough(ReturnKindExt kind)
cached
newtype TTypedContent = MkTypedContent(Content c, DataFlowType t) { store(_, c, _, _, t) }
@@ -1304,6 +1315,44 @@ class DataFlowCallOption extends TDataFlowCallOption {
}
}
/** An optional `ParameterPosition`. */
class ParameterPositionOption extends TParameterPositionOption {
string toString() {
this = TParameterPositionNone() and
result = "(none)"
or
exists(ParameterPosition pos |
this = TParameterPositionSome(pos) and
result = pos.toString()
)
}
}
/**
* A return context used to calculate flow summaries in reverse flow.
*
* The possible values are:
*
* - `TReturnCtxNone()`: no return flow.
* - `TReturnCtxNoFlowThrough()`: return flow, but flow through is not possible.
* - `TReturnCtxMaybeFlowThrough(ReturnKindExt kind)`: return flow, of kind `kind`, and
* flow through may be possible.
*/
class ReturnCtx extends TReturnCtx {
string toString() {
this = TReturnCtxNone() and
result = "(none)"
or
this = TReturnCtxNoFlowThrough() and
result = "(no flow through)"
or
exists(ReturnKindExt kind |
this = TReturnCtxMaybeFlowThrough(kind) and
result = kind.toString()
)
}
}
/** A `Content` tagged with the type of a containing object. */
class TypedContent extends MkTypedContent {
private Content c;

View File

@@ -244,4 +244,20 @@ module Consistency {
not callable = viableCallable(call) and
not any(ConsistencyConfiguration c).viableImplInCallContextTooLargeExclude(call, ctx, callable)
}
query predicate uniqueParameterNodeAtPosition(
DataFlowCallable c, ParameterPosition pos, Node p, string msg
) {
isParameterNode(p, c, pos) and
not exists(unique(Node p0 | isParameterNode(p0, c, pos))) and
msg = "Parameters with overlapping positions."
}
query predicate uniqueParameterNodePosition(
DataFlowCallable c, ParameterPosition pos, Node p, string msg
) {
isParameterNode(p, c, pos) and
not exists(unique(ParameterPosition pos0 | isParameterNode(p, c, pos0))) and
msg = "Parameter node with multiple positions."
}
}

View File

@@ -26,67 +26,3 @@ predicate localFlowStep(Node nodeFrom, Node nodeTo) {
*/
pragma[inline]
predicate localFlow(Node source, Node sink) { localFlowStep*(source, sink) }
/**
* DEPRECATED. Use the API graphs library (`semmle.python.ApiGraphs`) instead.
*
* For a drop-in replacement, use `API::moduleImport(name).getAUse()`.
*
* Gets a `Node` that refers to the module referenced by `name`.
* Note that for the statement `import pkg.mod`, the new variable introduced is `pkg` that is a
* reference to the module `pkg`.
*
* This predicate handles (with optional `... as <new-name>`):
* 1. `import <name>`
* 2. `from <package> import <module>` when `<name> = <package> + "." + <module>`
* 3. `from <module> import <member>` when `<name> = <module> + "." + <member>`
*
* Finally, in `from <module> import <member>` we consider the `ImportExpr` corresponding to
* `<module>` to be a reference to that module.
*
* Note:
* While it is technically possible that `import mypkg.foo` and `from mypkg import foo` can give different values,
* it's highly unlikely that this will be a problem in production level code.
* Example: If `mypkg/__init__.py` contains `foo = 42`, then `from mypkg import foo` will not import the module
* `mypkg/foo.py` but the variable `foo` containing `42` -- however, `import mypkg.foo` will always cause `mypkg.foo`
* to refer to the module.
*/
deprecated Node importNode(string name) {
exists(Variable var, Import imp, Alias alias |
alias = imp.getAName() and
alias.getAsname() = var.getAStore() and
(
name = alias.getValue().(ImportMember).getImportedModuleName()
or
name = alias.getValue().(ImportExpr).getImportedModuleName()
) and
result.asExpr() = alias.getValue()
)
or
// Although it may seem superfluous to consider the `foo` part of `from foo import bar as baz` to
// be a reference to a module (since that reference only makes sense locally within the `import`
// statement), it's important for our use of type trackers to consider this local reference to
// also refer to the `foo` module. That way, if one wants to track references to the `bar`
// attribute using a type tracker, one can simply write
//
// ```ql
// DataFlow::Node bar_attr_tracker(TypeTracker t) {
// t.startInAttr("bar") and
// result = foo_module_tracker()
// or
// exists(TypeTracker t2 | result = bar_attr_tracker(t2).track(t2, t))
// }
// ```
//
// Where `foo_module_tracker` is a type tracker that tracks references to the `foo` module.
// Because named imports are modeled as `AttrRead`s, the statement `from foo import bar as baz`
// is interpreted as if it was an assignment `baz = foo.bar`, which means `baz` gets tracked as a
// reference to `foo.bar`, as desired.
exists(ImportExpr imp_expr |
imp_expr.getName() = name and
result.asCfgNode().getNode() = imp_expr and
// in `import foo.bar` we DON'T want to give a result for `importNode("foo.bar")`,
// only for `importNode("foo")`. We exclude those cases with the following clause.
not exists(Import imp | imp.getAName().getValue() = imp_expr)
)
}

View File

@@ -246,14 +246,14 @@ module Public {
predicate isAutoGenerated() { none() }
}
/** A callable with a flow summary stating there is no flow via the callable. */
class NegativeSummarizedCallable extends SummarizedCallableBase {
NegativeSummarizedCallable() { negativeSummaryElement(this, _) }
/** A callable where there is no flow via the callable. */
class NeutralCallable extends SummarizedCallableBase {
NeutralCallable() { neutralElement(this, _) }
/**
* Holds if the negative summary is auto generated.
* Holds if the neutral is auto generated.
*/
predicate isAutoGenerated() { negativeSummaryElement(this, true) }
predicate isAutoGenerated() { neutralElement(this, true) }
}
}
@@ -520,7 +520,8 @@ module Private {
predicate summaryParameterNodeRange(SummarizedCallable c, ParameterPosition pos) {
parameterReadState(c, _, pos)
or
isParameterPostUpdate(_, c, pos)
// Same as `isParameterPostUpdate(_, c, pos)`, but can be used in a negative context
any(SummaryNodeState state).isOutputState(c, SummaryComponentStack::argument(pos))
}
private predicate callbackOutput(
@@ -1160,9 +1161,9 @@ module Private {
string toString() { result = super.toString() }
}
/** A flow summary to include in the `negativeSummary/1` query predicate. */
abstract class RelevantNegativeSummarizedCallable instanceof NegativeSummarizedCallable {
/** Gets the string representation of this callable used by `summary/1`. */
/** A model to include in the `neutral/1` query predicate. */
abstract class RelevantNeutralCallable instanceof NeutralCallable {
/** Gets the string representation of this callable used by `neutral/1`. */
abstract string getCallableCsv();
string toString() { result = super.toString() }
@@ -1179,13 +1180,13 @@ module Private {
if c.isAutoGenerated() then result = "generated" else result = "manual"
}
private string renderProvenanceNegative(NegativeSummarizedCallable c) {
private string renderProvenanceNeutral(NeutralCallable c) {
if c.isAutoGenerated() then result = "generated" else result = "manual"
}
/**
* A query predicate for outputting flow summaries in semi-colon separated format in QL tests.
* The syntax is: "namespace;type;overrides;name;signature;ext;inputspec;outputspec;kind;provenance"",
* The syntax is: "namespace;type;overrides;name;signature;ext;inputspec;outputspec;kind;provenance",
* ext is hardcoded to empty.
*/
query predicate summary(string csv) {
@@ -1204,14 +1205,14 @@ module Private {
}
/**
* Holds if a negative flow summary `csv` exists (semi-colon separated format). Used for testing purposes.
* Holds if a neutral model `csv` exists (semi-colon separated format). Used for testing purposes.
* The syntax is: "namespace;type;name;signature;provenance"",
*/
query predicate negativeSummary(string csv) {
exists(RelevantNegativeSummarizedCallable c |
query predicate neutral(string csv) {
exists(RelevantNeutralCallable c |
csv =
c.getCallableCsv() // Callable information
+ renderProvenanceNegative(c) // provenance
+ renderProvenanceNeutral(c) // provenance
)
}
}

View File

@@ -92,11 +92,11 @@ predicate summaryElement(
}
/**
* Holds if a negative flow summary exists for `c`, which means that there is no
* flow through `c`. The flag `generated` states whether the summary is autogenerated.
* Note. Negative flow summaries has not been implemented for Python.
* Holds if a neutral model exists for `c`, which means that there is no
* flow through `c`. The flag `generated` states whether the neutral model is autogenerated.
* Note. Neutral models have not been implemented for Python.
*/
predicate negativeSummaryElement(FlowSummary::SummarizedCallable c, boolean generated) { none() }
predicate neutralElement(FlowSummary::SummarizedCallable c, boolean generated) { none() }
/**
* Gets the summary component for specification component `c`, if any.

View File

@@ -1,14 +1,75 @@
/**
* INTERNAL. DO NOT USE.
*
* Provides predicates for resolving imports.
*/
private import python
private import semmle.python.dataflow.new.DataFlow
private import semmle.python.dataflow.new.internal.ImportStar
private import semmle.python.dataflow.new.TypeTracker
private import semmle.python.dataflow.new.internal.DataFlowPrivate
/**
* Python modules and the way imports are resolved are... complicated. Here's a crash course in how
* it works, as well as some caveats to bear in mind when looking at the implementation in this
* module.
*
* First, let's consider the humble `import` statement:
* ```python
* import foo
* import bar.baz
* import ham.eggs as spam
* ```
*
* In the AST, all imports are aliased, as in the last import above. That is, `import foo` becomes
* `import foo as foo`, and `import bar.baz` becomes `import bar as bar`. Note that `import` is
* exclusively used to import modules -- if `eggs` is an attribute of the `ham` module (and not a
* submodule of the `ham` package), then the third line above is an error.
*
* Next, we have the `from` statement. This one is a bit more complicated, but still has the same
* aliasing desugaring as above applied to it. Thus, `from foo import bar` becomes
* `from foo import bar as bar`.
*
* In general, `from foo import bar` can mean two different things:
*
* 1. If `foo` is a module, and `bar` is an attribute of `foo`, then `from foo import bar` imports
* the attribute `bar` into the current module (binding it to the name `bar`).
* 2. If `foo` is a package, and `bar` is already defined in `foo/__init__.py`,
* that value will be imported. If it is not defined, and `bar` is a submodule of `foo`, then
* `bar` is imported to `foo`, and the `bar` submodule imported.
* Note: We don't currently model if the attribute is already defined in `__init__.py`
* and always assume that the submodule will be used.
*
* Now, when it comes to how these imports are represented in the AST, things get a bit complicated.
* First of all, both of the above forms of imports get mapped to the same kind of AST node:
* `Import`. An `Import` node has a sequence of names, each of which is an `Alias` node. This `Alias`
* node represents the `x as y` bit of each imported module.
*
* The same is true for `from` imports. So, how then do we distinguish between the two forms of
* imports? The distinguishing feature is the left hand side of the `as` node. If the left hand side
* is an `ImportExpr`, then it is a plain import. If it is an `ImportMember`, then it is a `from`
* import. (And to confuse matters even more, this `ImportMember` contains another `ImportExpr` for
* the bit between the `from` and `import` keywords.)
*
* Caveats:
*
* - A relative import of the form `from .foo import bar as baz` not only imports `bar` and binds it
* to the name `baz`, but also imports `foo` and binds it to the name `foo`. This only happens with
* relative imports. `from foo import bar as baz` only binds `bar` to `baz`.
* - Modules may also be packages, so e.g. `import foo.bar` may import the `bar` submodule in the `foo`
* package, or the `bar` subpackage of the `foo` package. The practical difference here is the name of
* the module that is imported, as the package `foo.bar` will have the "name" `foo.bar.__init__`,
* corresponding to the fact that the code that is executed is in the `__init__.py` file of the
* `bar` subpackage.
*/
module ImportResolution {
/**
* Holds if the module `m` defines a name `name` by assigning `defn` to it. This is an
* overapproximation, as `name` may not in fact be exported (e.g. by defining an `__all__` that does
* not include `name`).
*/
pragma[nomagic]
predicate module_export(Module m, string name, DataFlow::CfgNode defn) {
exists(EssaVariable v |
v.getName() = name and
@@ -18,12 +79,223 @@ module ImportResolution {
or
defn.getNode() = v.getDefinition().(ArgumentRefinement).getArgument()
)
}
Module getModule(DataFlow::CfgNode node) {
exists(ModuleValue mv |
node.getNode().pointsTo(mv) and
result = mv.getScope()
or
exists(Alias a |
defn.asExpr() = [a.getValue(), a.getValue().(ImportMember).getModule()] and
a.getAsname().(Name).getId() = name and
defn.getScope() = m
)
}
/**
* Holds if the module `m` explicitly exports the name `name` by listing it in `__all__`. Only
* handles simple cases where we can statically tell that this is the case.
*/
private predicate all_mentions_name(Module m, string name) {
exists(DefinitionNode def, SequenceNode n |
def.getValue() = n and
def.(NameNode).getId() = "__all__" and
def.getScope() = m and
any(StrConst s | s.getText() = name) = n.getAnElement().getNode()
)
}
/**
* Holds if the module `m` either does not set `__all__` (and so implicitly exports anything that
* doesn't start with an underscore), or sets `__all__` in a way that's too complicated for us to
* handle (in which case we _also_ pretend that it just exports all such names).
*/
private predicate no_or_complicated_all(Module m) {
// No mention of `__all__` in the module
not exists(DefinitionNode def | def.getScope() = m and def.(NameNode).getId() = "__all__")
or
// `__all__` is set to a non-sequence value
exists(DefinitionNode def |
def.(NameNode).getId() = "__all__" and
def.getScope() = m and
not def.getValue() instanceof SequenceNode
)
or
// `__all__` is used in some way that doesn't involve storing a value in it. This usually means
// it is being mutated through `append` or `extend`, which we don't handle.
exists(NameNode n | n.getId() = "__all__" and n.getScope() = m and n.isLoad())
}
private predicate potential_module_export(Module m, string name) {
all_mentions_name(m, name)
or
no_or_complicated_all(m) and
(
exists(NameNode n | n.getId() = name and n.getScope() = m and name.charAt(0) != "_")
or
exists(Alias a | a.getAsname().(Name).getId() = name and a.getValue().getScope() = m)
)
}
/**
* Holds if the module `reexporter` exports the module `reexported` under the name
* `reexported_name`.
*/
private predicate module_reexport(Module reexporter, string reexported_name, Module reexported) {
exists(DataFlow::Node ref |
ref = getImmediateModuleReference(reexported) and
module_export(reexporter, reexported_name, ref) and
potential_module_export(reexporter, reexported_name)
)
}
/**
* Gets a reference to `sys.modules`.
*/
private DataFlow::Node sys_modules_reference() {
result =
any(DataFlow::AttrRef a |
a.getAttributeName() = "modules" and a.getObject().asExpr().(Name).getId() = "sys"
)
}
/** Gets a module that may have been added to `sys.modules`. */
private Module sys_modules_module_with_name(string name) {
exists(ControlFlowNode n, DataFlow::Node mod |
exists(SubscriptNode sub |
sub.getObject() = sys_modules_reference().asCfgNode() and
sub.getIndex() = n and
n.getNode().(StrConst).getText() = name and
sub.(DefinitionNode).getValue() = mod.asCfgNode() and
mod = getModuleReference(result)
)
)
}
Module getModuleImportedByImportStar(ImportStar i) {
isPreferredModuleForName(result.getFile(), i.getImportedModuleName())
}
/**
* Gets a data-flow node that may be a reference to a module with the name `module_name`.
*
* This is a helper predicate for `getImmediateModuleReference`. It captures the fact that in an
* import such as `import foo`,
* - `foo` may simply be the name of a module, or
* - `foo` may be the name of a package (in which case its name is actually `foo.__init__`), or
* - `foo` may be a module name that has been added to `sys.modules` (in which case its actual name can
* be anything, for instance `os.path` is either `posixpath` or `ntpath`).
*/
private DataFlow::Node getReferenceToModuleName(string module_name) {
// Regular import statements, e.g.
// import foo # implicitly `import foo as foo`
// import foo as foo_alias
exists(Import i, Alias a | a = i.getAName() |
result.asExpr() = a.getAsname() and
module_name = a.getValue().(ImportExpr).getImportedModuleName()
)
or
// The module part of a `from ... import ...` statement, e.g. the `..foo.bar` in
// from ..foo.bar import baz # ..foo.bar might point to, say, package.subpackage.foo.bar
exists(ImportMember i | result.asExpr() = i.getModule() |
module_name = i.getModule().(ImportExpr).getImportedModuleName()
)
or
// Modules (not attributes) imported via `from ... import ... statements`, e.g.
// from foo.bar import baz # imports foo.bar.baz as baz
// from foo.bar import baz as baz_alias # imports foo.bar.baz as baz_alias
exists(Import i, Alias a, ImportMember im | a = i.getAName() and im = a.getValue() |
result.asExpr() = a.getAsname() and
module_name = im.getModule().(ImportExpr).getImportedModuleName() + "." + im.getName()
)
or
// For parity with the points-to based solution, the `ImportExpr` and `ImportMember` bits of the
// above cases should _also_ point to the right modules.
result.asExpr() = any(ImportExpr i | i.getImportedModuleName() = module_name)
or
result.asExpr() =
any(ImportMember i |
i.getModule().(ImportExpr).getImportedModuleName() + "." + i.getName() = module_name
)
}
/**
* Gets a dataflow node that is an immediate reference to the module `m`.
*
* Because of attribute lookups, this is mutually recursive with `getModuleReference`.
*/
DataFlow::Node getImmediateModuleReference(Module m) {
exists(string module_name | result = getReferenceToModuleName(module_name) |
// Depending on whether the referenced module is a package or not, we may need to add a
// trailing `.__init__` to the module name.
isPreferredModuleForName(m.getFile(), module_name + ["", ".__init__"])
or
// Module defined via `sys.modules`
m = sys_modules_module_with_name(module_name)
)
or
// Reading an attribute on a module may return a submodule (or subpackage).
exists(DataFlow::AttrRead ar, Module p, string attr_name |
ar.accesses(getModuleReference(p), attr_name) and
result = ar
|
isPreferredModuleForName(m.getFile(), p.getPackageName() + "." + attr_name + ["", ".__init__"])
)
or
// This is also true for attributes that come from reexports.
exists(Module reexporter, string attr_name |
result.(DataFlow::AttrRead).accesses(getModuleReference(reexporter), attr_name) and
module_reexport(reexporter, attr_name, m)
)
or
// Submodules that are implicitly defined with relative imports of the form `from .foo import ...`.
// In practice, we create a definition for each module in a package, even if it is not imported.
exists(string submodule, Module package |
SsaSource::init_module_submodule_defn(result.asVar().getSourceVariable(),
package.getEntryNode()) and
isPreferredModuleForName(m.getFile(),
package.getPackageName() + "." + submodule + ["", ".__init__"])
)
}
/** Join-order helper for `getModuleReference`. */
pragma[nomagic]
private predicate module_reference_in_scope(DataFlow::Node node, Scope s, string name, Module m) {
node.getScope() = s and
node.asExpr().(Name).getId() = name and
pragma[only_bind_into](node) = getImmediateModuleReference(pragma[only_bind_into](m))
}
/** Join-order helper for `getModuleReference`. */
pragma[nomagic]
private predicate module_name_in_scope(DataFlow::Node node, Scope s, string name) {
node.getScope() = s and
exists(Name n | n = node.asExpr() |
n.getId() = name and
pragma[only_bind_into](n).isUse()
)
}
/**
* Gets a reference to the module `m` (including through certain kinds of local and global flow).
*/
DataFlow::Node getModuleReference(Module m) {
// Immedate references to the module
result = getImmediateModuleReference(m)
or
// Flow (local or global) forward to a later reference to the module.
exists(DataFlow::Node ref | ref = getModuleReference(m) |
simpleLocalFlowStepForTypetracking(ref, result)
or
exists(DataFlow::ModuleVariableNode mv |
mv.getAWrite() = ref and
result = mv.getARead()
)
)
or
// A reference to a name that is bound to a module in an enclosing scope.
exists(DataFlow::Node def, Scope def_scope, Scope use_scope, string name |
module_reference_in_scope(pragma[only_bind_into](def), pragma[only_bind_into](def_scope),
pragma[only_bind_into](name), pragma[only_bind_into](m)) and
module_name_in_scope(result, use_scope, name) and
use_scope.getEnclosingScope*() = def_scope
)
}
Module getModule(DataFlow::CfgNode node) { node = getModuleReference(result) }
}

View File

@@ -76,7 +76,7 @@ module ImportStar {
exists(ImportStar i, DataFlow::CfgNode imported_module |
imported_module.getNode().getNode() = i.getModule() and
i.getScope() = m and
result = ImportResolution::getModule(imported_module)
result = ImportResolution::getModuleImportedByImportStar(i)
)
}

View File

@@ -13,35 +13,36 @@ private import semmle.python.frameworks.data.ModelsAsData
private module Asyncpg {
class AsyncpgModel extends ModelInput::TypeModelCsv {
override predicate row(string row) {
// package1;type1;package2;type2;path
// type1;type2;path
row =
[
// a `ConnectionPool` that is created when the result of `asyncpg.create_pool()` is awaited.
"asyncpg;ConnectionPool;asyncpg;;Member[create_pool].ReturnValue.Awaited",
"asyncpg.ConnectionPool;asyncpg;Member[create_pool].ReturnValue.Awaited",
// a `Connection` that is created when
// * - the result of `asyncpg.connect()` is awaited.
// * - the result of calling `acquire` on a `ConnectionPool` is awaited.
"asyncpg;Connection;asyncpg;;Member[connect].ReturnValue.Awaited",
"asyncpg;Connection;asyncpg;ConnectionPool;Member[acquire].ReturnValue.Awaited",
"asyncpg.Connection;asyncpg;Member[connect].ReturnValue.Awaited",
"asyncpg.Connection;asyncpg.ConnectionPool;Member[acquire].ReturnValue.Awaited",
// Creating an internal `~Connection` type that contains both `Connection` and `ConnectionPool`.
"asyncpg;~Connection;asyncpg;Connection;", "asyncpg;~Connection;asyncpg;ConnectionPool;"
"asyncpg.~Connection;asyncpg.Connection;", //
"asyncpg.~Connection;asyncpg.ConnectionPool;"
]
}
}
class AsyncpgSink extends ModelInput::SinkModelCsv {
// package;type;path;kind
// type;path;kind
override predicate row(string row) {
row =
[
// `Connection`s and `ConnectionPool`s provide some methods that execute SQL.
"asyncpg;~Connection;Member[copy_from_query,execute,fetch,fetchrow,fetchval].Argument[0,query:];sql-injection",
"asyncpg;~Connection;Member[executemany].Argument[0,command:];sql-injection",
"asyncpg.~Connection;Member[copy_from_query,execute,fetch,fetchrow,fetchval].Argument[0,query:];sql-injection",
"asyncpg.~Connection;Member[executemany].Argument[0,command:];sql-injection",
// A model of `Connection` and `ConnectionPool`, which provide some methods that access the file system.
"asyncpg;~Connection;Member[copy_from_query,copy_from_table].Argument[output:];path-injection",
"asyncpg;~Connection;Member[copy_to_table].Argument[source:];path-injection",
"asyncpg.~Connection;Member[copy_from_query,copy_from_table].Argument[output:];path-injection",
"asyncpg.~Connection;Member[copy_to_table].Argument[source:];path-injection",
// the `PreparedStatement` class in `asyncpg`.
"asyncpg;Connection;Member[prepare].Argument[0,query:];sql-injection",
"asyncpg.Connection;Member[prepare].Argument[0,query:];sql-injection",
]
}
}
@@ -58,7 +59,7 @@ private module Asyncpg {
module Cursor {
class CursorConstruction extends SqlConstruction::Range, API::CallNode {
CursorConstruction() {
this = ModelOutput::getATypeNode("asyncpg", "Connection").getMember("cursor").getACall()
this = ModelOutput::getATypeNode("asyncpg.Connection").getMember("cursor").getACall()
}
override DataFlow::Node getSql() { result = this.getParameter(0, "query").asSink() }
@@ -76,7 +77,7 @@ private module Asyncpg {
or
exists(API::CallNode prepareCall |
prepareCall =
ModelOutput::getATypeNode("asyncpg", "Connection").getMember("prepare").getACall()
ModelOutput::getATypeNode("asyncpg.Connection").getMember("prepare").getACall()
|
sql = prepareCall.getParameter(0, "query").asSink() and
this =

View File

@@ -8,29 +8,170 @@ private import semmle.python.dataflow.new.DataFlow
private import semmle.python.dataflow.new.RemoteFlowSources
private import semmle.python.Concepts
private import semmle.python.ApiGraphs
import semmle.python.frameworks.internal.PEP249Impl
/**
* DEPRECATED: Use `PEP249::PEP249ModuleApiNode` instead.
* Provides classes modeling database interfaces following PEP 249.
* See https://www.python.org/dev/peps/pep-0249/.
*/
deprecated class PEP249ModuleApiNode = PEP249::PEP249ModuleApiNode;
module PEP249 {
/**
* An API graph node representing a module that implements PEP 249.
*/
abstract class PEP249ModuleApiNode extends API::Node {
/** Gets a string representation of this element. */
override string toString() { result = this.(API::Node).toString() }
}
/**
* DEPRECATED: Use `PEP249::Connection` instead.
*/
deprecated module Connection = PEP249::Connection;
/** Gets a reference to the `connect` function of a module that implements PEP 249. */
DataFlow::Node connect() {
result = any(PEP249ModuleApiNode a).getMember("connect").getAValueReachableFromSource()
}
/**
* DEPRECATED: Use `PEP249::Cursor` instead.
*/
deprecated module cursor = PEP249::Cursor;
/**
* Provides models for database connections (following PEP 249).
*
* See https://www.python.org/dev/peps/pep-0249/#connection-objects.
*/
module Connection {
/**
* A source of database connections (following PEP 249), extend this class to model new instances.
*
* This can include instantiations of the class, return values from function
* calls, or a special parameter that will be set when functions are called by external
* libraries.
*
* Use the predicate `Connection::instance()` to get references to database connections (following PEP 249).
*
* Extend this class if the module implementing PEP 249 offers more direct ways to obtain
* a connection than going through `connect`.
*/
abstract class InstanceSource extends DataFlow::Node { }
/**
* DEPRECATED: Use `PEP249::execute` instead.
*/
deprecated predicate execute = PEP249::execute/0;
/** A call to the `connect` function of a module that implements PEP 249. */
private class ConnectCall extends InstanceSource, DataFlow::CallCfgNode {
ConnectCall() { this.getFunction() = connect() }
}
/**
* DEPRECATED: Use `PEP249::connect` instead.
*/
deprecated predicate connect = PEP249::connect/0;
/** Gets a reference to a database connection (following PEP 249). */
private DataFlow::TypeTrackingNode instance(DataFlow::TypeTracker t) {
t.start() and
result instanceof InstanceSource
or
exists(DataFlow::TypeTracker t2 | result = instance(t2).track(t2, t))
}
/** Gets a reference to a database connection (following PEP 249). */
DataFlow::Node instance() { instance(DataFlow::TypeTracker::end()).flowsTo(result) }
}
/**
* Provides models for database cursors (following PEP 249).
*
* These are returned by the `cursor` method on a database connection.
* See https://www.python.org/dev/peps/pep-0249/#cursor.
*/
module Cursor {
/**
* A source of database cursors (following PEP 249), extend this class to model new instances.
*
* This can include instantiations of the class, return values from function
* calls, or a special parameter that will be set when functions are called by external
* libraries.
*
* Use the predicate `Cursor::instance()` to get references to database cursors (following PEP 249).
*
* Extend this class if the module implementing PEP 249 offers more direct ways to obtain
* a connection than going through `connect`.
*/
abstract class InstanceSource extends DataFlow::LocalSourceNode { }
/** Gets a reference to a database cursor. */
private DataFlow::TypeTrackingNode instance(DataFlow::TypeTracker t) {
t.start() and
result instanceof InstanceSource
or
exists(DataFlow::TypeTracker t2 | result = instance(t2).track(t2, t))
}
/** Gets a reference to a database cursor. */
DataFlow::Node instance() { instance(DataFlow::TypeTracker::end()).flowsTo(result) }
/** Gets a reference to the `cursor` method on a database connection. */
private DataFlow::TypeTrackingNode methodRef(DataFlow::TypeTracker t) {
t.startInAttr("cursor") and
result = Connection::instance()
or
exists(DataFlow::TypeTracker t2 | result = methodRef(t2).track(t2, t))
}
/** Gets a reference to the `cursor` method on a database connection. */
DataFlow::Node methodRef() { methodRef(DataFlow::TypeTracker::end()).flowsTo(result) }
/** A call to the `cursor` method on a database connection */
private class CursorCall extends InstanceSource, DataFlow::CallCfgNode {
CursorCall() { this.getFunction() = methodRef() }
}
}
/**
* Gets a reference to the `execute` method on a cursor (or on a connection).
*
* Note: while `execute` method on a connection is not part of PEP249, if it is used, we
* recognize it as an alias for constructing a cursor and calling `execute` on it.
*
* See https://peps.python.org/pep-0249/#execute.
*/
private DataFlow::TypeTrackingNode execute(DataFlow::TypeTracker t) {
t.startInAttr("execute") and
result in [Cursor::instance(), Connection::instance()]
or
exists(DataFlow::TypeTracker t2 | result = execute(t2).track(t2, t))
}
/**
* Gets a reference to the `execute` method on a cursor (or on a connection).
*
* Note: while `execute` method on a connection is not part of PEP249, if it is used, we
* recognize it as an alias for constructing a cursor and calling `execute` on it.
*
* See https://peps.python.org/pep-0249/#execute.
*/
DataFlow::Node execute() { execute(DataFlow::TypeTracker::end()).flowsTo(result) }
/**
* A call to the `execute` method on a cursor or a connection.
*
* See https://peps.python.org/pep-0249/#execute
*
* Note: While `execute` method on a connection is not part of PEP249, if it is used, we
* recognize it as an alias for constructing a cursor and calling `execute` on it.
*/
private class ExecuteCall extends SqlExecution::Range, DataFlow::CallCfgNode {
ExecuteCall() { this.getFunction() = execute() }
override DataFlow::Node getSql() { result in [this.getArg(0), this.getArgByName("sql")] }
}
private DataFlow::TypeTrackingNode executemany(DataFlow::TypeTracker t) {
t.startInAttr("executemany") and
result in [Cursor::instance(), Connection::instance()]
or
exists(DataFlow::TypeTracker t2 | result = executemany(t2).track(t2, t))
}
private DataFlow::Node executemany() { executemany(DataFlow::TypeTracker::end()).flowsTo(result) }
/**
* A call to the `executemany` method on a cursor or a connection.
*
* See https://peps.python.org/pep-0249/#executemany
*
* Note: While `executemany` method on a connection is not part of PEP249, if it is used, we
* recognize it as an alias for constructing a cursor and calling `executemany` on it.
*/
private class ExecutemanyCall extends SqlExecution::Range, DataFlow::CallCfgNode {
ExecutemanyCall() { this.getFunction() = executemany() }
override DataFlow::Node getSql() { result in [this.getArg(0), this.getArgByName("sql")] }
}
}

View File

@@ -1163,13 +1163,16 @@ private module StdlibPrivate {
API::Node subprocess() { result = API::moduleImport("subprocess") }
/**
* A call to `subprocess.Popen` or helper functions (call, check_call, check_output, run)
* A call to `subprocess.Popen` or helper functions (call, check_call, check_output, run, getoutput, getstatusoutput)
* See https://docs.python.org/3.8/library/subprocess.html#subprocess.Popen
* ref: https://docs.python.org/3/library/subprocess.html#legacy-shell-invocation-functions
*/
private class SubprocessPopenCall extends SystemCommandExecution::Range, DataFlow::CallCfgNode {
SubprocessPopenCall() {
exists(string name |
name in ["Popen", "call", "check_call", "check_output", "run"] and
name in [
"Popen", "call", "check_call", "check_output", "run", "getoutput", "getstatusoutput"
] and
this = subprocess().getMember(name).getACall()
)
}

View File

@@ -231,119 +231,4 @@ module Werkzeug {
override string getAsyncMethodName() { none() }
}
}
import WerkzeugOld
}
/**
* Old version that contains the deprecated modules.
*/
private module WerkzeugOld {
/**
* DEPRECATED: Use the modeling available directly in the `Werkzeug` module instead.
*
* Provides models for the `werkzeug` module.
*/
deprecated module werkzeug {
/**
* DEPRECATED: Use the modeling available directly in the `Werkzeug` module instead.
*
* Provides models for the `werkzeug.datastructures` module.
*/
deprecated module datastructures {
/**
* DEPRECATED: Use `Werkzeug::MultiDict` instead.
*
* Provides models for the `werkzeug.datastructures.MultiDict` class
*
* See https://werkzeug.palletsprojects.com/en/1.0.x/datastructures/#werkzeug.datastructures.MultiDict.
*/
deprecated module MultiDict {
/**
* DEPRECATED. Use `Werkzeug::MultiDict::InstanceSource` instead.
*
* A source of instances of `werkzeug.datastructures.MultiDict`, extend this class to model new instances.
*
* This can include instantiations of the class, return values from function
* calls, or a special parameter that will be set when functions are called by an external
* library.
*
* Use the predicate `MultiDict::instance()` to get references to instances of `werkzeug.datastructures.MultiDict`.
*/
abstract deprecated class InstanceSourceApiNode extends API::Node { }
/**
* DEPRECATED
*
* Gets a reference to the `getlist` method on an instance of `werkzeug.datastructures.MultiDict`.
*
* See https://werkzeug.palletsprojects.com/en/1.0.x/datastructures/#werkzeug.datastructures.Headers.getlist
*/
deprecated DataFlow::Node getlist() {
result = any(InstanceSourceApiNode a).getMember("getlist").getAValueReachableFromSource()
}
private class MultiDictAdditionalTaintStep extends TaintTracking::AdditionalTaintStep {
override predicate step(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
// obj -> obj.getlist
exists(DataFlow::AttrRead read |
read.getObject() = nodeFrom and
nodeTo = read and
nodeTo = getlist()
)
or
// getlist -> getlist()
nodeFrom = getlist() and
nodeTo.(DataFlow::CallCfgNode).getFunction() = nodeFrom
}
}
}
/**
* DEPRECATED: Use `Werkzeug::FileStorage` instead.
*
* Provides models for the `werkzeug.datastructures.FileStorage` class
*
* See https://werkzeug.palletsprojects.com/en/1.0.x/datastructures/#werkzeug.datastructures.FileStorage.
*/
deprecated module FileStorage {
/**
* DEPRECATED. Use `Werkzeug::FileStorage::InstanceSource` instead.
*
* A source of instances of `werkzeug.datastructures.FileStorage`, extend this class to model new instances.
*
* This can include instantiations of the class, return values from function
* calls, or a special parameter that will be set when functions are called by an external
* library.
*
* Use the predicate `FileStorage::instance()` to get references to instances of `werkzeug.datastructures.FileStorage`.
*/
abstract deprecated class InstanceSourceApiNode extends API::Node { }
/** Gets a reference to an instance of `werkzeug.datastructures.FileStorage`. */
deprecated DataFlow::Node instance() {
result = any(InstanceSourceApiNode a).getAValueReachableFromSource()
}
private class FileStorageAdditionalTaintStep extends TaintTracking::AdditionalTaintStep {
override predicate step(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
nodeFrom = instance() and
exists(DataFlow::AttrRead read | nodeTo = read |
read.getAttributeName() in [
// str
"filename", "name", "content_type", "mimetype",
// file-like
"stream",
// TODO: werkzeug.datastructures.Headers
"headers",
// dict[str, str]
"mimetype_params"
] and
read.getObject() = nodeFrom
)
}
}
}
}
}
}

View File

@@ -5,23 +5,20 @@
*
* The CSV specification has the following columns:
* - Sources:
* `package; type; path; kind`
* `type; path; kind`
* - Sinks:
* `package; type; path; kind`
* `type; path; kind`
* - Summaries:
* `package; type; path; input; output; kind`
* `type; path; input; output; kind`
* - Types:
* `package1; type1; package2; type2; path`
* `type1; type2; path`
*
* The interpretation of a row is similar to API-graphs with a left-to-right
* reading.
* 1. The `package` column selects a package name, as it would be referenced in the source code,
* such as an NPM package, PIP package, or Ruby gem. (See `ModelsAsData.qll` for language-specific details).
* It may also be a synthetic package used for a type definition (see type definitions below).
* 2. The `type` column selects all instances of a named type originating from that package,
* or the empty string if referring to the package itself.
* 1. The `type` column selects all instances of a named type. The syntax of this column is language-specific.
* The language defines some type names that the analysis knows how to identify without models.
* It can also be a synthetic type name defined by a type definition (see type definitions below).
* 3. The `path` column is a `.`-separated list of "access path tokens" to resolve, starting at the node selected by `package` and `type`.
* 2. The `path` column is a `.`-separated list of "access path tokens" to resolve, starting at the node selected by `type`.
*
* Every language supports the following tokens:
* - Argument[n]: the n-th argument to a call. May be a range of form `x..y` (inclusive) and/or a comma-separated list.
@@ -42,10 +39,10 @@
*
* For the time being, please consult `ApiGraphModelsSpecific.qll` to see which language-specific tokens are currently supported.
*
* 4. The `input` and `output` columns specify how data enters and leaves the element selected by the
* first `(package, type, path)` tuple. Both strings are `.`-separated access paths
* 3. The `input` and `output` columns specify how data enters and leaves the element selected by the
* first `(type, path)` tuple. Both strings are `.`-separated access paths
* of the same syntax as the `path` column.
* 5. The `kind` column is a tag that can be referenced from QL to determine to
* 4. The `kind` column is a tag that can be referenced from QL to determine to
* which classes the interpreted elements should be added. For example, for
* sources `"remote"` indicates a default remote flow source, and for summaries
* `"taint"` indicates a default additional taint step and `"value"` indicates a
@@ -53,17 +50,17 @@
*
* ### Types
*
* A type row of form `package1; type1; package2; type2; path` indicates that `package2; type2; path`
* should be seen as an instance of the type `package1; type1`.
* A type row of form `type1; type2; path` indicates that `type2; path`
* should be seen as an instance of the type `type1`.
*
* A `(package,type)` pair may refer to a static type or a synthetic type name used internally in the model.
* A type may refer to a static type or a synthetic type name used internally in the model.
* Synthetic type names can be used to reuse intermediate sub-paths, when there are multiple ways to access the same
* element.
* See `ModelsAsData.qll` for the language-specific interpretation of packages and static type names.
* See `ModelsAsData.qll` for the language-specific interpretation of type names.
*
* By convention, if one wants to avoid clashes with static types from the package, the type name
* should be prefixed with a tilde character (`~`). For example, `(foo, ~Bar)` can be used to indicate that
* the type is related to the `foo` package but is not intended to match a static type.
* By convention, if one wants to avoid clashes with static types, the type name
* should be prefixed with a tilde character (`~`). For example, `~Bar` can be used to indicate that
* the type is not intended to match a static type.
*/
private import ApiGraphModelsSpecific as Specific
@@ -75,6 +72,7 @@ private module API = Specific::API;
private module DataFlow = Specific::DataFlow;
private import Specific::AccessPathSyntax
private import ApiGraphModelsExtensions as Extensions
/** Module containing hooks for providing input data to be interpreted as a model. */
module ModelInput {
@@ -89,9 +87,9 @@ module ModelInput {
*
* A row of form
* ```
* package;type;path;kind
* type;path;kind
* ```
* indicates that the value at `(package, type, path)` should be seen as a flow
* indicates that the value at `(type, path)` should be seen as a flow
* source of the given `kind`.
*
* The kind `remote` represents a general remote flow source.
@@ -110,9 +108,9 @@ module ModelInput {
*
* A row of form
* ```
* package;type;path;kind
* type;path;kind
* ```
* indicates that the value at `(package, type, path)` should be seen as a sink
* indicates that the value at `(type, path)` should be seen as a sink
* of the given `kind`.
*/
abstract predicate row(string row);
@@ -129,9 +127,9 @@ module ModelInput {
*
* A row of form
* ```
* package;type;path;input;output;kind
* type;path;input;output;kind
* ```
* indicates that for each call to `(package, type, path)`, the value referred to by `input`
* indicates that for each call to `(type, path)`, the value referred to by `input`
* can flow to the value referred to by `output`.
*
* `kind` should be either `value` or `taint`, for value-preserving or taint-preserving steps,
@@ -151,9 +149,9 @@ module ModelInput {
*
* A row of form,
* ```
* package1;type1;package2;type2;path
* type1;type2;path
* ```
* indicates that `(package2, type2, path)` should be seen as an instance of `(package1, type1)`.
* indicates that `(type2, path)` should be seen as an instance of `type1`.
*/
abstract predicate row(string row);
}
@@ -163,28 +161,28 @@ module ModelInput {
*/
class TypeModel extends Unit {
/**
* Gets a data-flow node that is a source of the type `package;type`.
* Gets a data-flow node that is a source of the given `type`.
*
* This must not depend on API graphs, but ensures that an API node is generated for
* the source.
*/
DataFlow::Node getASource(string package, string type) { none() }
DataFlow::Node getASource(string type) { none() }
/**
* Gets a data-flow node that is a sink of the type `package;type`,
* Gets a data-flow node that is a sink of the given `type`,
* usually because it is an argument passed to a parameter of that type.
*
* This must not depend on API graphs, but ensures that an API node is generated for
* the sink.
*/
DataFlow::Node getASink(string package, string type) { none() }
DataFlow::Node getASink(string type) { none() }
/**
* Gets an API node that is a source or sink of the type `package;type`.
* Gets an API node that is a source or sink of the given `type`.
*
* Unlike `getASource` and `getASink`, this may depend on API graphs.
*/
API::Node getAnApiNode(string package, string type) { none() }
API::Node getAnApiNode(string type) { none() }
}
/**
@@ -209,7 +207,7 @@ private import ModelInput
/**
* An empty class, except in specific tests.
*
* If this is non-empty, all models are parsed even if the package is not
* If this is non-empty, all models are parsed even if the type name is not
* considered relevant for the current database.
*/
abstract class TestAllModels extends Unit { }
@@ -232,54 +230,53 @@ private predicate typeModel(string row) { any(TypeModelCsv s).row(inversePad(row
private predicate typeVariableModel(string row) { any(TypeVariableModelCsv s).row(inversePad(row)) }
/** Holds if a source model exists for the given parameters. */
predicate sourceModel(string package, string type, string path, string kind) {
predicate sourceModel(string type, string path, string kind) {
exists(string row |
sourceModel(row) and
row.splitAt(";", 0) = package and
row.splitAt(";", 1) = type and
row.splitAt(";", 2) = path and
row.splitAt(";", 3) = kind
row.splitAt(";", 0) = type and
row.splitAt(";", 1) = path and
row.splitAt(";", 2) = kind
)
or
Extensions::sourceModel(type, path, kind)
}
/** Holds if a sink model exists for the given parameters. */
private predicate sinkModel(string package, string type, string path, string kind) {
private predicate sinkModel(string type, string path, string kind) {
exists(string row |
sinkModel(row) and
row.splitAt(";", 0) = package and
row.splitAt(";", 1) = type and
row.splitAt(";", 2) = path and
row.splitAt(";", 3) = kind
row.splitAt(";", 0) = type and
row.splitAt(";", 1) = path and
row.splitAt(";", 2) = kind
)
or
Extensions::sinkModel(type, path, kind)
}
/** Holds if a summary model `row` exists for the given parameters. */
private predicate summaryModel(
string package, string type, string path, string input, string output, string kind
) {
private predicate summaryModel(string type, string path, string input, string output, string kind) {
exists(string row |
summaryModel(row) and
row.splitAt(";", 0) = package and
row.splitAt(";", 1) = type and
row.splitAt(";", 2) = path and
row.splitAt(";", 3) = input and
row.splitAt(";", 4) = output and
row.splitAt(";", 5) = kind
row.splitAt(";", 0) = type and
row.splitAt(";", 1) = path and
row.splitAt(";", 2) = input and
row.splitAt(";", 3) = output and
row.splitAt(";", 4) = kind
)
or
Extensions::summaryModel(type, path, input, output, kind)
}
/** Holds if a type model exists for the given parameters. */
private predicate typeModel(
string package1, string type1, string package2, string type2, string path
) {
private predicate typeModel(string type1, string type2, string path) {
exists(string row |
typeModel(row) and
row.splitAt(";", 0) = package1 and
row.splitAt(";", 1) = type1 and
row.splitAt(";", 2) = package2 and
row.splitAt(";", 3) = type2 and
row.splitAt(";", 4) = path
row.splitAt(";", 0) = type1 and
row.splitAt(";", 1) = type2 and
row.splitAt(";", 2) = path
)
or
Extensions::typeModel(type1, type2, path)
}
/** Holds if a type variable model exists for the given parameters. */
@@ -289,64 +286,55 @@ private predicate typeVariableModel(string name, string path) {
row.splitAt(";", 0) = name and
row.splitAt(";", 1) = path
)
}
/**
* Gets a package that should be seen as an alias for the given other `package`,
* or the `package` itself.
*/
bindingset[package]
bindingset[result]
string getAPackageAlias(string package) {
typeModel(package, "", result, "", "")
or
result = package
Extensions::typeVariableModel(name, path)
}
/**
* Holds if CSV rows involving `package` might be relevant for the analysis of this database.
* Holds if CSV rows involving `type` might be relevant for the analysis of this database.
*/
private predicate isRelevantPackage(string package) {
predicate isRelevantType(string type) {
(
sourceModel(package, _, _, _) or
sinkModel(package, _, _, _) or
summaryModel(package, _, _, _, _, _) or
typeModel(_, _, package, _, _)
sourceModel(type, _, _) or
sinkModel(type, _, _) or
summaryModel(type, _, _, _, _) or
typeModel(_, type, _)
) and
(
Specific::isPackageUsed(package)
Specific::isTypeUsed(type)
or
exists(TestAllModels t)
)
or
exists(string other |
isRelevantPackage(other) and
typeModel(package, _, other, _, _)
exists(string other | isRelevantType(other) |
typeModel(type, other, _)
or
Specific::hasImplicitTypeModel(type, other)
)
}
/**
* Holds if `package,type,path` is used in some CSV row.
* Holds if `type,path` is used in some CSV row.
*/
pragma[nomagic]
predicate isRelevantFullPath(string package, string type, string path) {
isRelevantPackage(package) and
predicate isRelevantFullPath(string type, string path) {
isRelevantType(type) and
(
sourceModel(package, type, path, _) or
sinkModel(package, type, path, _) or
summaryModel(package, type, path, _, _, _) or
typeModel(_, _, package, type, path)
sourceModel(type, path, _) or
sinkModel(type, path, _) or
summaryModel(type, path, _, _, _) or
typeModel(_, type, path)
)
}
/** A string from a CSV row that should be parsed as an access path. */
private class AccessPathRange extends AccessPath::Range {
AccessPathRange() {
isRelevantFullPath(_, _, this)
isRelevantFullPath(_, this)
or
exists(string package | isRelevantPackage(package) |
summaryModel(package, _, _, this, _, _) or
summaryModel(package, _, _, _, this, _)
exists(string type | isRelevantType(type) |
summaryModel(type, _, this, _, _) or
summaryModel(type, _, _, this, _)
)
or
typeVariableModel(_, this)
@@ -400,83 +388,73 @@ private predicate invocationMatchesCallSiteFilter(Specific::InvokeNode invoke, A
}
private class TypeModelUseEntry extends API::EntryPoint {
private string package;
private string type;
TypeModelUseEntry() {
exists(any(TypeModel tm).getASource(package, type)) and
this = "TypeModelUseEntry;" + package + ";" + type
exists(any(TypeModel tm).getASource(type)) and
this = "TypeModelUseEntry;" + type
}
override DataFlow::LocalSourceNode getASource() {
result = any(TypeModel tm).getASource(package, type)
}
override DataFlow::LocalSourceNode getASource() { result = any(TypeModel tm).getASource(type) }
API::Node getNodeForType(string package_, string type_) {
package = package_ and type = type_ and result = this.getANode()
}
API::Node getNodeForType(string type_) { type = type_ and result = this.getANode() }
}
private class TypeModelDefEntry extends API::EntryPoint {
private string package;
private string type;
TypeModelDefEntry() {
exists(any(TypeModel tm).getASink(package, type)) and
this = "TypeModelDefEntry;" + package + ";" + type
exists(any(TypeModel tm).getASink(type)) and
this = "TypeModelDefEntry;" + type
}
override DataFlow::Node getASink() { result = any(TypeModel tm).getASink(package, type) }
override DataFlow::Node getASink() { result = any(TypeModel tm).getASink(type) }
API::Node getNodeForType(string package_, string type_) {
package = package_ and type = type_ and result = this.getANode()
}
API::Node getNodeForType(string type_) { type = type_ and result = this.getANode() }
}
/**
* Gets an API node identified by the given `(package,type)` pair.
* Gets an API node identified by the given `type`.
*/
pragma[nomagic]
private API::Node getNodeFromType(string package, string type) {
exists(string package2, string type2, AccessPath path2 |
typeModel(package, type, package2, type2, path2) and
result = getNodeFromPath(package2, type2, path2)
private API::Node getNodeFromType(string type) {
exists(string type2, AccessPath path2 |
typeModel(type, type2, path2) and
result = getNodeFromPath(type2, path2)
)
or
result = any(TypeModelUseEntry e).getNodeForType(package, type)
result = any(TypeModelUseEntry e).getNodeForType(type)
or
result = any(TypeModelDefEntry e).getNodeForType(package, type)
result = any(TypeModelDefEntry e).getNodeForType(type)
or
result = any(TypeModel t).getAnApiNode(package, type)
result = any(TypeModel t).getAnApiNode(type)
or
result = Specific::getExtraNodeFromType(package, type)
result = Specific::getExtraNodeFromType(type)
}
/**
* Gets the API node identified by the first `n` tokens of `path` in the given `(package, type, path)` tuple.
* Gets the API node identified by the first `n` tokens of `path` in the given `(type, path)` tuple.
*/
pragma[nomagic]
private API::Node getNodeFromPath(string package, string type, AccessPath path, int n) {
isRelevantFullPath(package, type, path) and
private API::Node getNodeFromPath(string type, AccessPath path, int n) {
isRelevantFullPath(type, path) and
(
n = 0 and
result = getNodeFromType(package, type)
result = getNodeFromType(type)
or
result = Specific::getExtraNodeFromPath(package, type, path, n)
result = Specific::getExtraNodeFromPath(type, path, n)
)
or
result = getSuccessorFromNode(getNodeFromPath(package, type, path, n - 1), path.getToken(n - 1))
result = getSuccessorFromNode(getNodeFromPath(type, path, n - 1), path.getToken(n - 1))
or
// Similar to the other recursive case, but where the path may have stepped through one or more call-site filters
result =
getSuccessorFromInvoke(getInvocationFromPath(package, type, path, n - 1), path.getToken(n - 1))
result = getSuccessorFromInvoke(getInvocationFromPath(type, path, n - 1), path.getToken(n - 1))
or
// Apply a subpath
result =
getNodeFromSubPath(getNodeFromPath(package, type, path, n - 1), getSubPathAt(path, n - 1))
result = getNodeFromSubPath(getNodeFromPath(type, path, n - 1), getSubPathAt(path, n - 1))
or
// Apply a type step
typeStep(getNodeFromPath(package, type, path, n), result)
typeStep(getNodeFromPath(type, path, n), result)
}
/**
@@ -496,15 +474,15 @@ private AccessPath getSubPathAt(AccessPath path, int n) {
pragma[nomagic]
private API::Node getNodeFromSubPath(API::Node base, AccessPath subPath, int n) {
exists(AccessPath path, int k |
base = [getNodeFromPath(_, _, path, k), getNodeFromSubPath(_, path, k)] and
base = [getNodeFromPath(_, path, k), getNodeFromSubPath(_, path, k)] and
subPath = getSubPathAt(path, k) and
result = base and
n = 0
)
or
exists(string package, string type, AccessPath basePath |
typeStepModel(package, type, basePath, subPath) and
base = getNodeFromPath(package, type, basePath) and
exists(string type, AccessPath basePath |
typeStepModel(type, basePath, subPath) and
base = getNodeFromPath(type, basePath) and
result = base and
n = 0
)
@@ -543,42 +521,40 @@ private API::Node getNodeFromSubPath(API::Node base, AccessPath subPath) {
result = getNodeFromSubPath(base, subPath, subPath.getNumToken())
}
/** Gets the node identified by the given `(package, type, path)` tuple. */
private API::Node getNodeFromPath(string package, string type, AccessPath path) {
result = getNodeFromPath(package, type, path, path.getNumToken())
/** Gets the node identified by the given `(type, path)` tuple. */
private API::Node getNodeFromPath(string type, AccessPath path) {
result = getNodeFromPath(type, path, path.getNumToken())
}
pragma[nomagic]
private predicate typeStepModel(string package, string type, AccessPath basePath, AccessPath output) {
summaryModel(package, type, basePath, "", output, "type")
private predicate typeStepModel(string type, AccessPath basePath, AccessPath output) {
summaryModel(type, basePath, "", output, "type")
}
pragma[nomagic]
private predicate typeStep(API::Node pred, API::Node succ) {
exists(string package, string type, AccessPath basePath, AccessPath output |
typeStepModel(package, type, basePath, output) and
pred = getNodeFromPath(package, type, basePath) and
exists(string type, AccessPath basePath, AccessPath output |
typeStepModel(type, basePath, output) and
pred = getNodeFromPath(type, basePath) and
succ = getNodeFromSubPath(pred, output)
)
}
/**
* Gets an invocation identified by the given `(package, type, path)` tuple.
* Gets an invocation identified by the given `(type, path)` tuple.
*
* Unlike `getNodeFromPath`, the `path` may end with one or more call-site filters.
*/
private Specific::InvokeNode getInvocationFromPath(
string package, string type, AccessPath path, int n
) {
result = Specific::getAnInvocationOf(getNodeFromPath(package, type, path, n))
private Specific::InvokeNode getInvocationFromPath(string type, AccessPath path, int n) {
result = Specific::getAnInvocationOf(getNodeFromPath(type, path, n))
or
result = getInvocationFromPath(package, type, path, n - 1) and
result = getInvocationFromPath(type, path, n - 1) and
invocationMatchesCallSiteFilter(result, path.getToken(n - 1))
}
/** Gets an invocation identified by the given `(package, type, path)` tuple. */
private Specific::InvokeNode getInvocationFromPath(string package, string type, AccessPath path) {
result = getInvocationFromPath(package, type, path, path.getNumToken())
/** Gets an invocation identified by the given `(type, path)` tuple. */
private Specific::InvokeNode getInvocationFromPath(string type, AccessPath path) {
result = getInvocationFromPath(type, path, path.getNumToken())
}
/**
@@ -631,9 +607,9 @@ module ModelOutput {
*/
cached
API::Node getASourceNode(string kind) {
exists(string package, string type, string path |
sourceModel(package, type, path, kind) and
result = getNodeFromPath(package, type, path)
exists(string type, string path |
sourceModel(type, path, kind) and
result = getNodeFromPath(type, path)
)
}
@@ -642,9 +618,9 @@ module ModelOutput {
*/
cached
API::Node getASinkNode(string kind) {
exists(string package, string type, string path |
sinkModel(package, type, path, kind) and
result = getNodeFromPath(package, type, path)
exists(string type, string path |
sinkModel(type, path, kind) and
result = getNodeFromPath(type, path)
)
}
@@ -653,32 +629,31 @@ module ModelOutput {
*/
cached
predicate relevantSummaryModel(
string package, string type, string path, string input, string output, string kind
string type, string path, string input, string output, string kind
) {
isRelevantPackage(package) and
summaryModel(package, type, path, input, output, kind)
isRelevantType(type) and
summaryModel(type, path, input, output, kind)
}
/**
* Holds if a `baseNode` is an invocation identified by the `package,type,path` part of a summary row.
* Holds if a `baseNode` is an invocation identified by the `type,path` part of a summary row.
*/
cached
predicate resolvedSummaryBase(
string package, string type, string path, Specific::InvokeNode baseNode
) {
summaryModel(package, type, path, _, _, _) and
baseNode = getInvocationFromPath(package, type, path)
predicate resolvedSummaryBase(string type, string path, Specific::InvokeNode baseNode) {
summaryModel(type, path, _, _, _) and
baseNode = getInvocationFromPath(type, path)
}
/**
* Holds if `node` is seen as an instance of `(package,type)` due to a type definition
* Holds if `node` is seen as an instance of `type` due to a type definition
* contributed by a CSV model.
*/
cached
API::Node getATypeNode(string package, string type) { result = getNodeFromType(package, type) }
API::Node getATypeNode(string type) { result = getNodeFromType(type) }
}
import Cached
import Specific::ModelOutputSpecific
/**
* Gets an error message relating to an invalid CSV row in a model.
@@ -686,13 +661,13 @@ module ModelOutput {
string getAWarning() {
// Check number of columns
exists(string row, string kind, int expectedArity, int actualArity |
any(SourceModelCsv csv).row(row) and kind = "source" and expectedArity = 4
any(SourceModelCsv csv).row(row) and kind = "source" and expectedArity = 3
or
any(SinkModelCsv csv).row(row) and kind = "sink" and expectedArity = 4
any(SinkModelCsv csv).row(row) and kind = "sink" and expectedArity = 3
or
any(SummaryModelCsv csv).row(row) and kind = "summary" and expectedArity = 6
any(SummaryModelCsv csv).row(row) and kind = "summary" and expectedArity = 5
or
any(TypeModelCsv csv).row(row) and kind = "type" and expectedArity = 5
any(TypeModelCsv csv).row(row) and kind = "type" and expectedArity = 3
or
any(TypeVariableModelCsv csv).row(row) and kind = "type-variable" and expectedArity = 2
|
@@ -705,7 +680,7 @@ module ModelOutput {
or
// Check names and arguments of access path tokens
exists(AccessPath path, AccessPathToken token |
(isRelevantFullPath(_, _, path) or typeVariableModel(_, path)) and
(isRelevantFullPath(_, path) or typeVariableModel(_, path)) and
token = path.getToken(_)
|
not isValidTokenNameInIdentifyingAccessPath(token.getName()) and

View File

@@ -0,0 +1,36 @@
/**
* Defines extensible predicates for contributing library models from data extensions.
*/
/**
* Holds if the value at `(type, path)` should be seen as a flow
* source of the given `kind`.
*
* The kind `remote` represents a general remote flow source.
*/
extensible predicate sourceModel(string type, string path, string kind);
/**
* Holds if the value at `(type, path)` should be seen as a sink
* of the given `kind`.
*/
extensible predicate sinkModel(string type, string path, string kind);
/**
* Holds if calls to `(type, path)`, the value referred to by `input`
* can flow to the value referred to by `output`.
*
* `kind` should be either `value` or `taint`, for value-preserving or taint-preserving steps,
* respectively.
*/
extensible predicate summaryModel(string type, string path, string input, string output, string kind);
/**
* Holds if `(type2, path)` should be seen as an instance of `type1`.
*/
extensible predicate typeModel(string type1, string type2, string path);
/**
* Holds if `path` can be substituted for a token `TypeVar[name]`.
*/
extensible predicate typeVariableModel(string name, string path);

View File

@@ -31,19 +31,22 @@ import semmle.python.dataflow.new.DataFlow::DataFlow as DataFlow
private import AccessPathSyntax
/**
* Holds if models describing `package` may be relevant for the analysis of this database.
* Holds if models describing `type` may be relevant for the analysis of this database.
*/
predicate isPackageUsed(string package) { API::moduleImportExists(package) }
predicate isTypeUsed(string type) { API::moduleImportExists(type) }
/** Gets a Python-specific interpretation of the `(package, type, path)` tuple after resolving the first `n` access path tokens. */
bindingset[package, type, path]
API::Node getExtraNodeFromPath(string package, string type, AccessPath path, int n) { none() }
/**
* Holds if `type` can be obtained from an instance of `otherType` due to
* language semantics modeled by `getExtraNodeFromType`.
*/
predicate hasImplicitTypeModel(string type, string otherType) { none() }
/** Gets a Python-specific interpretation of the `(package, type)` tuple. */
API::Node getExtraNodeFromType(string package, string type) {
type = "" and
result = API::moduleImport(package)
}
/** Gets a Python-specific interpretation of the `(type, path)` tuple after resolving the first `n` access path tokens. */
bindingset[type, path]
API::Node getExtraNodeFromPath(string type, AccessPath path, int n) { none() }
/** Gets a Python-specific interpretation of the given `type`. */
API::Node getExtraNodeFromType(string type) { result = API::moduleImport(type) }
/**
* Gets a Python-specific API graph successor of `node` reachable by resolving `token`.
@@ -121,9 +124,9 @@ predicate invocationMatchesExtraCallSiteFilter(API::CallNode invoke, AccessPathT
*/
pragma[nomagic]
private predicate relevantInputOutputPath(API::CallNode base, AccessPath inputOrOutput) {
exists(string package, string type, string input, string output, string path |
ModelOutput::relevantSummaryModel(package, type, path, input, output, _) and
ModelOutput::resolvedSummaryBase(package, type, path, base) and
exists(string type, string input, string output, string path |
ModelOutput::relevantSummaryModel(type, path, input, output, _) and
ModelOutput::resolvedSummaryBase(type, path, base) and
inputOrOutput = [input, output]
)
}
@@ -153,12 +156,9 @@ private API::Node getNodeFromInputOutputPath(API::CallNode baseNode, AccessPath
* Holds if a CSV summary contributed the step `pred -> succ` of the given `kind`.
*/
predicate summaryStep(API::Node pred, API::Node succ, string kind) {
exists(
string package, string type, string path, API::CallNode base, AccessPath input,
AccessPath output
|
ModelOutput::relevantSummaryModel(package, type, path, input, output, kind) and
ModelOutput::resolvedSummaryBase(package, type, path, base) and
exists(string type, string path, API::CallNode base, AccessPath input, AccessPath output |
ModelOutput::relevantSummaryModel(type, path, input, output, kind) and
ModelOutput::resolvedSummaryBase(type, path, base) and
pred = getNodeFromInputOutputPath(base, input) and
succ = getNodeFromInputOutputPath(base, output)
)
@@ -201,3 +201,5 @@ predicate isExtraValidTokenArgumentInIdentifyingAccessPath(string name, string a
argument.regexpMatch("\\w+:") // keyword argument
)
}
module ModelOutputSpecific { }

View File

@@ -0,0 +1,26 @@
extensions:
# Contribute empty data sets to avoid errors about an undefined extensionals
- addsTo:
pack: codeql/python-all
extensible: sourceModel
data: []
- addsTo:
pack: codeql/python-all
extensible: sinkModel
data: []
- addsTo:
pack: codeql/python-all
extensible: summaryModel
data: []
- addsTo:
pack: codeql/python-all
extensible: typeModel
data: []
- addsTo:
pack: codeql/python-all
extensible: typeVariableModel
data: []

View File

@@ -1,204 +0,0 @@
/**
* INTERNAL: Do not use.
*
* Provides internal implementation of PEP249. This currently resides in a different
* file than `python/ql/src/semmle/python/frameworks/PEP249.qll`, since we used to
* export everything without being encapsulated in a module, and shadowing rules means
* that we can't just add the module directly to that file :(
*
* So once we can remove those deprecated things (Start of July 2022), we can also move
* the core implementation into its' proper place.
*
* Provides classes modeling PEP 249.
* See https://www.python.org/dev/peps/pep-0249/.
*/
private import python
private import semmle.python.dataflow.new.DataFlow
private import semmle.python.dataflow.new.RemoteFlowSources
private import semmle.python.Concepts
private import semmle.python.ApiGraphs
/**
* Provides classes modeling database interfaces following PEP 249.
* See https://www.python.org/dev/peps/pep-0249/.
*/
module PEP249 {
/**
* An API graph node representing a module that implements PEP 249.
*/
abstract class PEP249ModuleApiNode extends API::Node {
/** Gets a string representation of this element. */
override string toString() { result = this.(API::Node).toString() }
}
/** Gets a reference to the `connect` function of a module that implements PEP 249. */
DataFlow::Node connect() {
result = any(PEP249ModuleApiNode a).getMember("connect").getAValueReachableFromSource()
}
/**
* Provides models for database connections (following PEP 249).
*
* See https://www.python.org/dev/peps/pep-0249/#connection-objects.
*/
module Connection {
/**
* A source of database connections (following PEP 249), extend this class to model new instances.
*
* This can include instantiations of the class, return values from function
* calls, or a special parameter that will be set when functions are called by external
* libraries.
*
* Use the predicate `Connection::instance()` to get references to database connections (following PEP 249).
*
* Extend this class if the module implementing PEP 249 offers more direct ways to obtain
* a connection than going through `connect`.
*/
abstract class InstanceSource extends DataFlow::Node { }
/** A call to the `connect` function of a module that implements PEP 249. */
private class ConnectCall extends InstanceSource, DataFlow::CallCfgNode {
ConnectCall() { this.getFunction() = connect() }
}
/** Gets a reference to a database connection (following PEP 249). */
private DataFlow::TypeTrackingNode instance(DataFlow::TypeTracker t) {
t.start() and
result instanceof InstanceSource
or
exists(DataFlow::TypeTracker t2 | result = instance(t2).track(t2, t))
}
/** Gets a reference to a database connection (following PEP 249). */
DataFlow::Node instance() { instance(DataFlow::TypeTracker::end()).flowsTo(result) }
}
/**
* Provides models for database cursors (following PEP 249).
*
* These are returned by the `cursor` method on a database connection.
* See https://www.python.org/dev/peps/pep-0249/#cursor.
*/
module Cursor {
/**
* A source of database cursors (following PEP 249), extend this class to model new instances.
*
* This can include instantiations of the class, return values from function
* calls, or a special parameter that will be set when functions are called by external
* libraries.
*
* Use the predicate `Cursor::instance()` to get references to database cursors (following PEP 249).
*
* Extend this class if the module implementing PEP 249 offers more direct ways to obtain
* a connection than going through `connect`.
*/
abstract class InstanceSource extends DataFlow::LocalSourceNode { }
/** Gets a reference to a database cursor. */
private DataFlow::TypeTrackingNode instance(DataFlow::TypeTracker t) {
t.start() and
result instanceof InstanceSource
or
exists(DataFlow::TypeTracker t2 | result = instance(t2).track(t2, t))
}
/** Gets a reference to a database cursor. */
DataFlow::Node instance() { instance(DataFlow::TypeTracker::end()).flowsTo(result) }
/** Gets a reference to the `cursor` method on a database connection. */
private DataFlow::TypeTrackingNode methodRef(DataFlow::TypeTracker t) {
t.startInAttr("cursor") and
result = Connection::instance()
or
exists(DataFlow::TypeTracker t2 | result = methodRef(t2).track(t2, t))
}
/** Gets a reference to the `cursor` method on a database connection. */
DataFlow::Node methodRef() { methodRef(DataFlow::TypeTracker::end()).flowsTo(result) }
/** A call to the `cursor` method on a database connection */
private class CursorCall extends InstanceSource, DataFlow::CallCfgNode {
CursorCall() { this.getFunction() = methodRef() }
}
/** Gets a reference to a result of calling the `cursor` method on a database connection. */
private DataFlow::TypeTrackingNode methodResult(DataFlow::TypeTracker t) {
t.start() and
result.asCfgNode().(CallNode).getFunction() = methodRef().asCfgNode()
or
exists(DataFlow::TypeTracker t2 | result = methodResult(t2).track(t2, t))
}
/**
* DEPRECATED: Use `Cursor::instance()` to get references to database cursors instead.
*
* Gets a reference to a result of calling the `cursor` method on a database connection.
*/
deprecated DataFlow::Node methodResult() {
methodResult(DataFlow::TypeTracker::end()).flowsTo(result)
}
}
/**
* Gets a reference to the `execute` method on a cursor (or on a connection).
*
* Note: while `execute` method on a connection is not part of PEP249, if it is used, we
* recognize it as an alias for constructing a cursor and calling `execute` on it.
*
* See https://peps.python.org/pep-0249/#execute.
*/
private DataFlow::TypeTrackingNode execute(DataFlow::TypeTracker t) {
t.startInAttr("execute") and
result in [Cursor::instance(), Connection::instance()]
or
exists(DataFlow::TypeTracker t2 | result = execute(t2).track(t2, t))
}
/**
* Gets a reference to the `execute` method on a cursor (or on a connection).
*
* Note: while `execute` method on a connection is not part of PEP249, if it is used, we
* recognize it as an alias for constructing a cursor and calling `execute` on it.
*
* See https://peps.python.org/pep-0249/#execute.
*/
DataFlow::Node execute() { execute(DataFlow::TypeTracker::end()).flowsTo(result) }
/**
* A call to the `execute` method on a cursor or a connection.
*
* See https://peps.python.org/pep-0249/#execute
*
* Note: While `execute` method on a connection is not part of PEP249, if it is used, we
* recognize it as an alias for constructing a cursor and calling `execute` on it.
*/
private class ExecuteCall extends SqlExecution::Range, DataFlow::CallCfgNode {
ExecuteCall() { this.getFunction() = execute() }
override DataFlow::Node getSql() { result in [this.getArg(0), this.getArgByName("sql")] }
}
private DataFlow::TypeTrackingNode executemany(DataFlow::TypeTracker t) {
t.startInAttr("executemany") and
result in [Cursor::instance(), Connection::instance()]
or
exists(DataFlow::TypeTracker t2 | result = executemany(t2).track(t2, t))
}
private DataFlow::Node executemany() { executemany(DataFlow::TypeTracker::end()).flowsTo(result) }
/**
* A call to the `executemany` method on a cursor or a connection.
*
* See https://peps.python.org/pep-0249/#executemany
*
* Note: While `executemany` method on a connection is not part of PEP249, if it is used, we
* recognize it as an alias for constructing a cursor and calling `executemany` on it.
*/
private class ExecutemanyCall extends SqlExecution::Range, DataFlow::CallCfgNode {
ExecutemanyCall() { this.getFunction() = executemany() }
override DataFlow::Node getSql() { result in [this.getArg(0), this.getArgByName("sql")] }
}
}

View File

@@ -0,0 +1,61 @@
/**
* Provides default sources, sinks and sanitizers for detecting
* "PAM Authorization" vulnerabilities.
*/
import python
import semmle.python.ApiGraphs
import semmle.python.dataflow.new.TaintTracking
import semmle.python.dataflow.new.RemoteFlowSources
/**
* Provides default sources, sinks and sanitizers for detecting
* "PAM Authorization" vulnerabilities.
*/
module PamAuthorizationCustomizations {
/**
* Models a node corresponding to the `pam` library
*/
API::Node libPam() {
exists(API::CallNode findLibCall, API::CallNode cdllCall |
findLibCall =
API::moduleImport("ctypes").getMember("util").getMember("find_library").getACall() and
findLibCall.getParameter(0).getAValueReachingSink().asExpr().(StrConst).getText() = "pam" and
cdllCall = API::moduleImport("ctypes").getMember("CDLL").getACall() and
cdllCall.getParameter(0).getAValueReachingSink() = findLibCall
|
result = cdllCall.getReturn()
)
}
/**
* A data flow source for "PAM Authorization" vulnerabilities.
*/
abstract class Source extends DataFlow::Node { }
/**
* A data flow sink for "PAM Authorization" vulnerabilities.
*/
abstract class Sink extends DataFlow::Node { }
/**
* A source of remote user input, considered as a flow source.
*/
class RemoteFlowSourceAsSource extends Source, RemoteFlowSource { }
/**
* A vulnerable `pam_authenticate` call considered as a flow sink.
*/
class VulnPamAuthCall extends API::CallNode, Sink {
VulnPamAuthCall() {
exists(DataFlow::Node h |
this = libPam().getMember("pam_authenticate").getACall() and
h = this.getArg(0) and
not exists(API::CallNode acctMgmtCall |
acctMgmtCall = libPam().getMember("pam_acct_mgmt").getACall() and
DataFlow::localFlow(h, acctMgmtCall.getArg(0))
)
)
}
}
}

View File

@@ -0,0 +1,39 @@
/**
* Provides a taint-tracking configuration for detecting "PAM Authorization" vulnerabilities.
*
* Note, for performance reasons: only import this file if
* `PamAuthorization::Configuration` is needed, otherwise
* `PamAuthorizationCustomizations` should be imported instead.
*/
import python
import semmle.python.ApiGraphs
import semmle.python.dataflow.new.TaintTracking
import PamAuthorizationCustomizations::PamAuthorizationCustomizations
/**
* A taint-tracking configuration for detecting "PAM Authorization" vulnerabilities.
*/
class Configuration extends TaintTracking::Configuration {
Configuration() { this = "PamAuthorization" }
override predicate isSource(DataFlow::Node node) { node instanceof Source }
override predicate isSink(DataFlow::Node node) { node instanceof Sink }
override predicate isAdditionalTaintStep(DataFlow::Node node1, DataFlow::Node node2) {
// Models flow from a remotely supplied username field to a PAM `handle`.
// `retval = pam_start(service, username, byref(conv), byref(handle))`
exists(API::CallNode pamStart, DataFlow::Node handle, API::CallNode pointer |
pointer = API::moduleImport("ctypes").getMember(["pointer", "byref"]).getACall() and
pamStart = libPam().getMember("pam_start").getACall() and
pointer = pamStart.getArg(3) and
handle = pointer.getArg(0) and
pamStart.getArg(1) = node1 and
handle = node2
)
or
// Flow from handle to the authenticate call in the final step
exists(VulnPamAuthCall c | c.getArg(0) = node1 | node2 = c)
}
}

View File

@@ -0,0 +1,21 @@
/**
* INTERNAL: Do not use.
*
* Provides predicates for recommended encryption key sizes.
* Such that we can share this logic across our CodeQL analysis of different languages.
*/
/** Returns the minimum recommended key size for RSA. */
int minSecureKeySizeRsa() { result = 2048 }
/** Returns the minimum recommended key size for DSA. */
int minSecureKeySizeDsa() { result = 2048 }
/** Returns the minimum recommended key size for DH. */
int minSecureKeySizeDh() { result = 2048 }
/** Returns the minimum recommended key size for elliptic curve cryptography. */
int minSecureKeySizeEcc() { result = 256 }
/** Returns the minimum recommended key size for AES. */
int minSecureKeySizeAes() { result = 128 }

View File

@@ -13,17 +13,15 @@
*/
import python
import semmle.python.dataflow.new.DataFlow
import semmle.python.ApiGraphs
from CallNode call_to_super, string name
from DataFlow::CallCfgNode call_to_super, string name
where
exists(GlobalVariable gv, ControlFlowNode cn |
call_to_super = ClassValue::super_().getACall() and
gv.getId() = "super" and
cn = call_to_super.getArg(0) and
name = call_to_super.getScope().getScope().(Class).getName() and
exists(ClassValue other |
cn.pointsTo(other) and
not other.getScope().getName() = name
)
call_to_super = API::builtin("super").getACall() and
name = call_to_super.getScope().getScope().(Class).getName() and
exists(DataFlow::Node arg |
arg = call_to_super.getArg(0) and
arg.getALocalSource().asExpr().(Name).getId() != name
)
select call_to_super.getNode(), "First argument to super() should be " + name + "."

View File

@@ -1,7 +1,7 @@
/**
* @name PAM authorization bypass due to incorrect usage
* @description Not using `pam_acct_mgmt` after `pam_authenticate` to check the validity of a login can lead to authorization bypass.
* @kind problem
* @kind path-problem
* @problem.severity warning
* @security-severity 8.1
* @precision high
@@ -11,28 +11,12 @@
*/
import python
import DataFlow::PathGraph
import semmle.python.ApiGraphs
import experimental.semmle.python.Concepts
import semmle.python.dataflow.new.TaintTracking
import semmle.python.security.dataflow.PamAuthorizationQuery
API::Node libPam() {
exists(API::CallNode findLibCall, API::CallNode cdllCall |
findLibCall = API::moduleImport("ctypes").getMember("util").getMember("find_library").getACall() and
findLibCall.getParameter(0).getAValueReachingSink().asExpr().(StrConst).getText() = "pam" and
cdllCall = API::moduleImport("ctypes").getMember("CDLL").getACall() and
cdllCall.getParameter(0).getAValueReachingSink() = findLibCall
|
result = cdllCall.getReturn()
)
}
from API::CallNode authenticateCall, DataFlow::Node handle
where
authenticateCall = libPam().getMember("pam_authenticate").getACall() and
handle = authenticateCall.getArg(0) and
not exists(API::CallNode acctMgmtCall |
acctMgmtCall = libPam().getMember("pam_acct_mgmt").getACall() and
DataFlow::localFlow(handle, acctMgmtCall.getArg(0))
)
select authenticateCall,
"This PAM authentication call may lead to an authorization bypass, since 'pam_acct_mgmt' is not called afterwards."
from Configuration config, DataFlow::PathNode source, DataFlow::PathNode sink
where config.hasFlowPath(source, sink)
select sink.getNode(), source, sink,
"This PAM authentication depends on a $@, and 'pam_acct_mgmt' is not called afterwards.",
source.getNode(), "user-provided value"

View File

@@ -11,13 +11,13 @@ As computational power increases, the ability to break ciphers grows and keys ne
<p>
The three main asymmetric key algorithms currently in use are RivestShamirAdleman (RSA) cryptography, Digital Signature Algorithm (DSA), and Elliptic-curve cryptography (ECC).
With current technology, key sizes of 2048 bits for RSA and DSA,
or 224 bits for ECC, are regarded as unbreakable.
or 256 bits for ECC, are regarded as unbreakable.
</p>
</overview>
<recommendation>
<p>
Increase the key size to the recommended amount or larger. For RSA or DSA this is at least 2048 bits, for ECC this is at least 224 bits.
Increase the key size to the recommended amount or larger. For RSA or DSA this is at least 2048 bits, for ECC this is at least 256 bits.
</p>
</recommendation>
@@ -45,4 +45,3 @@ Recommendation for Transitioning the Use of Cryptographic Algorithms and Key Len
</li>
</references>
</qhelp>

View File

@@ -0,0 +1,4 @@
---
category: minorAnalysis
---
* Added modeling of `getpass.getpass` as a source of passwords, which will be an additional source for `py/clear-text-logging-sensitive-data`, `py/clear-text-storage-sensitive-data`, and `py/weak-sensitive-data-hashing`.

View File

@@ -0,0 +1,4 @@
---
category: minorAnalysis
---
* Bumped the minimum keysize we consider secure for elliptic curve cryptography from 224 to 256 bits, following current best practices. This might effect results from the _Use of weak cryptographic key_ (`py/weak-crypto-key`) query.

View File

@@ -1,6 +1,7 @@
<!DOCTYPE qhelp PUBLIC
"-//Semmle//qhelp//EN"
"qhelp.dtd">
<!-- Disabled since it refers to examples which do not exist. -->
<qhelp>
<overview>

View File

@@ -4,7 +4,7 @@
* destination file path is within the destination directory can cause files outside
* the destination directory to be overwritten.
* @kind path-problem
* @id py/tarslip
* @id py/tarslip-extended
* @problem.severity error
* @security-severity 7.5
* @precision high

View File

@@ -6,7 +6,7 @@
* @problem.severity error
* @security-severity 2.9
* @sub-severity high
* @id py/reflective-xss
* @id py/reflective-xss-email
* @tags security
* external/cwe/cwe-079
* external/cwe/cwe-116

View File

@@ -0,0 +1,87 @@
/**
* @name Taint sinks
* @description Sinks from TaintTracking queries.
* @kind problem
* @problem.severity recommendation
* @id py/meta/alerts/taint-sinks
* @tags meta
* @precision very-low
*/
private import python
private import semmle.python.dataflow.new.DataFlow
private import meta.MetaMetrics
import semmle.python.security.dataflow.CleartextLoggingCustomizations
import semmle.python.security.dataflow.CleartextStorageCustomizations
import semmle.python.security.dataflow.CodeInjectionCustomizations
import semmle.python.security.dataflow.CommandInjectionCustomizations
import semmle.python.security.dataflow.LdapInjectionCustomizations
import semmle.python.security.dataflow.LogInjectionCustomizations
import semmle.python.security.dataflow.PathInjectionCustomizations
import semmle.python.security.dataflow.PolynomialReDoSCustomizations
import semmle.python.security.dataflow.ReflectedXSSCustomizations
import semmle.python.security.dataflow.RegexInjectionCustomizations
import semmle.python.security.dataflow.ServerSideRequestForgeryCustomizations
import semmle.python.security.dataflow.SqlInjectionCustomizations
import semmle.python.security.dataflow.StackTraceExposureCustomizations
import semmle.python.security.dataflow.TarSlipCustomizations
import semmle.python.security.dataflow.UnsafeDeserializationCustomizations
import semmle.python.security.dataflow.UrlRedirectCustomizations
import semmle.python.security.dataflow.WeakSensitiveDataHashingCustomizations
import semmle.python.security.dataflow.XmlBombCustomizations
import semmle.python.security.dataflow.XpathInjectionCustomizations
import semmle.python.security.dataflow.XxeCustomizations
DataFlow::Node relevantTaintSink(string kind) {
not result.getLocation().getFile() instanceof IgnoredFile and
(
kind = "CleartextLogging" and result instanceof CleartextLogging::Sink
or
kind = "CleartextStorage" and result instanceof CleartextStorage::Sink
or
kind = "CodeInjection" and result instanceof CodeInjection::Sink
or
kind = "CommandInjection" and result instanceof CommandInjection::Sink
or
kind = "LdapInjection (DN)" and result instanceof LdapInjection::DnSink
or
kind = "LdapInjection (Filter)" and result instanceof LdapInjection::FilterSink
or
kind = "LogInjection" and result instanceof LogInjection::Sink
or
kind = "PathInjection" and result instanceof PathInjection::Sink
or
kind = "PolynomialReDoS" and result instanceof PolynomialReDoS::Sink
or
kind = "ReflectedXss" and result instanceof ReflectedXss::Sink
or
kind = "RegexInjection" and result instanceof RegexInjection::Sink
or
kind = "ServerSideRequestForgery" and result instanceof ServerSideRequestForgery::Sink
or
kind = "SqlInjection" and result instanceof SqlInjection::Sink
or
kind = "StackTraceExposure" and result instanceof StackTraceExposure::Sink
or
kind = "TarSlip" and result instanceof TarSlip::Sink
or
kind = "UnsafeDeserialization" and result instanceof UnsafeDeserialization::Sink
or
kind = "UrlRedirect" and result instanceof UrlRedirect::Sink
or
kind = "WeakSensitiveDataHashing (NormalHashFunction)" and
result instanceof NormalHashFunction::Sink
or
kind = "WeakSensitiveDataHashing (ComputationallyExpensiveHashFunction)" and
result instanceof ComputationallyExpensiveHashFunction::Sink
or
kind = "XmlBomb" and result instanceof XmlBomb::Sink
or
kind = "XpathInjection" and result instanceof XpathInjection::Sink
or
kind = "Xxe" and result instanceof Xxe::Sink
)
}
from string kind
select relevantTaintSink(kind), kind + " sink"

View File

@@ -10,7 +10,11 @@
import python
import semmle.python.dataflow.new.internal.DataFlowPrivate
import meta.MetaMetrics
from DataFlowCall c, DataFlowCallableValue f
where c.getCallable() = f
where
c.getCallable() = f and
not c.getLocation().getFile() instanceof IgnoredFile and
not f.getScope().getLocation().getFile() instanceof IgnoredFile
select c, "Call to $@", f.getScope(), f.toString()

View File

@@ -16,8 +16,9 @@ class DataFlowTest extends FlowTest {
query predicate missingAnnotationOnSink(Location location, string error, string element) {
error = "ERROR, you should add `# $ MISSING: flow` annotation" and
exists(DataFlow::Node sink |
any(TestConfiguration config).isSink(sink) and
// note: we only care about `SINK` and not `SINK_F`, so we have to reconstruct manually.
exists(DataFlow::CallCfgNode call |
// note: we only care about `SINK` and not `SINK_F`, so we have to reconstruct manually.
call.getFunction().asCfgNode().(NameNode).getId() = "SINK" and
(sink = call.getArg(_) or sink = call.getArgByName(_))
) and

View File

@@ -19,3 +19,5 @@ reverseRead
argHasPostUpdate
postWithInFlow
viableImplInCallContextTooLarge
uniqueParameterNodeAtPosition
uniqueParameterNodePosition

View File

@@ -19,3 +19,5 @@ reverseRead
argHasPostUpdate
postWithInFlow
viableImplInCallContextTooLarge
uniqueParameterNodeAtPosition
uniqueParameterNodePosition

View File

@@ -19,3 +19,5 @@ reverseRead
argHasPostUpdate
postWithInFlow
viableImplInCallContextTooLarge
uniqueParameterNodeAtPosition
uniqueParameterNodePosition

View File

@@ -1,80 +0,0 @@
| classes.py:14:17:14:60 | ControlFlowNode for Attribute() | classes.py:14:17:14:60 | ControlFlowNode for Attribute() |
| classes.py:45:16:45:35 | ControlFlowNode for Attribute() | classes.py:45:16:45:35 | ControlFlowNode for Attribute() |
| classes.py:60:17:60:27 | [pre objCreate] ControlFlowNode for With_init() | classes.py:54:18:54:21 | ControlFlowNode for self |
| classes.py:242:9:242:24 | ControlFlowNode for set() | classes.py:242:9:242:24 | ControlFlowNode for set() |
| classes.py:247:9:247:30 | ControlFlowNode for frozenset() | classes.py:247:9:247:30 | ControlFlowNode for frozenset() |
| classes.py:252:9:252:28 | ControlFlowNode for dict() | classes.py:252:9:252:28 | ControlFlowNode for dict() |
| classes.py:559:16:559:17 | ControlFlowNode for Str | classes.py:565:5:565:22 | ControlFlowNode for Subscript |
| classes.py:565:5:565:16 | ControlFlowNode for with_getitem | classes.py:555:21:555:24 | ControlFlowNode for self |
| classes.py:565:18:565:21 | ControlFlowNode for arg2 | classes.py:555:27:555:29 | ControlFlowNode for key |
| classes.py:581:5:581:16 | ControlFlowNode for with_setitem | classes.py:570:21:570:24 | ControlFlowNode for self |
| classes.py:581:18:581:21 | ControlFlowNode for arg2 | classes.py:570:27:570:29 | ControlFlowNode for key |
| classes.py:581:26:581:29 | ControlFlowNode for arg3 | classes.py:570:32:570:36 | ControlFlowNode for value |
| classes.py:595:9:595:20 | ControlFlowNode for with_delitem | classes.py:586:21:586:24 | ControlFlowNode for self |
| classes.py:595:22:595:25 | ControlFlowNode for arg2 | classes.py:586:27:586:29 | ControlFlowNode for key |
| classes.py:618:16:618:28 | ControlFlowNode for Attribute() | classes.py:618:16:618:28 | ControlFlowNode for Attribute() |
| classes.py:659:15:659:18 | ControlFlowNode for self | classes.py:667:5:667:19 | ControlFlowNode for BinaryExpr |
| classes.py:661:16:661:19 | ControlFlowNode for self | classes.py:667:5:667:19 | ControlFlowNode for BinaryExpr |
| classes.py:667:5:667:12 | ControlFlowNode for with_add | classes.py:657:17:657:20 | ControlFlowNode for self |
| classes.py:667:5:667:12 | ControlFlowNode for with_add | classes.py:667:5:667:19 | ControlFlowNode for BinaryExpr |
| classes.py:667:16:667:19 | ControlFlowNode for arg2 | classes.py:657:23:657:27 | ControlFlowNode for other |
| classes.py:674:15:674:18 | ControlFlowNode for self | classes.py:682:5:682:19 | ControlFlowNode for BinaryExpr |
| classes.py:676:16:676:19 | ControlFlowNode for self | classes.py:682:5:682:19 | ControlFlowNode for BinaryExpr |
| classes.py:682:5:682:12 | ControlFlowNode for with_sub | classes.py:672:17:672:20 | ControlFlowNode for self |
| classes.py:682:5:682:12 | ControlFlowNode for with_sub | classes.py:682:5:682:19 | ControlFlowNode for BinaryExpr |
| classes.py:682:16:682:19 | ControlFlowNode for arg2 | classes.py:672:23:672:27 | ControlFlowNode for other |
| classes.py:689:15:689:18 | ControlFlowNode for self | classes.py:697:5:697:19 | ControlFlowNode for BinaryExpr |
| classes.py:691:16:691:19 | ControlFlowNode for self | classes.py:697:5:697:19 | ControlFlowNode for BinaryExpr |
| classes.py:697:5:697:12 | ControlFlowNode for with_mul | classes.py:687:17:687:20 | ControlFlowNode for self |
| classes.py:697:5:697:12 | ControlFlowNode for with_mul | classes.py:697:5:697:19 | ControlFlowNode for BinaryExpr |
| classes.py:697:16:697:19 | ControlFlowNode for arg2 | classes.py:687:23:687:27 | ControlFlowNode for other |
| classes.py:704:15:704:18 | ControlFlowNode for self | classes.py:712:5:712:22 | ControlFlowNode for BinaryExpr |
| classes.py:706:16:706:19 | ControlFlowNode for self | classes.py:712:5:712:22 | ControlFlowNode for BinaryExpr |
| classes.py:712:5:712:15 | ControlFlowNode for with_matmul | classes.py:702:20:702:23 | ControlFlowNode for self |
| classes.py:712:5:712:15 | ControlFlowNode for with_matmul | classes.py:712:5:712:22 | ControlFlowNode for BinaryExpr |
| classes.py:712:19:712:22 | ControlFlowNode for arg2 | classes.py:702:26:702:30 | ControlFlowNode for other |
| classes.py:719:15:719:18 | ControlFlowNode for self | classes.py:727:5:727:23 | ControlFlowNode for BinaryExpr |
| classes.py:721:16:721:19 | ControlFlowNode for self | classes.py:727:5:727:23 | ControlFlowNode for BinaryExpr |
| classes.py:727:5:727:16 | ControlFlowNode for with_truediv | classes.py:717:21:717:24 | ControlFlowNode for self |
| classes.py:727:5:727:16 | ControlFlowNode for with_truediv | classes.py:727:5:727:23 | ControlFlowNode for BinaryExpr |
| classes.py:727:20:727:23 | ControlFlowNode for arg2 | classes.py:717:27:717:31 | ControlFlowNode for other |
| classes.py:734:15:734:18 | ControlFlowNode for self | classes.py:742:5:742:25 | ControlFlowNode for BinaryExpr |
| classes.py:736:16:736:19 | ControlFlowNode for self | classes.py:742:5:742:25 | ControlFlowNode for BinaryExpr |
| classes.py:742:5:742:17 | ControlFlowNode for with_floordiv | classes.py:732:22:732:25 | ControlFlowNode for self |
| classes.py:742:5:742:17 | ControlFlowNode for with_floordiv | classes.py:742:5:742:25 | ControlFlowNode for BinaryExpr |
| classes.py:742:22:742:25 | ControlFlowNode for arg2 | classes.py:732:28:732:32 | ControlFlowNode for other |
| classes.py:749:15:749:18 | ControlFlowNode for self | classes.py:757:5:757:19 | ControlFlowNode for BinaryExpr |
| classes.py:751:16:751:19 | ControlFlowNode for self | classes.py:757:5:757:19 | ControlFlowNode for BinaryExpr |
| classes.py:757:5:757:12 | ControlFlowNode for with_mod | classes.py:747:17:747:20 | ControlFlowNode for self |
| classes.py:757:5:757:12 | ControlFlowNode for with_mod | classes.py:757:5:757:19 | ControlFlowNode for BinaryExpr |
| classes.py:757:16:757:19 | ControlFlowNode for arg2 | classes.py:747:23:747:27 | ControlFlowNode for other |
| classes.py:779:15:779:18 | ControlFlowNode for self | classes.py:793:5:793:20 | ControlFlowNode for BinaryExpr |
| classes.py:781:16:781:19 | ControlFlowNode for self | classes.py:793:5:793:20 | ControlFlowNode for BinaryExpr |
| classes.py:793:5:793:12 | ControlFlowNode for with_pow | classes.py:777:17:777:20 | ControlFlowNode for self |
| classes.py:793:5:793:12 | ControlFlowNode for with_pow | classes.py:793:5:793:20 | ControlFlowNode for BinaryExpr |
| classes.py:793:17:793:20 | ControlFlowNode for arg2 | classes.py:777:23:777:27 | ControlFlowNode for other |
| classes.py:800:15:800:18 | ControlFlowNode for self | classes.py:808:5:808:23 | ControlFlowNode for BinaryExpr |
| classes.py:802:16:802:19 | ControlFlowNode for self | classes.py:808:5:808:23 | ControlFlowNode for BinaryExpr |
| classes.py:808:5:808:15 | ControlFlowNode for with_lshift | classes.py:798:20:798:23 | ControlFlowNode for self |
| classes.py:808:5:808:15 | ControlFlowNode for with_lshift | classes.py:808:5:808:23 | ControlFlowNode for BinaryExpr |
| classes.py:808:20:808:23 | ControlFlowNode for arg2 | classes.py:798:26:798:30 | ControlFlowNode for other |
| classes.py:815:15:815:18 | ControlFlowNode for self | classes.py:823:5:823:23 | ControlFlowNode for BinaryExpr |
| classes.py:817:16:817:19 | ControlFlowNode for self | classes.py:823:5:823:23 | ControlFlowNode for BinaryExpr |
| classes.py:823:5:823:15 | ControlFlowNode for with_rshift | classes.py:813:20:813:23 | ControlFlowNode for self |
| classes.py:823:5:823:15 | ControlFlowNode for with_rshift | classes.py:823:5:823:23 | ControlFlowNode for BinaryExpr |
| classes.py:823:20:823:23 | ControlFlowNode for arg2 | classes.py:813:26:813:30 | ControlFlowNode for other |
| classes.py:830:15:830:18 | ControlFlowNode for self | classes.py:838:5:838:19 | ControlFlowNode for BinaryExpr |
| classes.py:832:16:832:19 | ControlFlowNode for self | classes.py:838:5:838:19 | ControlFlowNode for BinaryExpr |
| classes.py:838:5:838:12 | ControlFlowNode for with_and | classes.py:828:17:828:20 | ControlFlowNode for self |
| classes.py:838:5:838:12 | ControlFlowNode for with_and | classes.py:838:5:838:19 | ControlFlowNode for BinaryExpr |
| classes.py:838:16:838:19 | ControlFlowNode for arg2 | classes.py:828:23:828:27 | ControlFlowNode for other |
| classes.py:845:15:845:18 | ControlFlowNode for self | classes.py:853:5:853:19 | ControlFlowNode for BinaryExpr |
| classes.py:847:16:847:19 | ControlFlowNode for self | classes.py:853:5:853:19 | ControlFlowNode for BinaryExpr |
| classes.py:853:5:853:12 | ControlFlowNode for with_xor | classes.py:843:17:843:20 | ControlFlowNode for self |
| classes.py:853:5:853:12 | ControlFlowNode for with_xor | classes.py:853:5:853:19 | ControlFlowNode for BinaryExpr |
| classes.py:853:16:853:19 | ControlFlowNode for arg2 | classes.py:843:23:843:27 | ControlFlowNode for other |
| classes.py:860:15:860:18 | ControlFlowNode for self | classes.py:868:5:868:18 | ControlFlowNode for BinaryExpr |
| classes.py:862:16:862:19 | ControlFlowNode for self | classes.py:868:5:868:18 | ControlFlowNode for BinaryExpr |
| classes.py:868:5:868:11 | ControlFlowNode for with_or | classes.py:858:16:858:19 | ControlFlowNode for self |
| classes.py:868:5:868:11 | ControlFlowNode for with_or | classes.py:868:5:868:18 | ControlFlowNode for BinaryExpr |
| classes.py:868:15:868:18 | ControlFlowNode for arg2 | classes.py:858:22:858:26 | ControlFlowNode for other |

View File

@@ -1,37 +0,0 @@
import semmle.python.dataflow.new.DataFlow
private import semmle.python.dataflow.new.internal.DataFlowPrivate as DataFlowPrivate
/**
* A configuration to find the call graph edges.
*/
class CallGraphConfig extends DataFlow::Configuration {
CallGraphConfig() { this = "CallGraphConfig" }
override predicate isSource(DataFlow::Node node) {
node instanceof DataFlowPrivate::ReturnNode
or
// These sources should allow for the non-standard call syntax
node instanceof DataFlow::ArgumentNode
}
override predicate isSink(DataFlow::Node node) {
node instanceof DataFlowPrivate::OutNode
or
node instanceof DataFlow::ParameterNode and
// exclude parameters to the SINK-functions
not exists(DataFlowPrivate::DataFlowCallable c |
c.getParameter(_) = node.asCfgNode() and
c.getName().matches("SINK_")
)
}
}
from DataFlow::Node source, DataFlow::Node sink
where
source.getLocation().getFile().getBaseName() = "classes.py" and
sink.getLocation().getFile().getBaseName() = "classes.py" and
exists(CallGraphConfig cfg | cfg.hasFlow(source, sink))
select source, sink
// Ideally, we would just have 1-step paths either from argument to parameter
// or from return to call. This gives a bit more, so should be rewritten.
// We should also consider splitting this into two, one for each direction.

View File

@@ -19,3 +19,5 @@ reverseRead
argHasPostUpdate
postWithInFlow
viableImplInCallContextTooLarge
uniqueParameterNodeAtPosition
uniqueParameterNodePosition

View File

@@ -1,970 +0,0 @@
edges
| datamodel.py:35:7:35:7 | ControlFlowNode for a | datamodel.py:36:10:36:10 | ControlFlowNode for a |
| datamodel.py:38:8:38:13 | ControlFlowNode for SOURCE | datamodel.py:35:7:35:7 | ControlFlowNode for a |
| datamodel.py:38:8:38:13 | ControlFlowNode for SOURCE | datamodel.py:38:6:38:17 | ControlFlowNode for f() |
| datamodel.py:44:22:44:22 | ControlFlowNode for x | datamodel.py:46:16:46:16 | ControlFlowNode for x |
| datamodel.py:49:26:49:26 | ControlFlowNode for x | datamodel.py:50:16:50:16 | ControlFlowNode for x |
| datamodel.py:71:15:71:20 | ControlFlowNode for SOURCE | datamodel.py:44:22:44:22 | ControlFlowNode for x |
| datamodel.py:71:15:71:20 | ControlFlowNode for SOURCE | datamodel.py:71:6:71:24 | ControlFlowNode for Attribute() |
| datamodel.py:72:18:72:23 | ControlFlowNode for SOURCE | datamodel.py:44:22:44:22 | ControlFlowNode for x |
| datamodel.py:72:18:72:23 | ControlFlowNode for SOURCE | datamodel.py:72:6:72:27 | ControlFlowNode for Attribute() |
| datamodel.py:80:20:80:25 | ControlFlowNode for SOURCE | datamodel.py:49:26:49:26 | ControlFlowNode for x |
| datamodel.py:80:20:80:25 | ControlFlowNode for SOURCE | datamodel.py:80:6:80:26 | ControlFlowNode for Attribute() |
| datamodel.py:81:20:81:25 | ControlFlowNode for SOURCE | datamodel.py:49:26:49:26 | ControlFlowNode for x |
| datamodel.py:81:20:81:25 | ControlFlowNode for SOURCE | datamodel.py:81:6:81:26 | ControlFlowNode for Attribute() |
| datamodel.py:152:5:152:8 | [post store] ControlFlowNode for self [Attribute b] | datamodel.py:155:14:155:25 | ControlFlowNode for Customized() [Attribute b] |
| datamodel.py:152:14:152:19 | ControlFlowNode for SOURCE | datamodel.py:152:5:152:8 | [post store] ControlFlowNode for self [Attribute b] |
| datamodel.py:155:14:155:25 | ControlFlowNode for Customized() [Attribute b] | datamodel.py:159:6:159:15 | ControlFlowNode for customized [Attribute b] |
| datamodel.py:159:6:159:15 | ControlFlowNode for customized [Attribute b] | datamodel.py:159:6:159:17 | ControlFlowNode for Attribute |
| test.py:42:10:42:26 | ControlFlowNode for Tuple [Tuple element at index 1] | test.py:43:9:43:9 | ControlFlowNode for x [Tuple element at index 1] |
| test.py:42:21:42:26 | ControlFlowNode for SOURCE | test.py:42:10:42:26 | ControlFlowNode for Tuple [Tuple element at index 1] |
| test.py:43:9:43:9 | ControlFlowNode for x [Tuple element at index 1] | test.py:43:9:43:12 | ControlFlowNode for Subscript |
| test.py:43:9:43:12 | ControlFlowNode for Subscript | test.py:44:10:44:10 | ControlFlowNode for y |
| test.py:55:9:55:14 | ControlFlowNode for SOURCE | test.py:56:10:56:10 | ControlFlowNode for x |
| test.py:61:9:61:16 | ControlFlowNode for Str | test.py:62:10:62:10 | ControlFlowNode for x |
| test.py:66:9:66:17 | ControlFlowNode for Str | test.py:67:10:67:10 | ControlFlowNode for x |
| test.py:71:9:71:10 | ControlFlowNode for IntegerLiteral | test.py:72:10:72:10 | ControlFlowNode for x |
| test.py:76:9:76:12 | ControlFlowNode for FloatLiteral | test.py:77:10:77:10 | ControlFlowNode for x |
| test.py:87:10:87:15 | ControlFlowNode for SOURCE | test.py:88:10:88:10 | ControlFlowNode for x |
| test.py:93:9:93:16 | ControlFlowNode for List [List element] | test.py:94:10:94:10 | ControlFlowNode for x [List element] |
| test.py:93:10:93:15 | ControlFlowNode for SOURCE | test.py:93:9:93:16 | ControlFlowNode for List [List element] |
| test.py:94:10:94:10 | ControlFlowNode for x [List element] | test.py:94:10:94:13 | ControlFlowNode for Subscript |
| test.py:103:9:103:37 | ControlFlowNode for ListComp [List element] | test.py:104:10:104:10 | ControlFlowNode for x [List element] |
| test.py:103:10:103:15 | ControlFlowNode for SOURCE | test.py:103:9:103:37 | ControlFlowNode for ListComp [List element] |
| test.py:104:10:104:10 | ControlFlowNode for x [List element] | test.py:104:10:104:13 | ControlFlowNode for Subscript |
| test.py:108:9:108:29 | ControlFlowNode for ListComp [List element] | test.py:109:10:109:10 | ControlFlowNode for x [List element] |
| test.py:108:10:108:10 | ControlFlowNode for y | test.py:108:9:108:29 | ControlFlowNode for ListComp [List element] |
| test.py:108:16:108:16 | SSA variable y | test.py:108:10:108:10 | ControlFlowNode for y |
| test.py:108:21:108:28 | ControlFlowNode for List [List element] | test.py:108:16:108:16 | SSA variable y |
| test.py:108:22:108:27 | ControlFlowNode for SOURCE | test.py:108:21:108:28 | ControlFlowNode for List [List element] |
| test.py:109:10:109:10 | ControlFlowNode for x [List element] | test.py:109:10:109:13 | ControlFlowNode for Subscript |
| test.py:113:9:113:16 | ControlFlowNode for List [List element] | test.py:114:21:114:21 | ControlFlowNode for l [List element] |
| test.py:113:10:113:15 | ControlFlowNode for SOURCE | test.py:113:9:113:16 | ControlFlowNode for List [List element] |
| test.py:114:9:114:22 | ControlFlowNode for ListComp [List element] | test.py:115:10:115:10 | ControlFlowNode for x [List element] |
| test.py:114:10:114:10 | ControlFlowNode for y | test.py:114:9:114:22 | ControlFlowNode for ListComp [List element] |
| test.py:114:16:114:16 | SSA variable y | test.py:114:10:114:10 | ControlFlowNode for y |
| test.py:114:21:114:21 | ControlFlowNode for l [List element] | test.py:114:16:114:16 | SSA variable y |
| test.py:115:10:115:10 | ControlFlowNode for x [List element] | test.py:115:10:115:13 | ControlFlowNode for Subscript |
| test.py:125:9:125:16 | ControlFlowNode for Set [Set element] | test.py:126:10:126:10 | ControlFlowNode for x [Set element] |
| test.py:125:10:125:15 | ControlFlowNode for SOURCE | test.py:125:9:125:16 | ControlFlowNode for Set [Set element] |
| test.py:126:10:126:10 | ControlFlowNode for x [Set element] | test.py:126:10:126:16 | ControlFlowNode for Attribute() |
| test.py:130:9:130:37 | ControlFlowNode for SetComp [Set element] | test.py:131:10:131:10 | ControlFlowNode for x [Set element] |
| test.py:130:10:130:15 | ControlFlowNode for SOURCE | test.py:130:9:130:37 | ControlFlowNode for SetComp [Set element] |
| test.py:131:10:131:10 | ControlFlowNode for x [Set element] | test.py:131:10:131:16 | ControlFlowNode for Attribute() |
| test.py:135:9:135:29 | ControlFlowNode for SetComp [Set element] | test.py:136:10:136:10 | ControlFlowNode for x [Set element] |
| test.py:135:10:135:10 | ControlFlowNode for y | test.py:135:9:135:29 | ControlFlowNode for SetComp [Set element] |
| test.py:135:16:135:16 | SSA variable y | test.py:135:10:135:10 | ControlFlowNode for y |
| test.py:135:21:135:28 | ControlFlowNode for List [List element] | test.py:135:16:135:16 | SSA variable y |
| test.py:135:22:135:27 | ControlFlowNode for SOURCE | test.py:135:21:135:28 | ControlFlowNode for List [List element] |
| test.py:136:10:136:10 | ControlFlowNode for x [Set element] | test.py:136:10:136:16 | ControlFlowNode for Attribute() |
| test.py:140:9:140:16 | ControlFlowNode for Set [Set element] | test.py:141:21:141:21 | ControlFlowNode for l [Set element] |
| test.py:140:10:140:15 | ControlFlowNode for SOURCE | test.py:140:9:140:16 | ControlFlowNode for Set [Set element] |
| test.py:141:9:141:22 | ControlFlowNode for SetComp [Set element] | test.py:142:10:142:10 | ControlFlowNode for x [Set element] |
| test.py:141:10:141:10 | ControlFlowNode for y | test.py:141:9:141:22 | ControlFlowNode for SetComp [Set element] |
| test.py:141:16:141:16 | SSA variable y | test.py:141:10:141:10 | ControlFlowNode for y |
| test.py:141:21:141:21 | ControlFlowNode for l [Set element] | test.py:141:16:141:16 | SSA variable y |
| test.py:142:10:142:10 | ControlFlowNode for x [Set element] | test.py:142:10:142:16 | ControlFlowNode for Attribute() |
| test.py:152:9:152:21 | ControlFlowNode for Dict [Dictionary element at key s] | test.py:153:10:153:10 | ControlFlowNode for x [Dictionary element at key s] |
| test.py:152:15:152:20 | ControlFlowNode for SOURCE | test.py:152:9:152:21 | ControlFlowNode for Dict [Dictionary element at key s] |
| test.py:153:10:153:10 | ControlFlowNode for x [Dictionary element at key s] | test.py:153:10:153:15 | ControlFlowNode for Subscript |
| test.py:157:9:157:21 | ControlFlowNode for Dict [Dictionary element at key s] | test.py:158:10:158:10 | ControlFlowNode for x [Dictionary element at key s] |
| test.py:157:15:157:20 | ControlFlowNode for SOURCE | test.py:157:9:157:21 | ControlFlowNode for Dict [Dictionary element at key s] |
| test.py:158:10:158:10 | ControlFlowNode for x [Dictionary element at key s] | test.py:158:10:158:19 | ControlFlowNode for Attribute() |
| test.py:183:9:183:42 | ControlFlowNode for ListComp [List element] | test.py:184:10:184:10 | ControlFlowNode for x [List element] |
| test.py:183:10:183:10 | ControlFlowNode for y | test.py:183:9:183:42 | ControlFlowNode for ListComp [List element] |
| test.py:183:16:183:16 | SSA variable z [List element] | test.py:183:41:183:41 | ControlFlowNode for z [List element] |
| test.py:183:21:183:30 | ControlFlowNode for List [List element, List element] | test.py:183:16:183:16 | SSA variable z [List element] |
| test.py:183:22:183:29 | ControlFlowNode for List [List element] | test.py:183:21:183:30 | ControlFlowNode for List [List element, List element] |
| test.py:183:23:183:28 | ControlFlowNode for SOURCE | test.py:183:22:183:29 | ControlFlowNode for List [List element] |
| test.py:183:36:183:36 | SSA variable y | test.py:183:10:183:10 | ControlFlowNode for y |
| test.py:183:41:183:41 | ControlFlowNode for z [List element] | test.py:183:36:183:36 | SSA variable y |
| test.py:184:10:184:10 | ControlFlowNode for x [List element] | test.py:184:10:184:13 | ControlFlowNode for Subscript |
| test.py:188:9:188:68 | ControlFlowNode for ListComp [List element] | test.py:189:10:189:10 | ControlFlowNode for x [List element] |
| test.py:188:10:188:10 | ControlFlowNode for y | test.py:188:9:188:68 | ControlFlowNode for ListComp [List element] |
| test.py:188:16:188:16 | SSA variable v [List element, List element, List element] | test.py:188:45:188:45 | ControlFlowNode for v [List element, List element, List element] |
| test.py:188:21:188:34 | ControlFlowNode for List [List element, List element, List element, List element] | test.py:188:16:188:16 | SSA variable v [List element, List element, List element] |
| test.py:188:22:188:33 | ControlFlowNode for List [List element, List element, List element] | test.py:188:21:188:34 | ControlFlowNode for List [List element, List element, List element, List element] |
| test.py:188:23:188:32 | ControlFlowNode for List [List element, List element] | test.py:188:22:188:33 | ControlFlowNode for List [List element, List element, List element] |
| test.py:188:24:188:31 | ControlFlowNode for List [List element] | test.py:188:23:188:32 | ControlFlowNode for List [List element, List element] |
| test.py:188:25:188:30 | ControlFlowNode for SOURCE | test.py:188:24:188:31 | ControlFlowNode for List [List element] |
| test.py:188:40:188:40 | SSA variable u [List element, List element] | test.py:188:56:188:56 | ControlFlowNode for u [List element, List element] |
| test.py:188:45:188:45 | ControlFlowNode for v [List element, List element, List element] | test.py:188:40:188:40 | SSA variable u [List element, List element] |
| test.py:188:51:188:51 | SSA variable z [List element] | test.py:188:67:188:67 | ControlFlowNode for z [List element] |
| test.py:188:56:188:56 | ControlFlowNode for u [List element, List element] | test.py:188:51:188:51 | SSA variable z [List element] |
| test.py:188:62:188:62 | SSA variable y | test.py:188:10:188:10 | ControlFlowNode for y |
| test.py:188:67:188:67 | ControlFlowNode for z [List element] | test.py:188:62:188:62 | SSA variable y |
| test.py:189:10:189:10 | ControlFlowNode for x [List element] | test.py:189:10:189:13 | ControlFlowNode for Subscript |
| test.py:199:9:199:42 | ControlFlowNode for ListComp [List element] | test.py:200:10:200:10 | ControlFlowNode for x [List element] |
| test.py:199:10:199:10 | ControlFlowNode for y | test.py:199:9:199:42 | ControlFlowNode for ListComp [List element] |
| test.py:199:16:199:16 | SSA variable y | test.py:199:10:199:10 | ControlFlowNode for y |
| test.py:199:22:199:22 | ControlFlowNode for z | test.py:199:22:199:40 | ControlFlowNode for GeneratorExp [List element] |
| test.py:199:22:199:40 | ControlFlowNode for GeneratorExp [List element] | test.py:199:16:199:16 | SSA variable y |
| test.py:199:28:199:28 | SSA variable z | test.py:199:22:199:22 | ControlFlowNode for z |
| test.py:199:33:199:40 | ControlFlowNode for List [List element] | test.py:199:28:199:28 | SSA variable z |
| test.py:199:34:199:39 | ControlFlowNode for SOURCE | test.py:199:33:199:40 | ControlFlowNode for List [List element] |
| test.py:200:10:200:10 | ControlFlowNode for x [List element] | test.py:200:10:200:13 | ControlFlowNode for Subscript |
| test.py:205:9:205:47 | ControlFlowNode for ListComp [List element] | test.py:206:10:206:10 | ControlFlowNode for x [List element] |
| test.py:205:10:205:10 | ControlFlowNode for a | test.py:205:9:205:47 | ControlFlowNode for ListComp [List element] |
| test.py:205:17:205:17 | SSA variable a | test.py:205:10:205:10 | ControlFlowNode for a |
| test.py:205:17:205:20 | ControlFlowNode for Tuple [Tuple element at index 0] | test.py:205:17:205:17 | SSA variable a |
| test.py:205:17:205:20 | IterableSequence [Tuple element at index 0] | test.py:205:17:205:20 | ControlFlowNode for Tuple [Tuple element at index 0] |
| test.py:205:26:205:46 | ControlFlowNode for List [List element, Tuple element at index 0] | test.py:205:17:205:20 | IterableSequence [Tuple element at index 0] |
| test.py:205:28:205:33 | ControlFlowNode for SOURCE | test.py:205:28:205:44 | ControlFlowNode for Tuple [Tuple element at index 0] |
| test.py:205:28:205:44 | ControlFlowNode for Tuple [Tuple element at index 0] | test.py:205:26:205:46 | ControlFlowNode for List [List element, Tuple element at index 0] |
| test.py:206:10:206:10 | ControlFlowNode for x [List element] | test.py:206:10:206:13 | ControlFlowNode for Subscript |
| test.py:210:9:210:51 | ControlFlowNode for ListComp [List element] | test.py:211:10:211:10 | ControlFlowNode for x [List element] |
| test.py:210:10:210:10 | ControlFlowNode for a [List element] | test.py:210:10:210:13 | ControlFlowNode for Subscript |
| test.py:210:10:210:13 | ControlFlowNode for Subscript | test.py:210:9:210:51 | ControlFlowNode for ListComp [List element] |
| test.py:210:20:210:21 | IterableElement | test.py:210:20:210:21 | SSA variable a [List element] |
| test.py:210:20:210:21 | SSA variable a [List element] | test.py:210:10:210:10 | ControlFlowNode for a [List element] |
| test.py:210:20:210:24 | ControlFlowNode for Tuple [Tuple element at index 0] | test.py:210:20:210:21 | IterableElement |
| test.py:210:20:210:24 | IterableSequence [Tuple element at index 0] | test.py:210:20:210:24 | ControlFlowNode for Tuple [Tuple element at index 0] |
| test.py:210:30:210:50 | ControlFlowNode for List [List element, Tuple element at index 0] | test.py:210:20:210:24 | IterableSequence [Tuple element at index 0] |
| test.py:210:32:210:37 | ControlFlowNode for SOURCE | test.py:210:32:210:48 | ControlFlowNode for Tuple [Tuple element at index 0] |
| test.py:210:32:210:48 | ControlFlowNode for Tuple [Tuple element at index 0] | test.py:210:30:210:50 | ControlFlowNode for List [List element, Tuple element at index 0] |
| test.py:211:10:211:10 | ControlFlowNode for x [List element] | test.py:211:10:211:13 | ControlFlowNode for Subscript |
| test.py:349:11:349:16 | ControlFlowNode for SOURCE | test.py:349:11:349:17 | ControlFlowNode for Tuple [Tuple element at index 0] |
| test.py:349:11:349:17 | ControlFlowNode for Tuple [Tuple element at index 0] | test.py:349:10:349:21 | ControlFlowNode for Subscript |
| test.py:353:10:353:17 | ControlFlowNode for List [List element] | test.py:353:10:353:20 | ControlFlowNode for Subscript |
| test.py:353:11:353:16 | ControlFlowNode for SOURCE | test.py:353:10:353:17 | ControlFlowNode for List [List element] |
| test.py:357:10:357:22 | ControlFlowNode for Dict [Dictionary element at key s] | test.py:357:10:357:27 | ControlFlowNode for Subscript |
| test.py:357:16:357:21 | ControlFlowNode for SOURCE | test.py:357:10:357:22 | ControlFlowNode for Dict [Dictionary element at key s] |
| test.py:375:15:375:15 | ControlFlowNode for b | test.py:376:12:376:12 | ControlFlowNode for b |
| test.py:380:28:380:33 | ControlFlowNode for SOURCE | test.py:375:15:375:15 | ControlFlowNode for b |
| test.py:380:28:380:33 | ControlFlowNode for SOURCE | test.py:380:10:380:34 | ControlFlowNode for second() |
| test.py:388:30:388:35 | ControlFlowNode for SOURCE | test.py:375:15:375:15 | ControlFlowNode for b |
| test.py:388:30:388:35 | ControlFlowNode for SOURCE | test.py:388:10:388:36 | ControlFlowNode for second() |
| test.py:396:10:396:43 | KwUnpacked b | test.py:375:15:375:15 | ControlFlowNode for b |
| test.py:396:10:396:43 | KwUnpacked b | test.py:396:10:396:43 | ControlFlowNode for second() |
| test.py:396:30:396:42 | ControlFlowNode for Dict [Dictionary element at key b] | test.py:396:10:396:43 | KwUnpacked b |
| test.py:396:36:396:41 | ControlFlowNode for SOURCE | test.py:396:30:396:42 | ControlFlowNode for Dict [Dictionary element at key b] |
| test.py:399:21:399:21 | ControlFlowNode for b [Tuple element at index 0] | test.py:400:12:400:12 | ControlFlowNode for b [Tuple element at index 0] |
| test.py:400:12:400:12 | ControlFlowNode for b [Tuple element at index 0] | test.py:400:12:400:15 | ControlFlowNode for Subscript |
| test.py:404:10:404:39 | PosOverflowNode for f_extra_pos() [Tuple element at index 0] | test.py:399:21:399:21 | ControlFlowNode for b [Tuple element at index 0] |
| test.py:404:10:404:39 | PosOverflowNode for f_extra_pos() [Tuple element at index 0] | test.py:404:10:404:39 | ControlFlowNode for f_extra_pos() |
| test.py:404:33:404:38 | ControlFlowNode for SOURCE | test.py:404:10:404:39 | PosOverflowNode for f_extra_pos() [Tuple element at index 0] |
| test.py:407:26:407:26 | ControlFlowNode for b [Dictionary element at key b] | test.py:408:12:408:12 | ControlFlowNode for b [Dictionary element at key b] |
| test.py:408:12:408:12 | ControlFlowNode for b [Dictionary element at key b] | test.py:408:12:408:17 | ControlFlowNode for Subscript |
| test.py:412:10:412:45 | KwOverflowNode for f_extra_keyword() [Dictionary element at key b] | test.py:407:26:407:26 | ControlFlowNode for b [Dictionary element at key b] |
| test.py:412:10:412:45 | KwOverflowNode for f_extra_keyword() [Dictionary element at key b] | test.py:412:10:412:45 | ControlFlowNode for f_extra_keyword() |
| test.py:412:39:412:44 | ControlFlowNode for SOURCE | test.py:412:10:412:45 | KwOverflowNode for f_extra_keyword() [Dictionary element at key b] |
| test.py:429:15:429:20 | ControlFlowNode for SOURCE | test.py:429:10:429:20 | ControlFlowNode for BoolExpr |
| test.py:434:16:434:21 | ControlFlowNode for SOURCE | test.py:434:10:434:21 | ControlFlowNode for BoolExpr |
| test.py:445:10:445:15 | ControlFlowNode for SOURCE | test.py:445:10:445:38 | ControlFlowNode for IfExp |
| test.py:453:34:453:39 | ControlFlowNode for SOURCE | test.py:453:10:453:39 | ControlFlowNode for IfExp |
| test.py:474:11:474:11 | ControlFlowNode for x | test.py:475:16:475:16 | ControlFlowNode for x |
| test.py:477:12:477:17 | ControlFlowNode for SOURCE | test.py:474:11:474:11 | ControlFlowNode for x |
| test.py:477:12:477:17 | ControlFlowNode for SOURCE | test.py:477:10:477:18 | ControlFlowNode for f() |
| test.py:481:19:481:19 | ControlFlowNode for b | test.py:482:16:482:16 | ControlFlowNode for b |
| test.py:484:28:484:33 | ControlFlowNode for SOURCE | test.py:481:19:481:19 | ControlFlowNode for b |
| test.py:484:28:484:33 | ControlFlowNode for SOURCE | test.py:484:10:484:34 | ControlFlowNode for second() |
| test.py:495:19:495:19 | ControlFlowNode for b | test.py:496:16:496:16 | ControlFlowNode for b |
| test.py:498:30:498:35 | ControlFlowNode for SOURCE | test.py:495:19:495:19 | ControlFlowNode for b |
| test.py:498:30:498:35 | ControlFlowNode for SOURCE | test.py:498:10:498:36 | ControlFlowNode for second() |
| test.py:509:19:509:19 | ControlFlowNode for b | test.py:510:16:510:16 | ControlFlowNode for b |
| test.py:512:10:512:43 | KwUnpacked b | test.py:509:19:509:19 | ControlFlowNode for b |
| test.py:512:10:512:43 | KwUnpacked b | test.py:512:10:512:43 | ControlFlowNode for second() |
| test.py:512:30:512:42 | ControlFlowNode for Dict [Dictionary element at key b] | test.py:512:10:512:43 | KwUnpacked b |
| test.py:512:36:512:41 | ControlFlowNode for SOURCE | test.py:512:30:512:42 | ControlFlowNode for Dict [Dictionary element at key b] |
| test.py:516:30:516:30 | ControlFlowNode for b [Tuple element at index 0] | test.py:516:33:516:33 | ControlFlowNode for b [Tuple element at index 0] |
| test.py:516:33:516:33 | ControlFlowNode for b [Tuple element at index 0] | test.py:516:33:516:36 | ControlFlowNode for Subscript |
| test.py:517:10:517:39 | PosOverflowNode for f_extra_pos() [Tuple element at index 0] | test.py:516:30:516:30 | ControlFlowNode for b [Tuple element at index 0] |
| test.py:517:10:517:39 | PosOverflowNode for f_extra_pos() [Tuple element at index 0] | test.py:517:10:517:39 | ControlFlowNode for f_extra_pos() |
| test.py:517:33:517:38 | ControlFlowNode for SOURCE | test.py:517:10:517:39 | PosOverflowNode for f_extra_pos() [Tuple element at index 0] |
| test.py:521:35:521:35 | ControlFlowNode for b [Dictionary element at key b] | test.py:521:38:521:38 | ControlFlowNode for b [Dictionary element at key b] |
| test.py:521:38:521:38 | ControlFlowNode for b [Dictionary element at key b] | test.py:521:38:521:43 | ControlFlowNode for Subscript |
| test.py:522:10:522:45 | KwOverflowNode for f_extra_keyword() [Dictionary element at key b] | test.py:521:35:521:35 | ControlFlowNode for b [Dictionary element at key b] |
| test.py:522:10:522:45 | KwOverflowNode for f_extra_keyword() [Dictionary element at key b] | test.py:522:10:522:45 | ControlFlowNode for f_extra_keyword() |
| test.py:522:39:522:44 | ControlFlowNode for SOURCE | test.py:522:10:522:45 | KwOverflowNode for f_extra_keyword() [Dictionary element at key b] |
| test.py:534:9:534:14 | ControlFlowNode for SOURCE | test.py:536:10:536:10 | ControlFlowNode for a |
| test.py:534:9:534:14 | ControlFlowNode for SOURCE | test.py:541:10:541:10 | ControlFlowNode for b |
| test.py:546:10:546:15 | ControlFlowNode for SOURCE | test.py:546:10:546:26 | ControlFlowNode for Tuple [Tuple element at index 0] |
| test.py:546:10:546:26 | ControlFlowNode for Tuple [Tuple element at index 0] | test.py:547:5:547:8 | ControlFlowNode for Tuple [Tuple element at index 0] |
| test.py:547:5:547:5 | SSA variable a | test.py:548:10:548:10 | ControlFlowNode for a |
| test.py:547:5:547:8 | ControlFlowNode for Tuple [Tuple element at index 0] | test.py:547:5:547:5 | SSA variable a |
| test.py:554:10:554:15 | ControlFlowNode for SOURCE | test.py:554:10:554:36 | ControlFlowNode for Tuple [Tuple element at index 0] |
| test.py:554:10:554:36 | ControlFlowNode for Tuple [Tuple element at index 0] | test.py:555:5:555:13 | ControlFlowNode for Tuple [Tuple element at index 0] |
| test.py:554:10:554:36 | ControlFlowNode for Tuple [Tuple element at index 1, Tuple element at index 1] | test.py:555:5:555:13 | ControlFlowNode for Tuple [Tuple element at index 1, Tuple element at index 1] |
| test.py:554:19:554:35 | ControlFlowNode for Tuple [Tuple element at index 1] | test.py:554:10:554:36 | ControlFlowNode for Tuple [Tuple element at index 1, Tuple element at index 1] |
| test.py:554:30:554:35 | ControlFlowNode for SOURCE | test.py:554:19:554:35 | ControlFlowNode for Tuple [Tuple element at index 1] |
| test.py:555:5:555:5 | SSA variable a | test.py:556:10:556:10 | ControlFlowNode for a |
| test.py:555:5:555:13 | ControlFlowNode for Tuple [Tuple element at index 0] | test.py:555:5:555:5 | SSA variable a |
| test.py:555:5:555:13 | ControlFlowNode for Tuple [Tuple element at index 1, Tuple element at index 1] | test.py:555:9:555:12 | IterableSequence [Tuple element at index 1] |
| test.py:555:9:555:12 | ControlFlowNode for Tuple [Tuple element at index 1] | test.py:555:12:555:12 | SSA variable c |
| test.py:555:9:555:12 | IterableSequence [Tuple element at index 1] | test.py:555:9:555:12 | ControlFlowNode for Tuple [Tuple element at index 1] |
| test.py:555:12:555:12 | SSA variable c | test.py:558:10:558:10 | ControlFlowNode for c |
| test.py:563:9:563:33 | ControlFlowNode for List [List element, List element, List element, List element] | test.py:564:5:564:14 | IterableSequence [List element, List element, List element, List element] |
| test.py:563:10:563:21 | ControlFlowNode for List [List element, List element, List element] | test.py:563:9:563:33 | ControlFlowNode for List [List element, List element, List element, List element] |
| test.py:563:11:563:20 | ControlFlowNode for List [List element, List element] | test.py:563:10:563:21 | ControlFlowNode for List [List element, List element, List element] |
| test.py:563:12:563:19 | ControlFlowNode for List [List element] | test.py:563:11:563:20 | ControlFlowNode for List [List element, List element] |
| test.py:563:13:563:18 | ControlFlowNode for SOURCE | test.py:563:12:563:19 | ControlFlowNode for List [List element] |
| test.py:564:5:564:11 | ControlFlowNode for List [Tuple element at index 0, List element, List element] | test.py:564:6:564:10 | IterableSequence [List element, List element] |
| test.py:564:5:564:11 | IterableElement [List element, List element] | test.py:564:5:564:11 | ControlFlowNode for List [Tuple element at index 0, List element, List element] |
| test.py:564:5:564:11 | IterableSequence [List element, List element, List element] | test.py:564:5:564:11 | IterableElement [List element, List element] |
| test.py:564:5:564:14 | ControlFlowNode for Tuple [Tuple element at index 0, List element, List element, List element] | test.py:564:5:564:11 | IterableSequence [List element, List element, List element] |
| test.py:564:5:564:14 | IterableElement [List element, List element, List element] | test.py:564:5:564:14 | ControlFlowNode for Tuple [Tuple element at index 0, List element, List element, List element] |
| test.py:564:5:564:14 | IterableSequence [List element, List element, List element, List element] | test.py:564:5:564:14 | IterableElement [List element, List element, List element] |
| test.py:564:6:564:10 | ControlFlowNode for List [Tuple element at index 0, List element] | test.py:564:7:564:9 | IterableSequence [List element] |
| test.py:564:6:564:10 | IterableElement [List element] | test.py:564:6:564:10 | ControlFlowNode for List [Tuple element at index 0, List element] |
| test.py:564:6:564:10 | IterableSequence [List element, List element] | test.py:564:6:564:10 | IterableElement [List element] |
| test.py:564:7:564:9 | ControlFlowNode for List [Tuple element at index 0] | test.py:564:8:564:8 | SSA variable a |
| test.py:564:7:564:9 | IterableElement | test.py:564:7:564:9 | ControlFlowNode for List [Tuple element at index 0] |
| test.py:564:7:564:9 | IterableSequence [List element] | test.py:564:7:564:9 | IterableElement |
| test.py:564:8:564:8 | SSA variable a | test.py:565:10:565:10 | ControlFlowNode for a |
| test.py:571:10:571:15 | ControlFlowNode for SOURCE | test.py:571:10:571:34 | ControlFlowNode for Tuple [Tuple element at index 0] |
| test.py:571:10:571:34 | ControlFlowNode for Tuple [Tuple element at index 0] | test.py:572:5:572:12 | ControlFlowNode for Tuple [Tuple element at index 0] |
| test.py:571:10:571:34 | ControlFlowNode for Tuple [Tuple element at index 1] | test.py:572:5:572:12 | ControlFlowNode for Tuple [Tuple element at index 1] |
| test.py:571:18:571:23 | ControlFlowNode for SOURCE | test.py:571:10:571:34 | ControlFlowNode for Tuple [Tuple element at index 1] |
| test.py:572:5:572:5 | SSA variable a | test.py:573:10:573:10 | ControlFlowNode for a |
| test.py:572:5:572:12 | ControlFlowNode for Tuple [Tuple element at index 0] | test.py:572:5:572:5 | SSA variable a |
| test.py:572:5:572:12 | ControlFlowNode for Tuple [Tuple element at index 1] | test.py:572:8:572:9 | IterableElement |
| test.py:572:5:572:12 | ControlFlowNode for Tuple [Tuple element at index 1] | test.py:572:12:572:12 | SSA variable c |
| test.py:572:8:572:9 | IterableElement | test.py:572:8:572:9 | SSA variable b [List element] |
| test.py:572:8:572:9 | SSA variable b [List element] | test.py:575:10:575:10 | ControlFlowNode for b [List element] |
| test.py:572:12:572:12 | SSA variable c | test.py:576:12:576:12 | ControlFlowNode for c |
| test.py:575:10:575:10 | ControlFlowNode for b [List element] | test.py:575:10:575:13 | ControlFlowNode for Subscript |
| test.py:581:10:581:15 | ControlFlowNode for SOURCE | test.py:581:10:581:23 | ControlFlowNode for Tuple [Tuple element at index 0] |
| test.py:581:10:581:23 | ControlFlowNode for Tuple [Tuple element at index 0] | test.py:582:5:582:12 | ControlFlowNode for Tuple [Tuple element at index 0] |
| test.py:581:10:581:23 | ControlFlowNode for Tuple [Tuple element at index 1] | test.py:582:5:582:12 | ControlFlowNode for Tuple [Tuple element at index 1] |
| test.py:581:18:581:23 | ControlFlowNode for SOURCE | test.py:581:10:581:23 | ControlFlowNode for Tuple [Tuple element at index 1] |
| test.py:582:5:582:5 | SSA variable a | test.py:583:10:583:10 | ControlFlowNode for a |
| test.py:582:5:582:12 | ControlFlowNode for Tuple [Tuple element at index 0] | test.py:582:5:582:5 | SSA variable a |
| test.py:582:5:582:12 | ControlFlowNode for Tuple [Tuple element at index 1] | test.py:582:12:582:12 | SSA variable c |
| test.py:582:12:582:12 | SSA variable c | test.py:585:10:585:10 | ControlFlowNode for c |
| test.py:590:10:590:61 | ControlFlowNode for List [List element, List element] | test.py:593:6:593:23 | IterableSequence [List element, List element] |
| test.py:590:10:590:61 | ControlFlowNode for List [List element, List element] | test.py:601:5:601:24 | IterableSequence [List element, List element] |
| test.py:590:10:590:61 | ControlFlowNode for List [List element, List element] | test.py:609:6:609:23 | IterableSequence [List element, List element] |
| test.py:590:11:590:37 | ControlFlowNode for List [List element] | test.py:590:10:590:61 | ControlFlowNode for List [List element, List element] |
| test.py:590:12:590:17 | ControlFlowNode for SOURCE | test.py:590:11:590:37 | ControlFlowNode for List [List element] |
| test.py:590:31:590:36 | ControlFlowNode for SOURCE | test.py:590:11:590:37 | ControlFlowNode for List [List element] |
| test.py:590:40:590:47 | ControlFlowNode for List [List element] | test.py:590:10:590:61 | ControlFlowNode for List [List element, List element] |
| test.py:590:41:590:46 | ControlFlowNode for SOURCE | test.py:590:40:590:47 | ControlFlowNode for List [List element] |
| test.py:593:6:593:23 | ControlFlowNode for Tuple [Tuple element at index 0, List element] | test.py:593:7:593:16 | IterableSequence [List element] |
| test.py:593:6:593:23 | IterableElement [List element] | test.py:593:6:593:23 | ControlFlowNode for Tuple [Tuple element at index 0, List element] |
| test.py:593:6:593:23 | IterableSequence [List element, List element] | test.py:593:6:593:23 | IterableElement [List element] |
| test.py:593:7:593:8 | SSA variable a1 | test.py:594:10:594:11 | ControlFlowNode for a1 |
| test.py:593:7:593:16 | ControlFlowNode for Tuple [Tuple element at index 0] | test.py:593:7:593:8 | SSA variable a1 |
| test.py:593:7:593:16 | ControlFlowNode for Tuple [Tuple element at index 1] | test.py:593:11:593:12 | SSA variable a2 |
| test.py:593:7:593:16 | ControlFlowNode for Tuple [Tuple element at index 2] | test.py:593:15:593:16 | SSA variable a3 |
| test.py:593:7:593:16 | IterableElement | test.py:593:7:593:16 | ControlFlowNode for Tuple [Tuple element at index 0] |
| test.py:593:7:593:16 | IterableElement | test.py:593:7:593:16 | ControlFlowNode for Tuple [Tuple element at index 1] |
| test.py:593:7:593:16 | IterableElement | test.py:593:7:593:16 | ControlFlowNode for Tuple [Tuple element at index 2] |
| test.py:593:7:593:16 | IterableSequence [List element] | test.py:593:7:593:16 | IterableElement |
| test.py:593:11:593:12 | SSA variable a2 | test.py:595:12:595:13 | ControlFlowNode for a2 |
| test.py:593:15:593:16 | SSA variable a3 | test.py:596:10:596:11 | ControlFlowNode for a3 |
| test.py:601:5:601:24 | ControlFlowNode for List [Tuple element at index 0, List element] | test.py:601:7:601:16 | IterableSequence [List element] |
| test.py:601:5:601:24 | IterableElement [List element] | test.py:601:5:601:24 | ControlFlowNode for List [Tuple element at index 0, List element] |
| test.py:601:5:601:24 | IterableSequence [List element, List element] | test.py:601:5:601:24 | IterableElement [List element] |
| test.py:601:7:601:8 | SSA variable a1 | test.py:602:10:602:11 | ControlFlowNode for a1 |
| test.py:601:7:601:16 | ControlFlowNode for Tuple [Tuple element at index 0] | test.py:601:7:601:8 | SSA variable a1 |
| test.py:601:7:601:16 | ControlFlowNode for Tuple [Tuple element at index 1] | test.py:601:11:601:12 | SSA variable a2 |
| test.py:601:7:601:16 | ControlFlowNode for Tuple [Tuple element at index 2] | test.py:601:15:601:16 | SSA variable a3 |
| test.py:601:7:601:16 | IterableElement | test.py:601:7:601:16 | ControlFlowNode for Tuple [Tuple element at index 0] |
| test.py:601:7:601:16 | IterableElement | test.py:601:7:601:16 | ControlFlowNode for Tuple [Tuple element at index 1] |
| test.py:601:7:601:16 | IterableElement | test.py:601:7:601:16 | ControlFlowNode for Tuple [Tuple element at index 2] |
| test.py:601:7:601:16 | IterableSequence [List element] | test.py:601:7:601:16 | IterableElement |
| test.py:601:11:601:12 | SSA variable a2 | test.py:603:12:603:13 | ControlFlowNode for a2 |
| test.py:601:15:601:16 | SSA variable a3 | test.py:604:10:604:11 | ControlFlowNode for a3 |
| test.py:609:6:609:17 | ControlFlowNode for List [Tuple element at index 0] | test.py:609:7:609:8 | SSA variable a1 |
| test.py:609:6:609:17 | ControlFlowNode for List [Tuple element at index 1] | test.py:609:11:609:12 | SSA variable a2 |
| test.py:609:6:609:17 | ControlFlowNode for List [Tuple element at index 2] | test.py:609:15:609:16 | SSA variable a3 |
| test.py:609:6:609:17 | IterableElement | test.py:609:6:609:17 | ControlFlowNode for List [Tuple element at index 0] |
| test.py:609:6:609:17 | IterableElement | test.py:609:6:609:17 | ControlFlowNode for List [Tuple element at index 1] |
| test.py:609:6:609:17 | IterableElement | test.py:609:6:609:17 | ControlFlowNode for List [Tuple element at index 2] |
| test.py:609:6:609:17 | IterableSequence [List element] | test.py:609:6:609:17 | IterableElement |
| test.py:609:6:609:23 | ControlFlowNode for Tuple [Tuple element at index 0, List element] | test.py:609:6:609:17 | IterableSequence [List element] |
| test.py:609:6:609:23 | IterableElement [List element] | test.py:609:6:609:23 | ControlFlowNode for Tuple [Tuple element at index 0, List element] |
| test.py:609:6:609:23 | IterableSequence [List element, List element] | test.py:609:6:609:23 | IterableElement [List element] |
| test.py:609:7:609:8 | SSA variable a1 | test.py:610:10:610:11 | ControlFlowNode for a1 |
| test.py:609:11:609:12 | SSA variable a2 | test.py:611:12:611:13 | ControlFlowNode for a2 |
| test.py:609:15:609:16 | SSA variable a3 | test.py:612:10:612:11 | ControlFlowNode for a3 |
| test.py:618:11:618:47 | ControlFlowNode for Tuple [Tuple element at index 0, Tuple element at index 0] | test.py:621:5:621:19 | ControlFlowNode for List [Tuple element at index 0, Tuple element at index 0] |
| test.py:618:11:618:47 | ControlFlowNode for Tuple [Tuple element at index 0, Tuple element at index 0] | test.py:630:6:630:18 | ControlFlowNode for Tuple [Tuple element at index 0, Tuple element at index 0] |
| test.py:618:11:618:47 | ControlFlowNode for Tuple [Tuple element at index 0, Tuple element at index 0] | test.py:639:5:639:19 | ControlFlowNode for List [Tuple element at index 0, Tuple element at index 0] |
| test.py:618:11:618:47 | ControlFlowNode for Tuple [Tuple element at index 0, Tuple element at index 0] | test.py:648:6:648:18 | ControlFlowNode for Tuple [Tuple element at index 0, Tuple element at index 0] |
| test.py:618:11:618:47 | ControlFlowNode for Tuple [Tuple element at index 0, Tuple element at index 2] | test.py:621:5:621:19 | ControlFlowNode for List [Tuple element at index 0, Tuple element at index 2] |
| test.py:618:11:618:47 | ControlFlowNode for Tuple [Tuple element at index 0, Tuple element at index 2] | test.py:630:6:630:18 | ControlFlowNode for Tuple [Tuple element at index 0, Tuple element at index 2] |
| test.py:618:11:618:47 | ControlFlowNode for Tuple [Tuple element at index 0, Tuple element at index 2] | test.py:639:5:639:19 | ControlFlowNode for List [Tuple element at index 0, Tuple element at index 2] |
| test.py:618:11:618:47 | ControlFlowNode for Tuple [Tuple element at index 0, Tuple element at index 2] | test.py:648:6:648:18 | ControlFlowNode for Tuple [Tuple element at index 0, Tuple element at index 2] |
| test.py:618:12:618:17 | ControlFlowNode for SOURCE | test.py:618:12:618:36 | ControlFlowNode for Tuple [Tuple element at index 0] |
| test.py:618:12:618:36 | ControlFlowNode for Tuple [Tuple element at index 0] | test.py:618:11:618:47 | ControlFlowNode for Tuple [Tuple element at index 0, Tuple element at index 0] |
| test.py:618:12:618:36 | ControlFlowNode for Tuple [Tuple element at index 2] | test.py:618:11:618:47 | ControlFlowNode for Tuple [Tuple element at index 0, Tuple element at index 2] |
| test.py:618:31:618:36 | ControlFlowNode for SOURCE | test.py:618:12:618:36 | ControlFlowNode for Tuple [Tuple element at index 2] |
| test.py:621:5:621:19 | ControlFlowNode for List [Tuple element at index 0, Tuple element at index 0] | test.py:621:6:621:14 | IterableSequence [Tuple element at index 0] |
| test.py:621:5:621:19 | ControlFlowNode for List [Tuple element at index 0, Tuple element at index 2] | test.py:621:6:621:14 | IterableSequence [Tuple element at index 2] |
| test.py:621:6:621:14 | ControlFlowNode for List [Tuple element at index 0] | test.py:621:7:621:8 | SSA variable a1 |
| test.py:621:6:621:14 | ControlFlowNode for List [Tuple element at index 2] | test.py:621:11:621:13 | IterableElement |
| test.py:621:6:621:14 | IterableSequence [Tuple element at index 0] | test.py:621:6:621:14 | ControlFlowNode for List [Tuple element at index 0] |
| test.py:621:6:621:14 | IterableSequence [Tuple element at index 2] | test.py:621:6:621:14 | ControlFlowNode for List [Tuple element at index 2] |
| test.py:621:7:621:8 | SSA variable a1 | test.py:622:10:622:11 | ControlFlowNode for a1 |
| test.py:621:11:621:13 | IterableElement | test.py:621:11:621:13 | SSA variable a2 [List element] |
| test.py:621:11:621:13 | SSA variable a2 [List element] | test.py:624:12:624:13 | ControlFlowNode for a2 [List element] |
| test.py:621:11:621:13 | SSA variable a2 [List element] | test.py:625:10:625:11 | ControlFlowNode for a2 [List element] |
| test.py:624:12:624:13 | ControlFlowNode for a2 [List element] | test.py:624:12:624:16 | ControlFlowNode for Subscript |
| test.py:625:10:625:11 | ControlFlowNode for a2 [List element] | test.py:625:10:625:14 | ControlFlowNode for Subscript |
| test.py:630:6:630:18 | ControlFlowNode for Tuple [Tuple element at index 0, Tuple element at index 0] | test.py:630:7:630:13 | IterableSequence [Tuple element at index 0] |
| test.py:630:6:630:18 | ControlFlowNode for Tuple [Tuple element at index 0, Tuple element at index 2] | test.py:630:7:630:13 | IterableSequence [Tuple element at index 2] |
| test.py:630:7:630:8 | SSA variable a1 | test.py:631:10:631:11 | ControlFlowNode for a1 |
| test.py:630:7:630:13 | ControlFlowNode for Tuple [Tuple element at index 0] | test.py:630:7:630:8 | SSA variable a1 |
| test.py:630:7:630:13 | ControlFlowNode for Tuple [Tuple element at index 2] | test.py:630:11:630:13 | IterableElement |
| test.py:630:7:630:13 | IterableSequence [Tuple element at index 0] | test.py:630:7:630:13 | ControlFlowNode for Tuple [Tuple element at index 0] |
| test.py:630:7:630:13 | IterableSequence [Tuple element at index 2] | test.py:630:7:630:13 | ControlFlowNode for Tuple [Tuple element at index 2] |
| test.py:630:11:630:13 | IterableElement | test.py:630:11:630:13 | SSA variable a2 [List element] |
| test.py:630:11:630:13 | SSA variable a2 [List element] | test.py:633:12:633:13 | ControlFlowNode for a2 [List element] |
| test.py:630:11:630:13 | SSA variable a2 [List element] | test.py:634:10:634:11 | ControlFlowNode for a2 [List element] |
| test.py:633:12:633:13 | ControlFlowNode for a2 [List element] | test.py:633:12:633:16 | ControlFlowNode for Subscript |
| test.py:634:10:634:11 | ControlFlowNode for a2 [List element] | test.py:634:10:634:14 | ControlFlowNode for Subscript |
| test.py:639:5:639:19 | ControlFlowNode for List [Tuple element at index 0, Tuple element at index 0] | test.py:639:7:639:13 | IterableSequence [Tuple element at index 0] |
| test.py:639:5:639:19 | ControlFlowNode for List [Tuple element at index 0, Tuple element at index 2] | test.py:639:7:639:13 | IterableSequence [Tuple element at index 2] |
| test.py:639:7:639:8 | SSA variable a1 | test.py:640:10:640:11 | ControlFlowNode for a1 |
| test.py:639:7:639:13 | ControlFlowNode for Tuple [Tuple element at index 0] | test.py:639:7:639:8 | SSA variable a1 |
| test.py:639:7:639:13 | ControlFlowNode for Tuple [Tuple element at index 2] | test.py:639:11:639:13 | IterableElement |
| test.py:639:7:639:13 | IterableSequence [Tuple element at index 0] | test.py:639:7:639:13 | ControlFlowNode for Tuple [Tuple element at index 0] |
| test.py:639:7:639:13 | IterableSequence [Tuple element at index 2] | test.py:639:7:639:13 | ControlFlowNode for Tuple [Tuple element at index 2] |
| test.py:639:11:639:13 | IterableElement | test.py:639:11:639:13 | SSA variable a2 [List element] |
| test.py:639:11:639:13 | SSA variable a2 [List element] | test.py:642:12:642:13 | ControlFlowNode for a2 [List element] |
| test.py:639:11:639:13 | SSA variable a2 [List element] | test.py:643:10:643:11 | ControlFlowNode for a2 [List element] |
| test.py:642:12:642:13 | ControlFlowNode for a2 [List element] | test.py:642:12:642:16 | ControlFlowNode for Subscript |
| test.py:643:10:643:11 | ControlFlowNode for a2 [List element] | test.py:643:10:643:14 | ControlFlowNode for Subscript |
| test.py:648:6:648:14 | ControlFlowNode for List [Tuple element at index 0] | test.py:648:7:648:8 | SSA variable a1 |
| test.py:648:6:648:14 | ControlFlowNode for List [Tuple element at index 2] | test.py:648:11:648:13 | IterableElement |
| test.py:648:6:648:14 | IterableSequence [Tuple element at index 0] | test.py:648:6:648:14 | ControlFlowNode for List [Tuple element at index 0] |
| test.py:648:6:648:14 | IterableSequence [Tuple element at index 2] | test.py:648:6:648:14 | ControlFlowNode for List [Tuple element at index 2] |
| test.py:648:6:648:18 | ControlFlowNode for Tuple [Tuple element at index 0, Tuple element at index 0] | test.py:648:6:648:14 | IterableSequence [Tuple element at index 0] |
| test.py:648:6:648:18 | ControlFlowNode for Tuple [Tuple element at index 0, Tuple element at index 2] | test.py:648:6:648:14 | IterableSequence [Tuple element at index 2] |
| test.py:648:7:648:8 | SSA variable a1 | test.py:649:10:649:11 | ControlFlowNode for a1 |
| test.py:648:11:648:13 | IterableElement | test.py:648:11:648:13 | SSA variable a2 [List element] |
| test.py:648:11:648:13 | SSA variable a2 [List element] | test.py:651:12:651:13 | ControlFlowNode for a2 [List element] |
| test.py:648:11:648:13 | SSA variable a2 [List element] | test.py:652:10:652:11 | ControlFlowNode for a2 [List element] |
| test.py:651:12:651:13 | ControlFlowNode for a2 [List element] | test.py:651:12:651:16 | ControlFlowNode for Subscript |
| test.py:652:10:652:11 | ControlFlowNode for a2 [List element] | test.py:652:10:652:14 | ControlFlowNode for Subscript |
| test.py:659:19:659:24 | ControlFlowNode for SOURCE | test.py:660:10:660:10 | ControlFlowNode for a |
| test.py:667:10:667:51 | ControlFlowNode for List [List element, Tuple element at index 0] | test.py:668:16:668:17 | ControlFlowNode for tl [List element, Tuple element at index 0] |
| test.py:667:12:667:17 | ControlFlowNode for SOURCE | test.py:667:12:667:28 | ControlFlowNode for Tuple [Tuple element at index 0] |
| test.py:667:12:667:28 | ControlFlowNode for Tuple [Tuple element at index 0] | test.py:667:10:667:51 | ControlFlowNode for List [List element, Tuple element at index 0] |
| test.py:667:33:667:38 | ControlFlowNode for SOURCE | test.py:667:33:667:49 | ControlFlowNode for Tuple [Tuple element at index 0] |
| test.py:667:33:667:49 | ControlFlowNode for Tuple [Tuple element at index 0] | test.py:667:10:667:51 | ControlFlowNode for List [List element, Tuple element at index 0] |
| test.py:668:9:668:9 | SSA variable x | test.py:669:14:669:14 | ControlFlowNode for x |
| test.py:668:9:668:11 | ControlFlowNode for Tuple [Tuple element at index 0] | test.py:668:9:668:9 | SSA variable x |
| test.py:668:9:668:11 | IterableSequence [Tuple element at index 0] | test.py:668:9:668:11 | ControlFlowNode for Tuple [Tuple element at index 0] |
| test.py:668:16:668:17 | ControlFlowNode for tl [List element, Tuple element at index 0] | test.py:668:9:668:11 | IterableSequence [Tuple element at index 0] |
| test.py:675:10:675:51 | ControlFlowNode for List [List element, Tuple element at index 0] | test.py:676:17:676:18 | ControlFlowNode for tl [List element, Tuple element at index 0] |
| test.py:675:12:675:17 | ControlFlowNode for SOURCE | test.py:675:12:675:28 | ControlFlowNode for Tuple [Tuple element at index 0] |
| test.py:675:12:675:28 | ControlFlowNode for Tuple [Tuple element at index 0] | test.py:675:10:675:51 | ControlFlowNode for List [List element, Tuple element at index 0] |
| test.py:675:33:675:38 | ControlFlowNode for SOURCE | test.py:675:33:675:49 | ControlFlowNode for Tuple [Tuple element at index 0] |
| test.py:675:33:675:49 | ControlFlowNode for Tuple [Tuple element at index 0] | test.py:675:10:675:51 | ControlFlowNode for List [List element, Tuple element at index 0] |
| test.py:676:9:676:10 | IterableElement | test.py:676:9:676:10 | SSA variable x [List element] |
| test.py:676:9:676:10 | SSA variable x [List element] | test.py:678:14:678:14 | ControlFlowNode for x [List element] |
| test.py:676:9:676:12 | ControlFlowNode for Tuple [Tuple element at index 0] | test.py:676:9:676:10 | IterableElement |
| test.py:676:9:676:12 | ControlFlowNode for Tuple [Tuple element at index 0] | test.py:676:12:676:12 | SSA variable y |
| test.py:676:9:676:12 | IterableSequence [Tuple element at index 0] | test.py:676:9:676:12 | ControlFlowNode for Tuple [Tuple element at index 0] |
| test.py:676:12:676:12 | SSA variable y | test.py:679:16:679:16 | ControlFlowNode for y |
| test.py:676:17:676:18 | ControlFlowNode for tl [List element, Tuple element at index 0] | test.py:676:9:676:12 | IterableSequence [Tuple element at index 0] |
| test.py:678:14:678:14 | ControlFlowNode for x [List element] | test.py:678:14:678:17 | ControlFlowNode for Subscript |
| test.py:684:10:684:51 | ControlFlowNode for List [List element, Tuple element at index 0] | test.py:685:19:685:20 | ControlFlowNode for tl [List element, Tuple element at index 0] |
| test.py:684:12:684:17 | ControlFlowNode for SOURCE | test.py:684:12:684:28 | ControlFlowNode for Tuple [Tuple element at index 0] |
| test.py:684:12:684:28 | ControlFlowNode for Tuple [Tuple element at index 0] | test.py:684:10:684:51 | ControlFlowNode for List [List element, Tuple element at index 0] |
| test.py:684:33:684:38 | ControlFlowNode for SOURCE | test.py:684:33:684:49 | ControlFlowNode for Tuple [Tuple element at index 0] |
| test.py:684:33:684:49 | ControlFlowNode for Tuple [Tuple element at index 0] | test.py:684:10:684:51 | ControlFlowNode for List [List element, Tuple element at index 0] |
| test.py:685:9:685:9 | SSA variable x | test.py:686:14:686:14 | ControlFlowNode for x |
| test.py:685:9:685:14 | ControlFlowNode for Tuple [Tuple element at index 0] | test.py:685:9:685:9 | SSA variable x |
| test.py:685:9:685:14 | IterableSequence [Tuple element at index 0] | test.py:685:9:685:14 | ControlFlowNode for Tuple [Tuple element at index 0] |
| test.py:685:19:685:20 | ControlFlowNode for tl [List element, Tuple element at index 0] | test.py:685:9:685:14 | IterableSequence [Tuple element at index 0] |
| test.py:690:39:690:42 | ControlFlowNode for args [Tuple element at index 0] | test.py:691:14:691:17 | ControlFlowNode for args [Tuple element at index 0] |
| test.py:690:39:690:42 | ControlFlowNode for args [Tuple element at index 1] | test.py:691:14:691:17 | ControlFlowNode for args [Tuple element at index 1] |
| test.py:691:7:691:9 | SSA variable arg | test.py:692:10:692:12 | ControlFlowNode for arg |
| test.py:691:14:691:17 | ControlFlowNode for args [Tuple element at index 0] | test.py:691:7:691:9 | SSA variable arg |
| test.py:691:14:691:17 | ControlFlowNode for args [Tuple element at index 1] | test.py:691:7:691:9 | SSA variable arg |
| test.py:697:7:697:12 | ControlFlowNode for SOURCE | test.py:698:51:698:51 | ControlFlowNode for s |
| test.py:698:3:698:52 | PosOverflowNode for iterate_star_args() [Tuple element at index 0] | test.py:690:39:690:42 | ControlFlowNode for args [Tuple element at index 0] |
| test.py:698:3:698:52 | PosOverflowNode for iterate_star_args() [Tuple element at index 1] | test.py:690:39:690:42 | ControlFlowNode for args [Tuple element at index 1] |
| test.py:698:43:698:48 | ControlFlowNode for SOURCE | test.py:698:3:698:52 | PosOverflowNode for iterate_star_args() [Tuple element at index 0] |
| test.py:698:51:698:51 | ControlFlowNode for s | test.py:698:3:698:52 | PosOverflowNode for iterate_star_args() [Tuple element at index 1] |
| test.py:769:16:769:21 | ControlFlowNode for SOURCE | test.py:772:10:772:36 | ControlFlowNode for return_from_inner_scope() |
| test.py:807:35:807:35 | ControlFlowNode for x | test.py:808:10:808:10 | ControlFlowNode for x |
| test.py:807:37:807:42 | ControlFlowNode for SOURCE | test.py:807:35:807:35 | ControlFlowNode for x |
| test.py:807:48:807:48 | ControlFlowNode for y | test.py:809:10:809:10 | ControlFlowNode for y |
| test.py:807:50:807:55 | ControlFlowNode for SOURCE | test.py:807:48:807:48 | ControlFlowNode for y |
| test.py:807:61:807:61 | ControlFlowNode for z | test.py:810:10:810:10 | ControlFlowNode for z |
| test.py:807:63:807:68 | ControlFlowNode for SOURCE | test.py:807:61:807:61 | ControlFlowNode for z |
nodes
| datamodel.py:35:7:35:7 | ControlFlowNode for a | semmle.label | ControlFlowNode for a |
| datamodel.py:36:10:36:10 | ControlFlowNode for a | semmle.label | ControlFlowNode for a |
| datamodel.py:38:6:38:17 | ControlFlowNode for f() | semmle.label | ControlFlowNode for f() |
| datamodel.py:38:8:38:13 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
| datamodel.py:44:22:44:22 | ControlFlowNode for x | semmle.label | ControlFlowNode for x |
| datamodel.py:46:16:46:16 | ControlFlowNode for x | semmle.label | ControlFlowNode for x |
| datamodel.py:49:26:49:26 | ControlFlowNode for x | semmle.label | ControlFlowNode for x |
| datamodel.py:50:16:50:16 | ControlFlowNode for x | semmle.label | ControlFlowNode for x |
| datamodel.py:71:6:71:24 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
| datamodel.py:71:15:71:20 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
| datamodel.py:72:6:72:27 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
| datamodel.py:72:18:72:23 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
| datamodel.py:80:6:80:26 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
| datamodel.py:80:20:80:25 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
| datamodel.py:81:6:81:26 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
| datamodel.py:81:20:81:25 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
| datamodel.py:152:5:152:8 | [post store] ControlFlowNode for self [Attribute b] | semmle.label | [post store] ControlFlowNode for self [Attribute b] |
| datamodel.py:152:14:152:19 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
| datamodel.py:155:14:155:25 | ControlFlowNode for Customized() [Attribute b] | semmle.label | ControlFlowNode for Customized() [Attribute b] |
| datamodel.py:159:6:159:15 | ControlFlowNode for customized [Attribute b] | semmle.label | ControlFlowNode for customized [Attribute b] |
| datamodel.py:159:6:159:17 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
| test.py:42:10:42:26 | ControlFlowNode for Tuple [Tuple element at index 1] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 1] |
| test.py:42:21:42:26 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
| test.py:43:9:43:9 | ControlFlowNode for x [Tuple element at index 1] | semmle.label | ControlFlowNode for x [Tuple element at index 1] |
| test.py:43:9:43:12 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript |
| test.py:44:10:44:10 | ControlFlowNode for y | semmle.label | ControlFlowNode for y |
| test.py:55:9:55:14 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
| test.py:56:10:56:10 | ControlFlowNode for x | semmle.label | ControlFlowNode for x |
| test.py:61:9:61:16 | ControlFlowNode for Str | semmle.label | ControlFlowNode for Str |
| test.py:62:10:62:10 | ControlFlowNode for x | semmle.label | ControlFlowNode for x |
| test.py:66:9:66:17 | ControlFlowNode for Str | semmle.label | ControlFlowNode for Str |
| test.py:67:10:67:10 | ControlFlowNode for x | semmle.label | ControlFlowNode for x |
| test.py:71:9:71:10 | ControlFlowNode for IntegerLiteral | semmle.label | ControlFlowNode for IntegerLiteral |
| test.py:72:10:72:10 | ControlFlowNode for x | semmle.label | ControlFlowNode for x |
| test.py:76:9:76:12 | ControlFlowNode for FloatLiteral | semmle.label | ControlFlowNode for FloatLiteral |
| test.py:77:10:77:10 | ControlFlowNode for x | semmle.label | ControlFlowNode for x |
| test.py:87:10:87:15 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
| test.py:88:10:88:10 | ControlFlowNode for x | semmle.label | ControlFlowNode for x |
| test.py:93:9:93:16 | ControlFlowNode for List [List element] | semmle.label | ControlFlowNode for List [List element] |
| test.py:93:10:93:15 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
| test.py:94:10:94:10 | ControlFlowNode for x [List element] | semmle.label | ControlFlowNode for x [List element] |
| test.py:94:10:94:13 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript |
| test.py:103:9:103:37 | ControlFlowNode for ListComp [List element] | semmle.label | ControlFlowNode for ListComp [List element] |
| test.py:103:10:103:15 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
| test.py:104:10:104:10 | ControlFlowNode for x [List element] | semmle.label | ControlFlowNode for x [List element] |
| test.py:104:10:104:13 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript |
| test.py:108:9:108:29 | ControlFlowNode for ListComp [List element] | semmle.label | ControlFlowNode for ListComp [List element] |
| test.py:108:10:108:10 | ControlFlowNode for y | semmle.label | ControlFlowNode for y |
| test.py:108:16:108:16 | SSA variable y | semmle.label | SSA variable y |
| test.py:108:21:108:28 | ControlFlowNode for List [List element] | semmle.label | ControlFlowNode for List [List element] |
| test.py:108:22:108:27 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
| test.py:109:10:109:10 | ControlFlowNode for x [List element] | semmle.label | ControlFlowNode for x [List element] |
| test.py:109:10:109:13 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript |
| test.py:113:9:113:16 | ControlFlowNode for List [List element] | semmle.label | ControlFlowNode for List [List element] |
| test.py:113:10:113:15 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
| test.py:114:9:114:22 | ControlFlowNode for ListComp [List element] | semmle.label | ControlFlowNode for ListComp [List element] |
| test.py:114:10:114:10 | ControlFlowNode for y | semmle.label | ControlFlowNode for y |
| test.py:114:16:114:16 | SSA variable y | semmle.label | SSA variable y |
| test.py:114:21:114:21 | ControlFlowNode for l [List element] | semmle.label | ControlFlowNode for l [List element] |
| test.py:115:10:115:10 | ControlFlowNode for x [List element] | semmle.label | ControlFlowNode for x [List element] |
| test.py:115:10:115:13 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript |
| test.py:125:9:125:16 | ControlFlowNode for Set [Set element] | semmle.label | ControlFlowNode for Set [Set element] |
| test.py:125:10:125:15 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
| test.py:126:10:126:10 | ControlFlowNode for x [Set element] | semmle.label | ControlFlowNode for x [Set element] |
| test.py:126:10:126:16 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
| test.py:130:9:130:37 | ControlFlowNode for SetComp [Set element] | semmle.label | ControlFlowNode for SetComp [Set element] |
| test.py:130:10:130:15 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
| test.py:131:10:131:10 | ControlFlowNode for x [Set element] | semmle.label | ControlFlowNode for x [Set element] |
| test.py:131:10:131:16 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
| test.py:135:9:135:29 | ControlFlowNode for SetComp [Set element] | semmle.label | ControlFlowNode for SetComp [Set element] |
| test.py:135:10:135:10 | ControlFlowNode for y | semmle.label | ControlFlowNode for y |
| test.py:135:16:135:16 | SSA variable y | semmle.label | SSA variable y |
| test.py:135:21:135:28 | ControlFlowNode for List [List element] | semmle.label | ControlFlowNode for List [List element] |
| test.py:135:22:135:27 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
| test.py:136:10:136:10 | ControlFlowNode for x [Set element] | semmle.label | ControlFlowNode for x [Set element] |
| test.py:136:10:136:16 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
| test.py:140:9:140:16 | ControlFlowNode for Set [Set element] | semmle.label | ControlFlowNode for Set [Set element] |
| test.py:140:10:140:15 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
| test.py:141:9:141:22 | ControlFlowNode for SetComp [Set element] | semmle.label | ControlFlowNode for SetComp [Set element] |
| test.py:141:10:141:10 | ControlFlowNode for y | semmle.label | ControlFlowNode for y |
| test.py:141:16:141:16 | SSA variable y | semmle.label | SSA variable y |
| test.py:141:21:141:21 | ControlFlowNode for l [Set element] | semmle.label | ControlFlowNode for l [Set element] |
| test.py:142:10:142:10 | ControlFlowNode for x [Set element] | semmle.label | ControlFlowNode for x [Set element] |
| test.py:142:10:142:16 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
| test.py:152:9:152:21 | ControlFlowNode for Dict [Dictionary element at key s] | semmle.label | ControlFlowNode for Dict [Dictionary element at key s] |
| test.py:152:15:152:20 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
| test.py:153:10:153:10 | ControlFlowNode for x [Dictionary element at key s] | semmle.label | ControlFlowNode for x [Dictionary element at key s] |
| test.py:153:10:153:15 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript |
| test.py:157:9:157:21 | ControlFlowNode for Dict [Dictionary element at key s] | semmle.label | ControlFlowNode for Dict [Dictionary element at key s] |
| test.py:157:15:157:20 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
| test.py:158:10:158:10 | ControlFlowNode for x [Dictionary element at key s] | semmle.label | ControlFlowNode for x [Dictionary element at key s] |
| test.py:158:10:158:19 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
| test.py:183:9:183:42 | ControlFlowNode for ListComp [List element] | semmle.label | ControlFlowNode for ListComp [List element] |
| test.py:183:10:183:10 | ControlFlowNode for y | semmle.label | ControlFlowNode for y |
| test.py:183:16:183:16 | SSA variable z [List element] | semmle.label | SSA variable z [List element] |
| test.py:183:21:183:30 | ControlFlowNode for List [List element, List element] | semmle.label | ControlFlowNode for List [List element, List element] |
| test.py:183:22:183:29 | ControlFlowNode for List [List element] | semmle.label | ControlFlowNode for List [List element] |
| test.py:183:23:183:28 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
| test.py:183:36:183:36 | SSA variable y | semmle.label | SSA variable y |
| test.py:183:41:183:41 | ControlFlowNode for z [List element] | semmle.label | ControlFlowNode for z [List element] |
| test.py:184:10:184:10 | ControlFlowNode for x [List element] | semmle.label | ControlFlowNode for x [List element] |
| test.py:184:10:184:13 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript |
| test.py:188:9:188:68 | ControlFlowNode for ListComp [List element] | semmle.label | ControlFlowNode for ListComp [List element] |
| test.py:188:10:188:10 | ControlFlowNode for y | semmle.label | ControlFlowNode for y |
| test.py:188:16:188:16 | SSA variable v [List element, List element, List element] | semmle.label | SSA variable v [List element, List element, List element] |
| test.py:188:21:188:34 | ControlFlowNode for List [List element, List element, List element, List element] | semmle.label | ControlFlowNode for List [List element, List element, List element, List element] |
| test.py:188:22:188:33 | ControlFlowNode for List [List element, List element, List element] | semmle.label | ControlFlowNode for List [List element, List element, List element] |
| test.py:188:23:188:32 | ControlFlowNode for List [List element, List element] | semmle.label | ControlFlowNode for List [List element, List element] |
| test.py:188:24:188:31 | ControlFlowNode for List [List element] | semmle.label | ControlFlowNode for List [List element] |
| test.py:188:25:188:30 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
| test.py:188:40:188:40 | SSA variable u [List element, List element] | semmle.label | SSA variable u [List element, List element] |
| test.py:188:45:188:45 | ControlFlowNode for v [List element, List element, List element] | semmle.label | ControlFlowNode for v [List element, List element, List element] |
| test.py:188:51:188:51 | SSA variable z [List element] | semmle.label | SSA variable z [List element] |
| test.py:188:56:188:56 | ControlFlowNode for u [List element, List element] | semmle.label | ControlFlowNode for u [List element, List element] |
| test.py:188:62:188:62 | SSA variable y | semmle.label | SSA variable y |
| test.py:188:67:188:67 | ControlFlowNode for z [List element] | semmle.label | ControlFlowNode for z [List element] |
| test.py:189:10:189:10 | ControlFlowNode for x [List element] | semmle.label | ControlFlowNode for x [List element] |
| test.py:189:10:189:13 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript |
| test.py:199:9:199:42 | ControlFlowNode for ListComp [List element] | semmle.label | ControlFlowNode for ListComp [List element] |
| test.py:199:10:199:10 | ControlFlowNode for y | semmle.label | ControlFlowNode for y |
| test.py:199:16:199:16 | SSA variable y | semmle.label | SSA variable y |
| test.py:199:22:199:22 | ControlFlowNode for z | semmle.label | ControlFlowNode for z |
| test.py:199:22:199:40 | ControlFlowNode for GeneratorExp [List element] | semmle.label | ControlFlowNode for GeneratorExp [List element] |
| test.py:199:28:199:28 | SSA variable z | semmle.label | SSA variable z |
| test.py:199:33:199:40 | ControlFlowNode for List [List element] | semmle.label | ControlFlowNode for List [List element] |
| test.py:199:34:199:39 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
| test.py:200:10:200:10 | ControlFlowNode for x [List element] | semmle.label | ControlFlowNode for x [List element] |
| test.py:200:10:200:13 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript |
| test.py:205:9:205:47 | ControlFlowNode for ListComp [List element] | semmle.label | ControlFlowNode for ListComp [List element] |
| test.py:205:10:205:10 | ControlFlowNode for a | semmle.label | ControlFlowNode for a |
| test.py:205:17:205:17 | SSA variable a | semmle.label | SSA variable a |
| test.py:205:17:205:20 | ControlFlowNode for Tuple [Tuple element at index 0] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 0] |
| test.py:205:17:205:20 | IterableSequence [Tuple element at index 0] | semmle.label | IterableSequence [Tuple element at index 0] |
| test.py:205:26:205:46 | ControlFlowNode for List [List element, Tuple element at index 0] | semmle.label | ControlFlowNode for List [List element, Tuple element at index 0] |
| test.py:205:28:205:33 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
| test.py:205:28:205:44 | ControlFlowNode for Tuple [Tuple element at index 0] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 0] |
| test.py:206:10:206:10 | ControlFlowNode for x [List element] | semmle.label | ControlFlowNode for x [List element] |
| test.py:206:10:206:13 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript |
| test.py:210:9:210:51 | ControlFlowNode for ListComp [List element] | semmle.label | ControlFlowNode for ListComp [List element] |
| test.py:210:10:210:10 | ControlFlowNode for a [List element] | semmle.label | ControlFlowNode for a [List element] |
| test.py:210:10:210:13 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript |
| test.py:210:20:210:21 | IterableElement | semmle.label | IterableElement |
| test.py:210:20:210:21 | SSA variable a [List element] | semmle.label | SSA variable a [List element] |
| test.py:210:20:210:24 | ControlFlowNode for Tuple [Tuple element at index 0] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 0] |
| test.py:210:20:210:24 | IterableSequence [Tuple element at index 0] | semmle.label | IterableSequence [Tuple element at index 0] |
| test.py:210:30:210:50 | ControlFlowNode for List [List element, Tuple element at index 0] | semmle.label | ControlFlowNode for List [List element, Tuple element at index 0] |
| test.py:210:32:210:37 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
| test.py:210:32:210:48 | ControlFlowNode for Tuple [Tuple element at index 0] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 0] |
| test.py:211:10:211:10 | ControlFlowNode for x [List element] | semmle.label | ControlFlowNode for x [List element] |
| test.py:211:10:211:13 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript |
| test.py:349:10:349:21 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript |
| test.py:349:11:349:16 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
| test.py:349:11:349:17 | ControlFlowNode for Tuple [Tuple element at index 0] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 0] |
| test.py:353:10:353:17 | ControlFlowNode for List [List element] | semmle.label | ControlFlowNode for List [List element] |
| test.py:353:10:353:20 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript |
| test.py:353:11:353:16 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
| test.py:357:10:357:22 | ControlFlowNode for Dict [Dictionary element at key s] | semmle.label | ControlFlowNode for Dict [Dictionary element at key s] |
| test.py:357:10:357:27 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript |
| test.py:357:16:357:21 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
| test.py:375:15:375:15 | ControlFlowNode for b | semmle.label | ControlFlowNode for b |
| test.py:376:12:376:12 | ControlFlowNode for b | semmle.label | ControlFlowNode for b |
| test.py:380:10:380:34 | ControlFlowNode for second() | semmle.label | ControlFlowNode for second() |
| test.py:380:28:380:33 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
| test.py:388:10:388:36 | ControlFlowNode for second() | semmle.label | ControlFlowNode for second() |
| test.py:388:30:388:35 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
| test.py:396:10:396:43 | ControlFlowNode for second() | semmle.label | ControlFlowNode for second() |
| test.py:396:10:396:43 | KwUnpacked b | semmle.label | KwUnpacked b |
| test.py:396:30:396:42 | ControlFlowNode for Dict [Dictionary element at key b] | semmle.label | ControlFlowNode for Dict [Dictionary element at key b] |
| test.py:396:36:396:41 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
| test.py:399:21:399:21 | ControlFlowNode for b [Tuple element at index 0] | semmle.label | ControlFlowNode for b [Tuple element at index 0] |
| test.py:400:12:400:12 | ControlFlowNode for b [Tuple element at index 0] | semmle.label | ControlFlowNode for b [Tuple element at index 0] |
| test.py:400:12:400:15 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript |
| test.py:404:10:404:39 | ControlFlowNode for f_extra_pos() | semmle.label | ControlFlowNode for f_extra_pos() |
| test.py:404:10:404:39 | PosOverflowNode for f_extra_pos() [Tuple element at index 0] | semmle.label | PosOverflowNode for f_extra_pos() [Tuple element at index 0] |
| test.py:404:33:404:38 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
| test.py:407:26:407:26 | ControlFlowNode for b [Dictionary element at key b] | semmle.label | ControlFlowNode for b [Dictionary element at key b] |
| test.py:408:12:408:12 | ControlFlowNode for b [Dictionary element at key b] | semmle.label | ControlFlowNode for b [Dictionary element at key b] |
| test.py:408:12:408:17 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript |
| test.py:412:10:412:45 | ControlFlowNode for f_extra_keyword() | semmle.label | ControlFlowNode for f_extra_keyword() |
| test.py:412:10:412:45 | KwOverflowNode for f_extra_keyword() [Dictionary element at key b] | semmle.label | KwOverflowNode for f_extra_keyword() [Dictionary element at key b] |
| test.py:412:39:412:44 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
| test.py:429:10:429:20 | ControlFlowNode for BoolExpr | semmle.label | ControlFlowNode for BoolExpr |
| test.py:429:15:429:20 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
| test.py:434:10:434:21 | ControlFlowNode for BoolExpr | semmle.label | ControlFlowNode for BoolExpr |
| test.py:434:16:434:21 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
| test.py:445:10:445:15 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
| test.py:445:10:445:38 | ControlFlowNode for IfExp | semmle.label | ControlFlowNode for IfExp |
| test.py:453:10:453:39 | ControlFlowNode for IfExp | semmle.label | ControlFlowNode for IfExp |
| test.py:453:34:453:39 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
| test.py:474:11:474:11 | ControlFlowNode for x | semmle.label | ControlFlowNode for x |
| test.py:475:16:475:16 | ControlFlowNode for x | semmle.label | ControlFlowNode for x |
| test.py:477:10:477:18 | ControlFlowNode for f() | semmle.label | ControlFlowNode for f() |
| test.py:477:12:477:17 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
| test.py:481:19:481:19 | ControlFlowNode for b | semmle.label | ControlFlowNode for b |
| test.py:482:16:482:16 | ControlFlowNode for b | semmle.label | ControlFlowNode for b |
| test.py:484:10:484:34 | ControlFlowNode for second() | semmle.label | ControlFlowNode for second() |
| test.py:484:28:484:33 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
| test.py:495:19:495:19 | ControlFlowNode for b | semmle.label | ControlFlowNode for b |
| test.py:496:16:496:16 | ControlFlowNode for b | semmle.label | ControlFlowNode for b |
| test.py:498:10:498:36 | ControlFlowNode for second() | semmle.label | ControlFlowNode for second() |
| test.py:498:30:498:35 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
| test.py:509:19:509:19 | ControlFlowNode for b | semmle.label | ControlFlowNode for b |
| test.py:510:16:510:16 | ControlFlowNode for b | semmle.label | ControlFlowNode for b |
| test.py:512:10:512:43 | ControlFlowNode for second() | semmle.label | ControlFlowNode for second() |
| test.py:512:10:512:43 | KwUnpacked b | semmle.label | KwUnpacked b |
| test.py:512:30:512:42 | ControlFlowNode for Dict [Dictionary element at key b] | semmle.label | ControlFlowNode for Dict [Dictionary element at key b] |
| test.py:512:36:512:41 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
| test.py:516:30:516:30 | ControlFlowNode for b [Tuple element at index 0] | semmle.label | ControlFlowNode for b [Tuple element at index 0] |
| test.py:516:33:516:33 | ControlFlowNode for b [Tuple element at index 0] | semmle.label | ControlFlowNode for b [Tuple element at index 0] |
| test.py:516:33:516:36 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript |
| test.py:517:10:517:39 | ControlFlowNode for f_extra_pos() | semmle.label | ControlFlowNode for f_extra_pos() |
| test.py:517:10:517:39 | PosOverflowNode for f_extra_pos() [Tuple element at index 0] | semmle.label | PosOverflowNode for f_extra_pos() [Tuple element at index 0] |
| test.py:517:33:517:38 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
| test.py:521:35:521:35 | ControlFlowNode for b [Dictionary element at key b] | semmle.label | ControlFlowNode for b [Dictionary element at key b] |
| test.py:521:38:521:38 | ControlFlowNode for b [Dictionary element at key b] | semmle.label | ControlFlowNode for b [Dictionary element at key b] |
| test.py:521:38:521:43 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript |
| test.py:522:10:522:45 | ControlFlowNode for f_extra_keyword() | semmle.label | ControlFlowNode for f_extra_keyword() |
| test.py:522:10:522:45 | KwOverflowNode for f_extra_keyword() [Dictionary element at key b] | semmle.label | KwOverflowNode for f_extra_keyword() [Dictionary element at key b] |
| test.py:522:39:522:44 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
| test.py:534:9:534:14 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
| test.py:536:10:536:10 | ControlFlowNode for a | semmle.label | ControlFlowNode for a |
| test.py:541:10:541:10 | ControlFlowNode for b | semmle.label | ControlFlowNode for b |
| test.py:546:10:546:15 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
| test.py:546:10:546:26 | ControlFlowNode for Tuple [Tuple element at index 0] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 0] |
| test.py:547:5:547:5 | SSA variable a | semmle.label | SSA variable a |
| test.py:547:5:547:8 | ControlFlowNode for Tuple [Tuple element at index 0] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 0] |
| test.py:548:10:548:10 | ControlFlowNode for a | semmle.label | ControlFlowNode for a |
| test.py:554:10:554:15 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
| test.py:554:10:554:36 | ControlFlowNode for Tuple [Tuple element at index 0] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 0] |
| test.py:554:10:554:36 | ControlFlowNode for Tuple [Tuple element at index 1, Tuple element at index 1] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 1, Tuple element at index 1] |
| test.py:554:19:554:35 | ControlFlowNode for Tuple [Tuple element at index 1] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 1] |
| test.py:554:30:554:35 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
| test.py:555:5:555:5 | SSA variable a | semmle.label | SSA variable a |
| test.py:555:5:555:13 | ControlFlowNode for Tuple [Tuple element at index 0] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 0] |
| test.py:555:5:555:13 | ControlFlowNode for Tuple [Tuple element at index 1, Tuple element at index 1] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 1, Tuple element at index 1] |
| test.py:555:9:555:12 | ControlFlowNode for Tuple [Tuple element at index 1] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 1] |
| test.py:555:9:555:12 | IterableSequence [Tuple element at index 1] | semmle.label | IterableSequence [Tuple element at index 1] |
| test.py:555:12:555:12 | SSA variable c | semmle.label | SSA variable c |
| test.py:556:10:556:10 | ControlFlowNode for a | semmle.label | ControlFlowNode for a |
| test.py:558:10:558:10 | ControlFlowNode for c | semmle.label | ControlFlowNode for c |
| test.py:563:9:563:33 | ControlFlowNode for List [List element, List element, List element, List element] | semmle.label | ControlFlowNode for List [List element, List element, List element, List element] |
| test.py:563:10:563:21 | ControlFlowNode for List [List element, List element, List element] | semmle.label | ControlFlowNode for List [List element, List element, List element] |
| test.py:563:11:563:20 | ControlFlowNode for List [List element, List element] | semmle.label | ControlFlowNode for List [List element, List element] |
| test.py:563:12:563:19 | ControlFlowNode for List [List element] | semmle.label | ControlFlowNode for List [List element] |
| test.py:563:13:563:18 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
| test.py:564:5:564:11 | ControlFlowNode for List [Tuple element at index 0, List element, List element] | semmle.label | ControlFlowNode for List [Tuple element at index 0, List element, List element] |
| test.py:564:5:564:11 | IterableElement [List element, List element] | semmle.label | IterableElement [List element, List element] |
| test.py:564:5:564:11 | IterableSequence [List element, List element, List element] | semmle.label | IterableSequence [List element, List element, List element] |
| test.py:564:5:564:14 | ControlFlowNode for Tuple [Tuple element at index 0, List element, List element, List element] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 0, List element, List element, List element] |
| test.py:564:5:564:14 | IterableElement [List element, List element, List element] | semmle.label | IterableElement [List element, List element, List element] |
| test.py:564:5:564:14 | IterableSequence [List element, List element, List element, List element] | semmle.label | IterableSequence [List element, List element, List element, List element] |
| test.py:564:6:564:10 | ControlFlowNode for List [Tuple element at index 0, List element] | semmle.label | ControlFlowNode for List [Tuple element at index 0, List element] |
| test.py:564:6:564:10 | IterableElement [List element] | semmle.label | IterableElement [List element] |
| test.py:564:6:564:10 | IterableSequence [List element, List element] | semmle.label | IterableSequence [List element, List element] |
| test.py:564:7:564:9 | ControlFlowNode for List [Tuple element at index 0] | semmle.label | ControlFlowNode for List [Tuple element at index 0] |
| test.py:564:7:564:9 | IterableElement | semmle.label | IterableElement |
| test.py:564:7:564:9 | IterableSequence [List element] | semmle.label | IterableSequence [List element] |
| test.py:564:8:564:8 | SSA variable a | semmle.label | SSA variable a |
| test.py:565:10:565:10 | ControlFlowNode for a | semmle.label | ControlFlowNode for a |
| test.py:571:10:571:15 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
| test.py:571:10:571:34 | ControlFlowNode for Tuple [Tuple element at index 0] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 0] |
| test.py:571:10:571:34 | ControlFlowNode for Tuple [Tuple element at index 1] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 1] |
| test.py:571:18:571:23 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
| test.py:572:5:572:5 | SSA variable a | semmle.label | SSA variable a |
| test.py:572:5:572:12 | ControlFlowNode for Tuple [Tuple element at index 0] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 0] |
| test.py:572:5:572:12 | ControlFlowNode for Tuple [Tuple element at index 1] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 1] |
| test.py:572:8:572:9 | IterableElement | semmle.label | IterableElement |
| test.py:572:8:572:9 | SSA variable b [List element] | semmle.label | SSA variable b [List element] |
| test.py:572:12:572:12 | SSA variable c | semmle.label | SSA variable c |
| test.py:573:10:573:10 | ControlFlowNode for a | semmle.label | ControlFlowNode for a |
| test.py:575:10:575:10 | ControlFlowNode for b [List element] | semmle.label | ControlFlowNode for b [List element] |
| test.py:575:10:575:13 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript |
| test.py:576:12:576:12 | ControlFlowNode for c | semmle.label | ControlFlowNode for c |
| test.py:581:10:581:15 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
| test.py:581:10:581:23 | ControlFlowNode for Tuple [Tuple element at index 0] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 0] |
| test.py:581:10:581:23 | ControlFlowNode for Tuple [Tuple element at index 1] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 1] |
| test.py:581:18:581:23 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
| test.py:582:5:582:5 | SSA variable a | semmle.label | SSA variable a |
| test.py:582:5:582:12 | ControlFlowNode for Tuple [Tuple element at index 0] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 0] |
| test.py:582:5:582:12 | ControlFlowNode for Tuple [Tuple element at index 1] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 1] |
| test.py:582:12:582:12 | SSA variable c | semmle.label | SSA variable c |
| test.py:583:10:583:10 | ControlFlowNode for a | semmle.label | ControlFlowNode for a |
| test.py:585:10:585:10 | ControlFlowNode for c | semmle.label | ControlFlowNode for c |
| test.py:590:10:590:61 | ControlFlowNode for List [List element, List element] | semmle.label | ControlFlowNode for List [List element, List element] |
| test.py:590:11:590:37 | ControlFlowNode for List [List element] | semmle.label | ControlFlowNode for List [List element] |
| test.py:590:12:590:17 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
| test.py:590:31:590:36 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
| test.py:590:40:590:47 | ControlFlowNode for List [List element] | semmle.label | ControlFlowNode for List [List element] |
| test.py:590:41:590:46 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
| test.py:593:6:593:23 | ControlFlowNode for Tuple [Tuple element at index 0, List element] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 0, List element] |
| test.py:593:6:593:23 | IterableElement [List element] | semmle.label | IterableElement [List element] |
| test.py:593:6:593:23 | IterableSequence [List element, List element] | semmle.label | IterableSequence [List element, List element] |
| test.py:593:7:593:8 | SSA variable a1 | semmle.label | SSA variable a1 |
| test.py:593:7:593:16 | ControlFlowNode for Tuple [Tuple element at index 0] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 0] |
| test.py:593:7:593:16 | ControlFlowNode for Tuple [Tuple element at index 1] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 1] |
| test.py:593:7:593:16 | ControlFlowNode for Tuple [Tuple element at index 2] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 2] |
| test.py:593:7:593:16 | IterableElement | semmle.label | IterableElement |
| test.py:593:7:593:16 | IterableSequence [List element] | semmle.label | IterableSequence [List element] |
| test.py:593:11:593:12 | SSA variable a2 | semmle.label | SSA variable a2 |
| test.py:593:15:593:16 | SSA variable a3 | semmle.label | SSA variable a3 |
| test.py:594:10:594:11 | ControlFlowNode for a1 | semmle.label | ControlFlowNode for a1 |
| test.py:595:12:595:13 | ControlFlowNode for a2 | semmle.label | ControlFlowNode for a2 |
| test.py:596:10:596:11 | ControlFlowNode for a3 | semmle.label | ControlFlowNode for a3 |
| test.py:601:5:601:24 | ControlFlowNode for List [Tuple element at index 0, List element] | semmle.label | ControlFlowNode for List [Tuple element at index 0, List element] |
| test.py:601:5:601:24 | IterableElement [List element] | semmle.label | IterableElement [List element] |
| test.py:601:5:601:24 | IterableSequence [List element, List element] | semmle.label | IterableSequence [List element, List element] |
| test.py:601:7:601:8 | SSA variable a1 | semmle.label | SSA variable a1 |
| test.py:601:7:601:16 | ControlFlowNode for Tuple [Tuple element at index 0] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 0] |
| test.py:601:7:601:16 | ControlFlowNode for Tuple [Tuple element at index 1] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 1] |
| test.py:601:7:601:16 | ControlFlowNode for Tuple [Tuple element at index 2] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 2] |
| test.py:601:7:601:16 | IterableElement | semmle.label | IterableElement |
| test.py:601:7:601:16 | IterableSequence [List element] | semmle.label | IterableSequence [List element] |
| test.py:601:11:601:12 | SSA variable a2 | semmle.label | SSA variable a2 |
| test.py:601:15:601:16 | SSA variable a3 | semmle.label | SSA variable a3 |
| test.py:602:10:602:11 | ControlFlowNode for a1 | semmle.label | ControlFlowNode for a1 |
| test.py:603:12:603:13 | ControlFlowNode for a2 | semmle.label | ControlFlowNode for a2 |
| test.py:604:10:604:11 | ControlFlowNode for a3 | semmle.label | ControlFlowNode for a3 |
| test.py:609:6:609:17 | ControlFlowNode for List [Tuple element at index 0] | semmle.label | ControlFlowNode for List [Tuple element at index 0] |
| test.py:609:6:609:17 | ControlFlowNode for List [Tuple element at index 1] | semmle.label | ControlFlowNode for List [Tuple element at index 1] |
| test.py:609:6:609:17 | ControlFlowNode for List [Tuple element at index 2] | semmle.label | ControlFlowNode for List [Tuple element at index 2] |
| test.py:609:6:609:17 | IterableElement | semmle.label | IterableElement |
| test.py:609:6:609:17 | IterableSequence [List element] | semmle.label | IterableSequence [List element] |
| test.py:609:6:609:23 | ControlFlowNode for Tuple [Tuple element at index 0, List element] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 0, List element] |
| test.py:609:6:609:23 | IterableElement [List element] | semmle.label | IterableElement [List element] |
| test.py:609:6:609:23 | IterableSequence [List element, List element] | semmle.label | IterableSequence [List element, List element] |
| test.py:609:7:609:8 | SSA variable a1 | semmle.label | SSA variable a1 |
| test.py:609:11:609:12 | SSA variable a2 | semmle.label | SSA variable a2 |
| test.py:609:15:609:16 | SSA variable a3 | semmle.label | SSA variable a3 |
| test.py:610:10:610:11 | ControlFlowNode for a1 | semmle.label | ControlFlowNode for a1 |
| test.py:611:12:611:13 | ControlFlowNode for a2 | semmle.label | ControlFlowNode for a2 |
| test.py:612:10:612:11 | ControlFlowNode for a3 | semmle.label | ControlFlowNode for a3 |
| test.py:618:11:618:47 | ControlFlowNode for Tuple [Tuple element at index 0, Tuple element at index 0] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 0, Tuple element at index 0] |
| test.py:618:11:618:47 | ControlFlowNode for Tuple [Tuple element at index 0, Tuple element at index 2] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 0, Tuple element at index 2] |
| test.py:618:12:618:17 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
| test.py:618:12:618:36 | ControlFlowNode for Tuple [Tuple element at index 0] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 0] |
| test.py:618:12:618:36 | ControlFlowNode for Tuple [Tuple element at index 2] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 2] |
| test.py:618:31:618:36 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
| test.py:621:5:621:19 | ControlFlowNode for List [Tuple element at index 0, Tuple element at index 0] | semmle.label | ControlFlowNode for List [Tuple element at index 0, Tuple element at index 0] |
| test.py:621:5:621:19 | ControlFlowNode for List [Tuple element at index 0, Tuple element at index 2] | semmle.label | ControlFlowNode for List [Tuple element at index 0, Tuple element at index 2] |
| test.py:621:6:621:14 | ControlFlowNode for List [Tuple element at index 0] | semmle.label | ControlFlowNode for List [Tuple element at index 0] |
| test.py:621:6:621:14 | ControlFlowNode for List [Tuple element at index 2] | semmle.label | ControlFlowNode for List [Tuple element at index 2] |
| test.py:621:6:621:14 | IterableSequence [Tuple element at index 0] | semmle.label | IterableSequence [Tuple element at index 0] |
| test.py:621:6:621:14 | IterableSequence [Tuple element at index 2] | semmle.label | IterableSequence [Tuple element at index 2] |
| test.py:621:7:621:8 | SSA variable a1 | semmle.label | SSA variable a1 |
| test.py:621:11:621:13 | IterableElement | semmle.label | IterableElement |
| test.py:621:11:621:13 | SSA variable a2 [List element] | semmle.label | SSA variable a2 [List element] |
| test.py:622:10:622:11 | ControlFlowNode for a1 | semmle.label | ControlFlowNode for a1 |
| test.py:624:12:624:13 | ControlFlowNode for a2 [List element] | semmle.label | ControlFlowNode for a2 [List element] |
| test.py:624:12:624:16 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript |
| test.py:625:10:625:11 | ControlFlowNode for a2 [List element] | semmle.label | ControlFlowNode for a2 [List element] |
| test.py:625:10:625:14 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript |
| test.py:630:6:630:18 | ControlFlowNode for Tuple [Tuple element at index 0, Tuple element at index 0] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 0, Tuple element at index 0] |
| test.py:630:6:630:18 | ControlFlowNode for Tuple [Tuple element at index 0, Tuple element at index 2] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 0, Tuple element at index 2] |
| test.py:630:7:630:8 | SSA variable a1 | semmle.label | SSA variable a1 |
| test.py:630:7:630:13 | ControlFlowNode for Tuple [Tuple element at index 0] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 0] |
| test.py:630:7:630:13 | ControlFlowNode for Tuple [Tuple element at index 2] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 2] |
| test.py:630:7:630:13 | IterableSequence [Tuple element at index 0] | semmle.label | IterableSequence [Tuple element at index 0] |
| test.py:630:7:630:13 | IterableSequence [Tuple element at index 2] | semmle.label | IterableSequence [Tuple element at index 2] |
| test.py:630:11:630:13 | IterableElement | semmle.label | IterableElement |
| test.py:630:11:630:13 | SSA variable a2 [List element] | semmle.label | SSA variable a2 [List element] |
| test.py:631:10:631:11 | ControlFlowNode for a1 | semmle.label | ControlFlowNode for a1 |
| test.py:633:12:633:13 | ControlFlowNode for a2 [List element] | semmle.label | ControlFlowNode for a2 [List element] |
| test.py:633:12:633:16 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript |
| test.py:634:10:634:11 | ControlFlowNode for a2 [List element] | semmle.label | ControlFlowNode for a2 [List element] |
| test.py:634:10:634:14 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript |
| test.py:639:5:639:19 | ControlFlowNode for List [Tuple element at index 0, Tuple element at index 0] | semmle.label | ControlFlowNode for List [Tuple element at index 0, Tuple element at index 0] |
| test.py:639:5:639:19 | ControlFlowNode for List [Tuple element at index 0, Tuple element at index 2] | semmle.label | ControlFlowNode for List [Tuple element at index 0, Tuple element at index 2] |
| test.py:639:7:639:8 | SSA variable a1 | semmle.label | SSA variable a1 |
| test.py:639:7:639:13 | ControlFlowNode for Tuple [Tuple element at index 0] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 0] |
| test.py:639:7:639:13 | ControlFlowNode for Tuple [Tuple element at index 2] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 2] |
| test.py:639:7:639:13 | IterableSequence [Tuple element at index 0] | semmle.label | IterableSequence [Tuple element at index 0] |
| test.py:639:7:639:13 | IterableSequence [Tuple element at index 2] | semmle.label | IterableSequence [Tuple element at index 2] |
| test.py:639:11:639:13 | IterableElement | semmle.label | IterableElement |
| test.py:639:11:639:13 | SSA variable a2 [List element] | semmle.label | SSA variable a2 [List element] |
| test.py:640:10:640:11 | ControlFlowNode for a1 | semmle.label | ControlFlowNode for a1 |
| test.py:642:12:642:13 | ControlFlowNode for a2 [List element] | semmle.label | ControlFlowNode for a2 [List element] |
| test.py:642:12:642:16 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript |
| test.py:643:10:643:11 | ControlFlowNode for a2 [List element] | semmle.label | ControlFlowNode for a2 [List element] |
| test.py:643:10:643:14 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript |
| test.py:648:6:648:14 | ControlFlowNode for List [Tuple element at index 0] | semmle.label | ControlFlowNode for List [Tuple element at index 0] |
| test.py:648:6:648:14 | ControlFlowNode for List [Tuple element at index 2] | semmle.label | ControlFlowNode for List [Tuple element at index 2] |
| test.py:648:6:648:14 | IterableSequence [Tuple element at index 0] | semmle.label | IterableSequence [Tuple element at index 0] |
| test.py:648:6:648:14 | IterableSequence [Tuple element at index 2] | semmle.label | IterableSequence [Tuple element at index 2] |
| test.py:648:6:648:18 | ControlFlowNode for Tuple [Tuple element at index 0, Tuple element at index 0] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 0, Tuple element at index 0] |
| test.py:648:6:648:18 | ControlFlowNode for Tuple [Tuple element at index 0, Tuple element at index 2] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 0, Tuple element at index 2] |
| test.py:648:7:648:8 | SSA variable a1 | semmle.label | SSA variable a1 |
| test.py:648:11:648:13 | IterableElement | semmle.label | IterableElement |
| test.py:648:11:648:13 | SSA variable a2 [List element] | semmle.label | SSA variable a2 [List element] |
| test.py:649:10:649:11 | ControlFlowNode for a1 | semmle.label | ControlFlowNode for a1 |
| test.py:651:12:651:13 | ControlFlowNode for a2 [List element] | semmle.label | ControlFlowNode for a2 [List element] |
| test.py:651:12:651:16 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript |
| test.py:652:10:652:11 | ControlFlowNode for a2 [List element] | semmle.label | ControlFlowNode for a2 [List element] |
| test.py:652:10:652:14 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript |
| test.py:659:19:659:24 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
| test.py:660:10:660:10 | ControlFlowNode for a | semmle.label | ControlFlowNode for a |
| test.py:667:10:667:51 | ControlFlowNode for List [List element, Tuple element at index 0] | semmle.label | ControlFlowNode for List [List element, Tuple element at index 0] |
| test.py:667:12:667:17 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
| test.py:667:12:667:28 | ControlFlowNode for Tuple [Tuple element at index 0] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 0] |
| test.py:667:33:667:38 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
| test.py:667:33:667:49 | ControlFlowNode for Tuple [Tuple element at index 0] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 0] |
| test.py:668:9:668:9 | SSA variable x | semmle.label | SSA variable x |
| test.py:668:9:668:11 | ControlFlowNode for Tuple [Tuple element at index 0] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 0] |
| test.py:668:9:668:11 | IterableSequence [Tuple element at index 0] | semmle.label | IterableSequence [Tuple element at index 0] |
| test.py:668:16:668:17 | ControlFlowNode for tl [List element, Tuple element at index 0] | semmle.label | ControlFlowNode for tl [List element, Tuple element at index 0] |
| test.py:669:14:669:14 | ControlFlowNode for x | semmle.label | ControlFlowNode for x |
| test.py:675:10:675:51 | ControlFlowNode for List [List element, Tuple element at index 0] | semmle.label | ControlFlowNode for List [List element, Tuple element at index 0] |
| test.py:675:12:675:17 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
| test.py:675:12:675:28 | ControlFlowNode for Tuple [Tuple element at index 0] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 0] |
| test.py:675:33:675:38 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
| test.py:675:33:675:49 | ControlFlowNode for Tuple [Tuple element at index 0] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 0] |
| test.py:676:9:676:10 | IterableElement | semmle.label | IterableElement |
| test.py:676:9:676:10 | SSA variable x [List element] | semmle.label | SSA variable x [List element] |
| test.py:676:9:676:12 | ControlFlowNode for Tuple [Tuple element at index 0] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 0] |
| test.py:676:9:676:12 | IterableSequence [Tuple element at index 0] | semmle.label | IterableSequence [Tuple element at index 0] |
| test.py:676:12:676:12 | SSA variable y | semmle.label | SSA variable y |
| test.py:676:17:676:18 | ControlFlowNode for tl [List element, Tuple element at index 0] | semmle.label | ControlFlowNode for tl [List element, Tuple element at index 0] |
| test.py:678:14:678:14 | ControlFlowNode for x [List element] | semmle.label | ControlFlowNode for x [List element] |
| test.py:678:14:678:17 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript |
| test.py:679:16:679:16 | ControlFlowNode for y | semmle.label | ControlFlowNode for y |
| test.py:684:10:684:51 | ControlFlowNode for List [List element, Tuple element at index 0] | semmle.label | ControlFlowNode for List [List element, Tuple element at index 0] |
| test.py:684:12:684:17 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
| test.py:684:12:684:28 | ControlFlowNode for Tuple [Tuple element at index 0] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 0] |
| test.py:684:33:684:38 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
| test.py:684:33:684:49 | ControlFlowNode for Tuple [Tuple element at index 0] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 0] |
| test.py:685:9:685:9 | SSA variable x | semmle.label | SSA variable x |
| test.py:685:9:685:14 | ControlFlowNode for Tuple [Tuple element at index 0] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 0] |
| test.py:685:9:685:14 | IterableSequence [Tuple element at index 0] | semmle.label | IterableSequence [Tuple element at index 0] |
| test.py:685:19:685:20 | ControlFlowNode for tl [List element, Tuple element at index 0] | semmle.label | ControlFlowNode for tl [List element, Tuple element at index 0] |
| test.py:686:14:686:14 | ControlFlowNode for x | semmle.label | ControlFlowNode for x |
| test.py:690:39:690:42 | ControlFlowNode for args [Tuple element at index 0] | semmle.label | ControlFlowNode for args [Tuple element at index 0] |
| test.py:690:39:690:42 | ControlFlowNode for args [Tuple element at index 1] | semmle.label | ControlFlowNode for args [Tuple element at index 1] |
| test.py:691:7:691:9 | SSA variable arg | semmle.label | SSA variable arg |
| test.py:691:14:691:17 | ControlFlowNode for args [Tuple element at index 0] | semmle.label | ControlFlowNode for args [Tuple element at index 0] |
| test.py:691:14:691:17 | ControlFlowNode for args [Tuple element at index 1] | semmle.label | ControlFlowNode for args [Tuple element at index 1] |
| test.py:692:10:692:12 | ControlFlowNode for arg | semmle.label | ControlFlowNode for arg |
| test.py:697:7:697:12 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
| test.py:698:3:698:52 | PosOverflowNode for iterate_star_args() [Tuple element at index 0] | semmle.label | PosOverflowNode for iterate_star_args() [Tuple element at index 0] |
| test.py:698:3:698:52 | PosOverflowNode for iterate_star_args() [Tuple element at index 1] | semmle.label | PosOverflowNode for iterate_star_args() [Tuple element at index 1] |
| test.py:698:43:698:48 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
| test.py:698:51:698:51 | ControlFlowNode for s | semmle.label | ControlFlowNode for s |
| test.py:769:16:769:21 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
| test.py:772:10:772:36 | ControlFlowNode for return_from_inner_scope() | semmle.label | ControlFlowNode for return_from_inner_scope() |
| test.py:807:35:807:35 | ControlFlowNode for x | semmle.label | ControlFlowNode for x |
| test.py:807:37:807:42 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
| test.py:807:48:807:48 | ControlFlowNode for y | semmle.label | ControlFlowNode for y |
| test.py:807:50:807:55 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
| test.py:807:61:807:61 | ControlFlowNode for z | semmle.label | ControlFlowNode for z |
| test.py:807:63:807:68 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
| test.py:808:10:808:10 | ControlFlowNode for x | semmle.label | ControlFlowNode for x |
| test.py:809:10:809:10 | ControlFlowNode for y | semmle.label | ControlFlowNode for y |
| test.py:810:10:810:10 | ControlFlowNode for z | semmle.label | ControlFlowNode for z |
subpaths
| datamodel.py:38:8:38:13 | ControlFlowNode for SOURCE | datamodel.py:35:7:35:7 | ControlFlowNode for a | datamodel.py:36:10:36:10 | ControlFlowNode for a | datamodel.py:38:6:38:17 | ControlFlowNode for f() |
| datamodel.py:71:15:71:20 | ControlFlowNode for SOURCE | datamodel.py:44:22:44:22 | ControlFlowNode for x | datamodel.py:46:16:46:16 | ControlFlowNode for x | datamodel.py:71:6:71:24 | ControlFlowNode for Attribute() |
| datamodel.py:72:18:72:23 | ControlFlowNode for SOURCE | datamodel.py:44:22:44:22 | ControlFlowNode for x | datamodel.py:46:16:46:16 | ControlFlowNode for x | datamodel.py:72:6:72:27 | ControlFlowNode for Attribute() |
| datamodel.py:80:20:80:25 | ControlFlowNode for SOURCE | datamodel.py:49:26:49:26 | ControlFlowNode for x | datamodel.py:50:16:50:16 | ControlFlowNode for x | datamodel.py:80:6:80:26 | ControlFlowNode for Attribute() |
| datamodel.py:81:20:81:25 | ControlFlowNode for SOURCE | datamodel.py:49:26:49:26 | ControlFlowNode for x | datamodel.py:50:16:50:16 | ControlFlowNode for x | datamodel.py:81:6:81:26 | ControlFlowNode for Attribute() |
| test.py:380:28:380:33 | ControlFlowNode for SOURCE | test.py:375:15:375:15 | ControlFlowNode for b | test.py:376:12:376:12 | ControlFlowNode for b | test.py:380:10:380:34 | ControlFlowNode for second() |
| test.py:388:30:388:35 | ControlFlowNode for SOURCE | test.py:375:15:375:15 | ControlFlowNode for b | test.py:376:12:376:12 | ControlFlowNode for b | test.py:388:10:388:36 | ControlFlowNode for second() |
| test.py:396:10:396:43 | KwUnpacked b | test.py:375:15:375:15 | ControlFlowNode for b | test.py:376:12:376:12 | ControlFlowNode for b | test.py:396:10:396:43 | ControlFlowNode for second() |
| test.py:404:10:404:39 | PosOverflowNode for f_extra_pos() [Tuple element at index 0] | test.py:399:21:399:21 | ControlFlowNode for b [Tuple element at index 0] | test.py:400:12:400:15 | ControlFlowNode for Subscript | test.py:404:10:404:39 | ControlFlowNode for f_extra_pos() |
| test.py:412:10:412:45 | KwOverflowNode for f_extra_keyword() [Dictionary element at key b] | test.py:407:26:407:26 | ControlFlowNode for b [Dictionary element at key b] | test.py:408:12:408:17 | ControlFlowNode for Subscript | test.py:412:10:412:45 | ControlFlowNode for f_extra_keyword() |
| test.py:477:12:477:17 | ControlFlowNode for SOURCE | test.py:474:11:474:11 | ControlFlowNode for x | test.py:475:16:475:16 | ControlFlowNode for x | test.py:477:10:477:18 | ControlFlowNode for f() |
| test.py:484:28:484:33 | ControlFlowNode for SOURCE | test.py:481:19:481:19 | ControlFlowNode for b | test.py:482:16:482:16 | ControlFlowNode for b | test.py:484:10:484:34 | ControlFlowNode for second() |
| test.py:498:30:498:35 | ControlFlowNode for SOURCE | test.py:495:19:495:19 | ControlFlowNode for b | test.py:496:16:496:16 | ControlFlowNode for b | test.py:498:10:498:36 | ControlFlowNode for second() |
| test.py:512:10:512:43 | KwUnpacked b | test.py:509:19:509:19 | ControlFlowNode for b | test.py:510:16:510:16 | ControlFlowNode for b | test.py:512:10:512:43 | ControlFlowNode for second() |
| test.py:517:10:517:39 | PosOverflowNode for f_extra_pos() [Tuple element at index 0] | test.py:516:30:516:30 | ControlFlowNode for b [Tuple element at index 0] | test.py:516:33:516:36 | ControlFlowNode for Subscript | test.py:517:10:517:39 | ControlFlowNode for f_extra_pos() |
| test.py:522:10:522:45 | KwOverflowNode for f_extra_keyword() [Dictionary element at key b] | test.py:521:35:521:35 | ControlFlowNode for b [Dictionary element at key b] | test.py:521:38:521:43 | ControlFlowNode for Subscript | test.py:522:10:522:45 | ControlFlowNode for f_extra_keyword() |
#select
| datamodel.py:38:6:38:17 | ControlFlowNode for f() | datamodel.py:38:8:38:13 | ControlFlowNode for SOURCE | datamodel.py:38:6:38:17 | ControlFlowNode for f() | Flow found |
| datamodel.py:71:6:71:24 | ControlFlowNode for Attribute() | datamodel.py:71:15:71:20 | ControlFlowNode for SOURCE | datamodel.py:71:6:71:24 | ControlFlowNode for Attribute() | Flow found |
| datamodel.py:72:6:72:27 | ControlFlowNode for Attribute() | datamodel.py:72:18:72:23 | ControlFlowNode for SOURCE | datamodel.py:72:6:72:27 | ControlFlowNode for Attribute() | Flow found |
| datamodel.py:80:6:80:26 | ControlFlowNode for Attribute() | datamodel.py:80:20:80:25 | ControlFlowNode for SOURCE | datamodel.py:80:6:80:26 | ControlFlowNode for Attribute() | Flow found |
| datamodel.py:81:6:81:26 | ControlFlowNode for Attribute() | datamodel.py:81:20:81:25 | ControlFlowNode for SOURCE | datamodel.py:81:6:81:26 | ControlFlowNode for Attribute() | Flow found |
| datamodel.py:159:6:159:17 | ControlFlowNode for Attribute | datamodel.py:152:14:152:19 | ControlFlowNode for SOURCE | datamodel.py:159:6:159:17 | ControlFlowNode for Attribute | Flow found |
| test.py:44:10:44:10 | ControlFlowNode for y | test.py:42:21:42:26 | ControlFlowNode for SOURCE | test.py:44:10:44:10 | ControlFlowNode for y | Flow found |
| test.py:56:10:56:10 | ControlFlowNode for x | test.py:55:9:55:14 | ControlFlowNode for SOURCE | test.py:56:10:56:10 | ControlFlowNode for x | Flow found |
| test.py:62:10:62:10 | ControlFlowNode for x | test.py:61:9:61:16 | ControlFlowNode for Str | test.py:62:10:62:10 | ControlFlowNode for x | Flow found |
| test.py:67:10:67:10 | ControlFlowNode for x | test.py:66:9:66:17 | ControlFlowNode for Str | test.py:67:10:67:10 | ControlFlowNode for x | Flow found |
| test.py:72:10:72:10 | ControlFlowNode for x | test.py:71:9:71:10 | ControlFlowNode for IntegerLiteral | test.py:72:10:72:10 | ControlFlowNode for x | Flow found |
| test.py:77:10:77:10 | ControlFlowNode for x | test.py:76:9:76:12 | ControlFlowNode for FloatLiteral | test.py:77:10:77:10 | ControlFlowNode for x | Flow found |
| test.py:88:10:88:10 | ControlFlowNode for x | test.py:87:10:87:15 | ControlFlowNode for SOURCE | test.py:88:10:88:10 | ControlFlowNode for x | Flow found |
| test.py:94:10:94:13 | ControlFlowNode for Subscript | test.py:93:10:93:15 | ControlFlowNode for SOURCE | test.py:94:10:94:13 | ControlFlowNode for Subscript | Flow found |
| test.py:104:10:104:13 | ControlFlowNode for Subscript | test.py:103:10:103:15 | ControlFlowNode for SOURCE | test.py:104:10:104:13 | ControlFlowNode for Subscript | Flow found |
| test.py:109:10:109:13 | ControlFlowNode for Subscript | test.py:108:22:108:27 | ControlFlowNode for SOURCE | test.py:109:10:109:13 | ControlFlowNode for Subscript | Flow found |
| test.py:115:10:115:13 | ControlFlowNode for Subscript | test.py:113:10:113:15 | ControlFlowNode for SOURCE | test.py:115:10:115:13 | ControlFlowNode for Subscript | Flow found |
| test.py:126:10:126:16 | ControlFlowNode for Attribute() | test.py:125:10:125:15 | ControlFlowNode for SOURCE | test.py:126:10:126:16 | ControlFlowNode for Attribute() | Flow found |
| test.py:131:10:131:16 | ControlFlowNode for Attribute() | test.py:130:10:130:15 | ControlFlowNode for SOURCE | test.py:131:10:131:16 | ControlFlowNode for Attribute() | Flow found |
| test.py:136:10:136:16 | ControlFlowNode for Attribute() | test.py:135:22:135:27 | ControlFlowNode for SOURCE | test.py:136:10:136:16 | ControlFlowNode for Attribute() | Flow found |
| test.py:142:10:142:16 | ControlFlowNode for Attribute() | test.py:140:10:140:15 | ControlFlowNode for SOURCE | test.py:142:10:142:16 | ControlFlowNode for Attribute() | Flow found |
| test.py:153:10:153:15 | ControlFlowNode for Subscript | test.py:152:15:152:20 | ControlFlowNode for SOURCE | test.py:153:10:153:15 | ControlFlowNode for Subscript | Flow found |
| test.py:158:10:158:19 | ControlFlowNode for Attribute() | test.py:157:15:157:20 | ControlFlowNode for SOURCE | test.py:158:10:158:19 | ControlFlowNode for Attribute() | Flow found |
| test.py:184:10:184:13 | ControlFlowNode for Subscript | test.py:183:23:183:28 | ControlFlowNode for SOURCE | test.py:184:10:184:13 | ControlFlowNode for Subscript | Flow found |
| test.py:189:10:189:13 | ControlFlowNode for Subscript | test.py:188:25:188:30 | ControlFlowNode for SOURCE | test.py:189:10:189:13 | ControlFlowNode for Subscript | Flow found |
| test.py:200:10:200:13 | ControlFlowNode for Subscript | test.py:199:34:199:39 | ControlFlowNode for SOURCE | test.py:200:10:200:13 | ControlFlowNode for Subscript | Flow found |
| test.py:206:10:206:13 | ControlFlowNode for Subscript | test.py:205:28:205:33 | ControlFlowNode for SOURCE | test.py:206:10:206:13 | ControlFlowNode for Subscript | Flow found |
| test.py:211:10:211:13 | ControlFlowNode for Subscript | test.py:210:32:210:37 | ControlFlowNode for SOURCE | test.py:211:10:211:13 | ControlFlowNode for Subscript | Flow found |
| test.py:349:10:349:21 | ControlFlowNode for Subscript | test.py:349:11:349:16 | ControlFlowNode for SOURCE | test.py:349:10:349:21 | ControlFlowNode for Subscript | Flow found |
| test.py:353:10:353:20 | ControlFlowNode for Subscript | test.py:353:11:353:16 | ControlFlowNode for SOURCE | test.py:353:10:353:20 | ControlFlowNode for Subscript | Flow found |
| test.py:357:10:357:27 | ControlFlowNode for Subscript | test.py:357:16:357:21 | ControlFlowNode for SOURCE | test.py:357:10:357:27 | ControlFlowNode for Subscript | Flow found |
| test.py:380:10:380:34 | ControlFlowNode for second() | test.py:380:28:380:33 | ControlFlowNode for SOURCE | test.py:380:10:380:34 | ControlFlowNode for second() | Flow found |
| test.py:388:10:388:36 | ControlFlowNode for second() | test.py:388:30:388:35 | ControlFlowNode for SOURCE | test.py:388:10:388:36 | ControlFlowNode for second() | Flow found |
| test.py:396:10:396:43 | ControlFlowNode for second() | test.py:396:36:396:41 | ControlFlowNode for SOURCE | test.py:396:10:396:43 | ControlFlowNode for second() | Flow found |
| test.py:404:10:404:39 | ControlFlowNode for f_extra_pos() | test.py:404:33:404:38 | ControlFlowNode for SOURCE | test.py:404:10:404:39 | ControlFlowNode for f_extra_pos() | Flow found |
| test.py:412:10:412:45 | ControlFlowNode for f_extra_keyword() | test.py:412:39:412:44 | ControlFlowNode for SOURCE | test.py:412:10:412:45 | ControlFlowNode for f_extra_keyword() | Flow found |
| test.py:429:10:429:20 | ControlFlowNode for BoolExpr | test.py:429:15:429:20 | ControlFlowNode for SOURCE | test.py:429:10:429:20 | ControlFlowNode for BoolExpr | Flow found |
| test.py:434:10:434:21 | ControlFlowNode for BoolExpr | test.py:434:16:434:21 | ControlFlowNode for SOURCE | test.py:434:10:434:21 | ControlFlowNode for BoolExpr | Flow found |
| test.py:445:10:445:38 | ControlFlowNode for IfExp | test.py:445:10:445:15 | ControlFlowNode for SOURCE | test.py:445:10:445:38 | ControlFlowNode for IfExp | Flow found |
| test.py:453:10:453:39 | ControlFlowNode for IfExp | test.py:453:34:453:39 | ControlFlowNode for SOURCE | test.py:453:10:453:39 | ControlFlowNode for IfExp | Flow found |
| test.py:477:10:477:18 | ControlFlowNode for f() | test.py:477:12:477:17 | ControlFlowNode for SOURCE | test.py:477:10:477:18 | ControlFlowNode for f() | Flow found |
| test.py:484:10:484:34 | ControlFlowNode for second() | test.py:484:28:484:33 | ControlFlowNode for SOURCE | test.py:484:10:484:34 | ControlFlowNode for second() | Flow found |
| test.py:498:10:498:36 | ControlFlowNode for second() | test.py:498:30:498:35 | ControlFlowNode for SOURCE | test.py:498:10:498:36 | ControlFlowNode for second() | Flow found |
| test.py:512:10:512:43 | ControlFlowNode for second() | test.py:512:36:512:41 | ControlFlowNode for SOURCE | test.py:512:10:512:43 | ControlFlowNode for second() | Flow found |
| test.py:517:10:517:39 | ControlFlowNode for f_extra_pos() | test.py:517:33:517:38 | ControlFlowNode for SOURCE | test.py:517:10:517:39 | ControlFlowNode for f_extra_pos() | Flow found |
| test.py:522:10:522:45 | ControlFlowNode for f_extra_keyword() | test.py:522:39:522:44 | ControlFlowNode for SOURCE | test.py:522:10:522:45 | ControlFlowNode for f_extra_keyword() | Flow found |
| test.py:536:10:536:10 | ControlFlowNode for a | test.py:534:9:534:14 | ControlFlowNode for SOURCE | test.py:536:10:536:10 | ControlFlowNode for a | Flow found |
| test.py:541:10:541:10 | ControlFlowNode for b | test.py:534:9:534:14 | ControlFlowNode for SOURCE | test.py:541:10:541:10 | ControlFlowNode for b | Flow found |
| test.py:548:10:548:10 | ControlFlowNode for a | test.py:546:10:546:15 | ControlFlowNode for SOURCE | test.py:548:10:548:10 | ControlFlowNode for a | Flow found |
| test.py:556:10:556:10 | ControlFlowNode for a | test.py:554:10:554:15 | ControlFlowNode for SOURCE | test.py:556:10:556:10 | ControlFlowNode for a | Flow found |
| test.py:558:10:558:10 | ControlFlowNode for c | test.py:554:30:554:35 | ControlFlowNode for SOURCE | test.py:558:10:558:10 | ControlFlowNode for c | Flow found |
| test.py:565:10:565:10 | ControlFlowNode for a | test.py:563:13:563:18 | ControlFlowNode for SOURCE | test.py:565:10:565:10 | ControlFlowNode for a | Flow found |
| test.py:573:10:573:10 | ControlFlowNode for a | test.py:571:10:571:15 | ControlFlowNode for SOURCE | test.py:573:10:573:10 | ControlFlowNode for a | Flow found |
| test.py:575:10:575:13 | ControlFlowNode for Subscript | test.py:571:18:571:23 | ControlFlowNode for SOURCE | test.py:575:10:575:13 | ControlFlowNode for Subscript | Flow found |
| test.py:576:12:576:12 | ControlFlowNode for c | test.py:571:18:571:23 | ControlFlowNode for SOURCE | test.py:576:12:576:12 | ControlFlowNode for c | Flow found |
| test.py:583:10:583:10 | ControlFlowNode for a | test.py:581:10:581:15 | ControlFlowNode for SOURCE | test.py:583:10:583:10 | ControlFlowNode for a | Flow found |
| test.py:585:10:585:10 | ControlFlowNode for c | test.py:581:18:581:23 | ControlFlowNode for SOURCE | test.py:585:10:585:10 | ControlFlowNode for c | Flow found |
| test.py:594:10:594:11 | ControlFlowNode for a1 | test.py:590:12:590:17 | ControlFlowNode for SOURCE | test.py:594:10:594:11 | ControlFlowNode for a1 | Flow found |
| test.py:594:10:594:11 | ControlFlowNode for a1 | test.py:590:31:590:36 | ControlFlowNode for SOURCE | test.py:594:10:594:11 | ControlFlowNode for a1 | Flow found |
| test.py:594:10:594:11 | ControlFlowNode for a1 | test.py:590:41:590:46 | ControlFlowNode for SOURCE | test.py:594:10:594:11 | ControlFlowNode for a1 | Flow found |
| test.py:595:12:595:13 | ControlFlowNode for a2 | test.py:590:12:590:17 | ControlFlowNode for SOURCE | test.py:595:12:595:13 | ControlFlowNode for a2 | Flow found |
| test.py:595:12:595:13 | ControlFlowNode for a2 | test.py:590:31:590:36 | ControlFlowNode for SOURCE | test.py:595:12:595:13 | ControlFlowNode for a2 | Flow found |
| test.py:595:12:595:13 | ControlFlowNode for a2 | test.py:590:41:590:46 | ControlFlowNode for SOURCE | test.py:595:12:595:13 | ControlFlowNode for a2 | Flow found |
| test.py:596:10:596:11 | ControlFlowNode for a3 | test.py:590:12:590:17 | ControlFlowNode for SOURCE | test.py:596:10:596:11 | ControlFlowNode for a3 | Flow found |
| test.py:596:10:596:11 | ControlFlowNode for a3 | test.py:590:31:590:36 | ControlFlowNode for SOURCE | test.py:596:10:596:11 | ControlFlowNode for a3 | Flow found |
| test.py:596:10:596:11 | ControlFlowNode for a3 | test.py:590:41:590:46 | ControlFlowNode for SOURCE | test.py:596:10:596:11 | ControlFlowNode for a3 | Flow found |
| test.py:602:10:602:11 | ControlFlowNode for a1 | test.py:590:12:590:17 | ControlFlowNode for SOURCE | test.py:602:10:602:11 | ControlFlowNode for a1 | Flow found |
| test.py:602:10:602:11 | ControlFlowNode for a1 | test.py:590:31:590:36 | ControlFlowNode for SOURCE | test.py:602:10:602:11 | ControlFlowNode for a1 | Flow found |
| test.py:602:10:602:11 | ControlFlowNode for a1 | test.py:590:41:590:46 | ControlFlowNode for SOURCE | test.py:602:10:602:11 | ControlFlowNode for a1 | Flow found |
| test.py:603:12:603:13 | ControlFlowNode for a2 | test.py:590:12:590:17 | ControlFlowNode for SOURCE | test.py:603:12:603:13 | ControlFlowNode for a2 | Flow found |
| test.py:603:12:603:13 | ControlFlowNode for a2 | test.py:590:31:590:36 | ControlFlowNode for SOURCE | test.py:603:12:603:13 | ControlFlowNode for a2 | Flow found |
| test.py:603:12:603:13 | ControlFlowNode for a2 | test.py:590:41:590:46 | ControlFlowNode for SOURCE | test.py:603:12:603:13 | ControlFlowNode for a2 | Flow found |
| test.py:604:10:604:11 | ControlFlowNode for a3 | test.py:590:12:590:17 | ControlFlowNode for SOURCE | test.py:604:10:604:11 | ControlFlowNode for a3 | Flow found |
| test.py:604:10:604:11 | ControlFlowNode for a3 | test.py:590:31:590:36 | ControlFlowNode for SOURCE | test.py:604:10:604:11 | ControlFlowNode for a3 | Flow found |
| test.py:604:10:604:11 | ControlFlowNode for a3 | test.py:590:41:590:46 | ControlFlowNode for SOURCE | test.py:604:10:604:11 | ControlFlowNode for a3 | Flow found |
| test.py:610:10:610:11 | ControlFlowNode for a1 | test.py:590:12:590:17 | ControlFlowNode for SOURCE | test.py:610:10:610:11 | ControlFlowNode for a1 | Flow found |
| test.py:610:10:610:11 | ControlFlowNode for a1 | test.py:590:31:590:36 | ControlFlowNode for SOURCE | test.py:610:10:610:11 | ControlFlowNode for a1 | Flow found |
| test.py:610:10:610:11 | ControlFlowNode for a1 | test.py:590:41:590:46 | ControlFlowNode for SOURCE | test.py:610:10:610:11 | ControlFlowNode for a1 | Flow found |
| test.py:611:12:611:13 | ControlFlowNode for a2 | test.py:590:12:590:17 | ControlFlowNode for SOURCE | test.py:611:12:611:13 | ControlFlowNode for a2 | Flow found |
| test.py:611:12:611:13 | ControlFlowNode for a2 | test.py:590:31:590:36 | ControlFlowNode for SOURCE | test.py:611:12:611:13 | ControlFlowNode for a2 | Flow found |
| test.py:611:12:611:13 | ControlFlowNode for a2 | test.py:590:41:590:46 | ControlFlowNode for SOURCE | test.py:611:12:611:13 | ControlFlowNode for a2 | Flow found |
| test.py:612:10:612:11 | ControlFlowNode for a3 | test.py:590:12:590:17 | ControlFlowNode for SOURCE | test.py:612:10:612:11 | ControlFlowNode for a3 | Flow found |
| test.py:612:10:612:11 | ControlFlowNode for a3 | test.py:590:31:590:36 | ControlFlowNode for SOURCE | test.py:612:10:612:11 | ControlFlowNode for a3 | Flow found |
| test.py:612:10:612:11 | ControlFlowNode for a3 | test.py:590:41:590:46 | ControlFlowNode for SOURCE | test.py:612:10:612:11 | ControlFlowNode for a3 | Flow found |
| test.py:622:10:622:11 | ControlFlowNode for a1 | test.py:618:12:618:17 | ControlFlowNode for SOURCE | test.py:622:10:622:11 | ControlFlowNode for a1 | Flow found |
| test.py:624:12:624:16 | ControlFlowNode for Subscript | test.py:618:31:618:36 | ControlFlowNode for SOURCE | test.py:624:12:624:16 | ControlFlowNode for Subscript | Flow found |
| test.py:625:10:625:14 | ControlFlowNode for Subscript | test.py:618:31:618:36 | ControlFlowNode for SOURCE | test.py:625:10:625:14 | ControlFlowNode for Subscript | Flow found |
| test.py:631:10:631:11 | ControlFlowNode for a1 | test.py:618:12:618:17 | ControlFlowNode for SOURCE | test.py:631:10:631:11 | ControlFlowNode for a1 | Flow found |
| test.py:633:12:633:16 | ControlFlowNode for Subscript | test.py:618:31:618:36 | ControlFlowNode for SOURCE | test.py:633:12:633:16 | ControlFlowNode for Subscript | Flow found |
| test.py:634:10:634:14 | ControlFlowNode for Subscript | test.py:618:31:618:36 | ControlFlowNode for SOURCE | test.py:634:10:634:14 | ControlFlowNode for Subscript | Flow found |
| test.py:640:10:640:11 | ControlFlowNode for a1 | test.py:618:12:618:17 | ControlFlowNode for SOURCE | test.py:640:10:640:11 | ControlFlowNode for a1 | Flow found |
| test.py:642:12:642:16 | ControlFlowNode for Subscript | test.py:618:31:618:36 | ControlFlowNode for SOURCE | test.py:642:12:642:16 | ControlFlowNode for Subscript | Flow found |
| test.py:643:10:643:14 | ControlFlowNode for Subscript | test.py:618:31:618:36 | ControlFlowNode for SOURCE | test.py:643:10:643:14 | ControlFlowNode for Subscript | Flow found |
| test.py:649:10:649:11 | ControlFlowNode for a1 | test.py:618:12:618:17 | ControlFlowNode for SOURCE | test.py:649:10:649:11 | ControlFlowNode for a1 | Flow found |
| test.py:651:12:651:16 | ControlFlowNode for Subscript | test.py:618:31:618:36 | ControlFlowNode for SOURCE | test.py:651:12:651:16 | ControlFlowNode for Subscript | Flow found |
| test.py:652:10:652:14 | ControlFlowNode for Subscript | test.py:618:31:618:36 | ControlFlowNode for SOURCE | test.py:652:10:652:14 | ControlFlowNode for Subscript | Flow found |
| test.py:660:10:660:10 | ControlFlowNode for a | test.py:659:19:659:24 | ControlFlowNode for SOURCE | test.py:660:10:660:10 | ControlFlowNode for a | Flow found |
| test.py:669:14:669:14 | ControlFlowNode for x | test.py:667:12:667:17 | ControlFlowNode for SOURCE | test.py:669:14:669:14 | ControlFlowNode for x | Flow found |
| test.py:669:14:669:14 | ControlFlowNode for x | test.py:667:33:667:38 | ControlFlowNode for SOURCE | test.py:669:14:669:14 | ControlFlowNode for x | Flow found |
| test.py:678:14:678:17 | ControlFlowNode for Subscript | test.py:675:12:675:17 | ControlFlowNode for SOURCE | test.py:678:14:678:17 | ControlFlowNode for Subscript | Flow found |
| test.py:678:14:678:17 | ControlFlowNode for Subscript | test.py:675:33:675:38 | ControlFlowNode for SOURCE | test.py:678:14:678:17 | ControlFlowNode for Subscript | Flow found |
| test.py:679:16:679:16 | ControlFlowNode for y | test.py:675:12:675:17 | ControlFlowNode for SOURCE | test.py:679:16:679:16 | ControlFlowNode for y | Flow found |
| test.py:679:16:679:16 | ControlFlowNode for y | test.py:675:33:675:38 | ControlFlowNode for SOURCE | test.py:679:16:679:16 | ControlFlowNode for y | Flow found |
| test.py:686:14:686:14 | ControlFlowNode for x | test.py:684:12:684:17 | ControlFlowNode for SOURCE | test.py:686:14:686:14 | ControlFlowNode for x | Flow found |
| test.py:686:14:686:14 | ControlFlowNode for x | test.py:684:33:684:38 | ControlFlowNode for SOURCE | test.py:686:14:686:14 | ControlFlowNode for x | Flow found |
| test.py:692:10:692:12 | ControlFlowNode for arg | test.py:697:7:697:12 | ControlFlowNode for SOURCE | test.py:692:10:692:12 | ControlFlowNode for arg | Flow found |
| test.py:692:10:692:12 | ControlFlowNode for arg | test.py:698:43:698:48 | ControlFlowNode for SOURCE | test.py:692:10:692:12 | ControlFlowNode for arg | Flow found |
| test.py:772:10:772:36 | ControlFlowNode for return_from_inner_scope() | test.py:769:16:769:21 | ControlFlowNode for SOURCE | test.py:772:10:772:36 | ControlFlowNode for return_from_inner_scope() | Flow found |
| test.py:808:10:808:10 | ControlFlowNode for x | test.py:807:37:807:42 | ControlFlowNode for SOURCE | test.py:808:10:808:10 | ControlFlowNode for x | Flow found |
| test.py:809:10:809:10 | ControlFlowNode for y | test.py:807:50:807:55 | ControlFlowNode for SOURCE | test.py:809:10:809:10 | ControlFlowNode for y | Flow found |
| test.py:810:10:810:10 | ControlFlowNode for z | test.py:807:63:807:68 | ControlFlowNode for SOURCE | test.py:810:10:810:10 | ControlFlowNode for z | Flow found |

View File

@@ -1,11 +0,0 @@
/**
* @kind path-problem
*/
import python
import experimental.dataflow.testConfig
import DataFlow::PathGraph
from TestConfiguration config, DataFlow::PathNode source, DataFlow::PathNode sink
where config.hasFlowPath(source, sink)
select sink.getNode(), source, sink, "Flow found"

View File

@@ -8,15 +8,30 @@
# Intended sources should be the variable `SOURCE` and intended sinks should be
# arguments to the function `SINK` (see python/ql/test/experimental/dataflow/testConfig.qll).
import sys
import os
import functools
sys.path.append(os.path.dirname(os.path.dirname((__file__))))
from testlib import expects
# These are defined so that we can evaluate the test code.
NONSOURCE = "not a source"
SOURCE = "source"
arg1 = "source1"
arg2 = "source2"
arg3 = "source3"
arg4 = "source4"
arg5 = "source5"
arg6 = "source6"
arg7 = "source7"
def is_source(x):
return x == "source" or x == b"source" or x == 42 or x == 42.0 or x == 42j
def SINK(x):
if is_source(x):
def SINK(x, expected=SOURCE):
if is_source(x) or x == expected:
print("OK")
else:
print("Unexpected flow", x)
@@ -27,6 +42,14 @@ def SINK_F(x):
else:
print("OK")
SINK1 = functools.partial(SINK, expected=arg1)
SINK2 = functools.partial(SINK, expected=arg2)
SINK3 = functools.partial(SINK, expected=arg3)
SINK4 = functools.partial(SINK, expected=arg4)
SINK5 = functools.partial(SINK, expected=arg5)
SINK6 = functools.partial(SINK, expected=arg6)
SINK7 = functools.partial(SINK, expected=arg7)
# Callable types
# These are the types to which the function call operation (see section Calls) can be applied:
@@ -41,17 +64,19 @@ SINK(f(SOURCE, 3)) #$ flow="SOURCE -> f(..)"
# An instance method object combines a class, a class instance and any callable object (normally a user-defined function).
class C(object):
def method(self, x, cls):
assert cls is self.__class__
return x
def method(self, x, y):
SINK1(x)
SINK2(y)
@classmethod
def classmethod(cls, x):
return x
def classmethod(cls, x, y):
SINK1(x)
SINK2(y)
@staticmethod
def staticmethod(x):
return x
def staticmethod(x, y):
SINK1(x)
SINK2(y)
def gen(self, x, count):
n = count
@@ -64,22 +89,40 @@ class C(object):
c = C()
# When an instance method object is created by retrieving a user-defined function object from a class via one of its instances, its __self__ attribute is the instance, and the method object is said to be bound. The new methods __func__ attribute is the original function object.
func_obj = c.method.__func__
@expects(6)
def test_method_call():
# When an instance method object is created by retrieving a user-defined function object from a class via one of its instances, its __self__ attribute is the instance, and the method object is said to be bound. The new methods __func__ attribute is the original function object.
func_obj = c.method.__func__
# When an instance method object is called, the underlying function (__func__) is called, inserting the class instance (__self__) in front of the argument list. For instance, when C is a class which contains a definition for a function f(), and x is an instance of C, calling x.f(1) is equivalent to calling C.f(x, 1).
SINK(c.method(SOURCE, C)) #$ flow="SOURCE -> c.method(..)"
SINK(C.method(c, SOURCE, C)) #$ flow="SOURCE -> C.method(..)"
SINK(func_obj(c, SOURCE, C)) #$ MISSING: flow="SOURCE -> func_obj(..)"
# When an instance method object is called, the underlying function (__func__) is called, inserting the class instance (__self__) in front of the argument list. For instance, when C is a class which contains a definition for a function f(), and x is an instance of C, calling x.f(1) is equivalent to calling C.f(x, 1).
c.method(arg1, arg2) # $ func=C.method arg1 arg2
C.method(c, arg1, arg2) # $ func=C.method arg1 arg2
func_obj(c, arg1, arg2) # $ MISSING: func=C.method arg1 arg2
# When an instance method object is created by retrieving a class method object from a class or instance, its __self__ attribute is the class itself, and its __func__ attribute is the function object underlying the class method.
c_func_obj = C.classmethod.__func__
@expects(6)
def test_classmethod_call():
# When an instance method object is created by retrieving a class method object from a class or instance, its __self__ attribute is the class itself, and its __func__ attribute is the function object underlying the class method.
c_func_obj = C.classmethod.__func__
# When an instance method object is derived from a class method object, the “class instance” stored in __self__ will actually be the class itself, so that calling either x.f(1) or C.f(1) is equivalent to calling f(C,1) where f is the underlying function.
c.classmethod(arg1, arg2) # $ func=C.classmethod arg1 arg2
C.classmethod(arg1, arg2) # $ func=C.classmethod arg1 arg2
c_func_obj(C, arg1, arg2) # $ MISSING: func=C.classmethod arg1 arg2
@expects(5)
def test_staticmethod_call():
# staticmethods does not have a __func__ attribute
try:
C.staticmethod.__func__
except AttributeError:
print("OK")
# When an instance method object is derived from a class method object, the “class instance” stored in __self__ will actually be the class itself, so that calling either x.f(1) or C.f(1) is equivalent to calling f(C,1) where f is the underlying function.
c.staticmethod(arg1, arg2) # $ func=C.staticmethod arg1 arg2
C.staticmethod(arg1, arg2) # $ func=C.staticmethod arg1 arg2
# When an instance method object is derived from a class method object, the “class instance” stored in __self__ will actually be the class itself, so that calling either x.f(1) or C.f(1) is equivalent to calling f(C,1) where f is the underlying function.
SINK(c.classmethod(SOURCE)) #$ flow="SOURCE -> c.classmethod(..)"
SINK(C.classmethod(SOURCE)) #$ flow="SOURCE -> C.classmethod(..)"
SINK(c_func_obj(C, SOURCE)) #$ MISSING: flow="SOURCE -> c_func_obj(..)"
# Generator functions
# A function or method which uses the yield statement (see section The yield statement) is called a generator function. Such a function, when called, always returns an iterator object which can be used to execute the body of the function: calling the iterators iterator.__next__() method will cause the function to execute until it provides a value using the yield statement. When the function executes a return statement or falls off the end, a StopIteration exception is raised and the iterator will have reached the end of the set of values to be returned.

View File

@@ -0,0 +1,24 @@
| file://:0:0:0:0 | Function generator_func | generator.py:1:20:1:21 | ControlFlowNode for xs |
| file://:0:0:0:0 | Function generator_func | generator.py:2:12:2:26 | ControlFlowNode for .0 |
| file://:0:0:0:0 | Function generator_func | generator.py:2:12:2:26 | ControlFlowNode for .0 |
| file://:0:0:0:0 | Function generator_func | generator.py:2:12:2:26 | ControlFlowNode for ListComp |
| file://:0:0:0:0 | Function generator_func | generator.py:2:13:2:13 | ControlFlowNode for Yield |
| file://:0:0:0:0 | Function generator_func | generator.py:2:13:2:13 | ControlFlowNode for x |
| file://:0:0:0:0 | Function generator_func | generator.py:2:19:2:19 | ControlFlowNode for x |
| file://:0:0:0:0 | Function generator_func | generator.py:2:24:2:25 | ControlFlowNode for xs |
| file://:0:0:0:0 | Module class_example | class_example.py:1:1:1:3 | ControlFlowNode for wat |
| file://:0:0:0:0 | Module class_example | class_example.py:1:7:1:7 | ControlFlowNode for IntegerLiteral |
| file://:0:0:0:0 | Module class_example | class_example.py:3:1:3:10 | ControlFlowNode for ClassExpr |
| file://:0:0:0:0 | Module class_example | class_example.py:3:7:3:9 | ControlFlowNode for Wat |
| file://:0:0:0:0 | Module class_example | class_example.py:4:5:4:7 | ControlFlowNode for wat |
| file://:0:0:0:0 | Module class_example | class_example.py:4:11:4:11 | ControlFlowNode for IntegerLiteral |
| file://:0:0:0:0 | Module class_example | class_example.py:5:5:5:9 | ControlFlowNode for print |
| file://:0:0:0:0 | Module class_example | class_example.py:5:5:5:26 | ControlFlowNode for print() |
| file://:0:0:0:0 | Module class_example | class_example.py:5:11:5:20 | ControlFlowNode for Str |
| file://:0:0:0:0 | Module class_example | class_example.py:5:23:5:25 | ControlFlowNode for wat |
| file://:0:0:0:0 | Module class_example | class_example.py:7:1:7:5 | ControlFlowNode for print |
| file://:0:0:0:0 | Module class_example | class_example.py:7:1:7:23 | ControlFlowNode for print() |
| file://:0:0:0:0 | Module class_example | class_example.py:7:7:7:17 | ControlFlowNode for Str |
| file://:0:0:0:0 | Module class_example | class_example.py:7:20:7:22 | ControlFlowNode for wat |
| file://:0:0:0:0 | Module generator | generator.py:1:1:1:23 | ControlFlowNode for FunctionExpr |
| file://:0:0:0:0 | Module generator | generator.py:1:5:1:18 | ControlFlowNode for generator_func |

View File

@@ -0,0 +1,6 @@
import python
import semmle.python.dataflow.new.DataFlow
from DataFlow::CfgNode node
where exists(node.getLocation().getFile().getRelativePath())
select node.getEnclosingCallable() as enclosingCallable, node

View File

@@ -0,0 +1,7 @@
wat = 1
class Wat:
wat = 2
print("in class", wat) # prints 2
print("in module", wat) # prints 1

View File

@@ -0,0 +1,2 @@
def generator_func(xs):
return [x for x in xs]

View File

@@ -19,3 +19,5 @@ reverseRead
argHasPostUpdate
postWithInFlow
viableImplInCallContextTooLarge
uniqueParameterNodeAtPosition
uniqueParameterNodePosition

View File

@@ -13,7 +13,17 @@ def is_source(x):
return x == "source" or x == b"source" or x == 42 or x == 42.0 or x == 42j
def SINK(x):
def SINK(x, *, not_present_at_runtime=False):
# not_present_at_runtime supports use-cases where we want flow from data-flow layer
# (so we want to use SINK), but we end up in a siaution where it's not possible to
# actually get flow from a source at runtime. The only use-case is for the
# cross-talk tests, where our ability to use if-then-else is limited because doing
# so would make cfg-splitting kick in, and that would solve the problem trivially
# (by the splitting).
if not_present_at_runtime:
print("OK")
return
if is_source(x):
print("OK")
else:
@@ -39,33 +49,55 @@ class MyObj(object):
self.foo = foo
def setFoo(obj, x):
SINK_F(obj.foo)
obj.foo = x
@expects(2) # $ unresolved_call=expects(..) unresolved_call=expects(..)(..)
@expects(3) # $ unresolved_call=expects(..) unresolved_call=expects(..)(..)
def test_indirect_assign():
myobj = MyObj("OK")
myobj = MyObj(NONSOURCE)
SINK_F(myobj.foo)
setFoo(myobj, SOURCE)
SINK(myobj.foo) # $ flow="SOURCE, l:-1 -> myobj.foo"
setFoo(myobj, NONSOURCE)
SINK_F(myobj.foo) # $ SPURIOUS: flow="SOURCE, l:-4 -> myobj.foo"
@expects(3) # $ unresolved_call=expects(..) unresolved_call=expects(..)(..)
def test_indirect_assign_method():
myobj = MyObj("OK")
myobj = MyObj(NONSOURCE)
SINK_F(myobj.foo)
myobj.setFoo(SOURCE)
SINK(myobj.foo) # $ flow="SOURCE, l:-1 -> myobj.foo"
myobj.setFoo(NONSOURCE)
SINK_F(myobj.foo) # $ SPURIOUS: flow="SOURCE, l:-4 -> myobj.foo"
@expects(3) # $ unresolved_call=expects(..) unresolved_call=expects(..)(..)
def test_indirect_assign_bound_method():
myobj = MyObj(NONSOURCE)
SINK_F(myobj.foo)
sf = myobj.setFoo
sf(SOURCE)
SINK(myobj.foo) # $ MISSING: flow="SOURCE, l:-1 -> myobj.foo"
sf(NONSOURCE)
SINK_F(myobj.foo)
@expects(3) # $ unresolved_call=expects(..) unresolved_call=expects(..)(..)
def test_direct_assign():
myobj = MyObj(NONSOURCE)
SINK_F(myobj.foo)
myobj.foo = SOURCE
SINK(myobj.foo) # $ flow="SOURCE, l:-1 -> myobj.foo"
def test_direct_assign_overwrite():
myobj = MyObj(NONSOURCE)
myobj.foo = SOURCE
myobj.foo = NONSOURCE
SINK_F(myobj.foo)
@@ -160,6 +192,164 @@ def test_nested_obj_method():
a.getObj().foo = x
SINK(a.obj.foo) # $ flow="SOURCE, l:-3 -> a.obj.foo"
# ------------------------------------------------------------------------------
# Field access on compound arguments
# ------------------------------------------------------------------------------
# TODO: Add support for this, see https://github.com/github/codeql/pull/10444
@expects(5) # $ unresolved_call=expects(..) unresolved_call=expects(..)(..)
def test_field_on_compound_arg(cond_true=True, cond_false=False):
class Ex:
def __init__(self):
self.attr = None
def set_attr(obj):
obj.attr = SOURCE
x = Ex()
y = Ex()
set_attr(x if cond_true else y)
SINK(x.attr) # $ MISSING: flow
x = Ex()
y = Ex()
set_attr(x if cond_false else y)
SINK(y.attr) # $ MISSING: flow
x = Ex()
y = Ex()
z = Ex()
set_attr(x if cond_false else (y if cond_true else z))
SINK_F(x.attr) # $ MISSING: flow
SINK(y.attr) # $ MISSING: flow
SINK_F(z.attr) # $ MISSING: flow
# ------------------------------------------------------------------------------
# Crosstalk test -- using different function based on conditional
# ------------------------------------------------------------------------------
# NOTE: These tests use `SINK(objy.y, not_present_at_runtime=True)` since it's not
# possible to use if-then-else statements, since that would make cfg-splitting kick in,
# and that would solve the problem trivially (by the splitting).
class CrosstalkTestX:
def __init__(self):
self.x = None
self.y = None
def setx(self, value):
self.x = value
def setvalue(self, value):
self.x = value
class CrosstalkTestY:
def __init__(self):
self.x = None
self.y = None
def sety(self ,value):
self.y = value
def setvalue(self, value):
self.y = value
@expects(8) # $ unresolved_call=expects(..) unresolved_call=expects(..)(..)
def test_no_crosstalk_reference(cond=True):
objx = CrosstalkTestX()
SINK_F(objx.x)
SINK_F(objx.y)
objy = CrosstalkTestY()
SINK_F(objy.x)
SINK_F(objy.y)
if cond:
objx.setvalue(SOURCE)
else:
objy.setvalue(SOURCE)
SINK(objx.x) # $ flow="SOURCE, l:-4 -> objx.x"
SINK_F(objx.y)
SINK_F(objy.x)
SINK(objy.y, not_present_at_runtime=True) # $ flow="SOURCE, l:-5 -> objy.y"
@expects(8) # $ unresolved_call=expects(..) unresolved_call=expects(..)(..)
def test_potential_crosstalk_different_name(cond=True):
objx = CrosstalkTestX()
SINK_F(objx.x)
SINK_F(objx.y)
objy = CrosstalkTestY()
SINK_F(objy.x)
SINK_F(objy.y)
if cond:
func = objx.setx
else:
func = objy.sety
func(SOURCE)
SINK(objx.x) # $ MISSING: flow="SOURCE, l:-2 -> objx.x"
SINK_F(objx.y)
SINK_F(objy.x)
SINK(objy.y, not_present_at_runtime=True) # $ MISSING: flow="SOURCE, l:-5 -> objy.y"
@expects(8) # $ unresolved_call=expects(..) unresolved_call=expects(..)(..)
def test_potential_crosstalk_same_name(cond=True):
objx = CrosstalkTestX()
SINK_F(objx.x)
SINK_F(objx.y)
objy = CrosstalkTestY()
SINK_F(objy.x)
SINK_F(objy.y)
if cond:
func = objx.setvalue
else:
func = objy.setvalue
func(SOURCE)
SINK(objx.x) # $ MISSING: flow="SOURCE, l:-2 -> objx.x"
SINK_F(objx.y)
SINK_F(objy.x)
SINK(objy.y, not_present_at_runtime=True) # $ MISSING: flow="SOURCE, l:-5 -> objy.y"
@expects(10) # $ unresolved_call=expects(..) unresolved_call=expects(..)(..)
def test_potential_crosstalk_same_name_object_reference(cond=True):
objx = CrosstalkTestX()
SINK_F(objx.x)
SINK_F(objx.y)
objy = CrosstalkTestY()
SINK_F(objy.x)
SINK_F(objy.y)
if cond:
obj = objx
else:
obj = objy
obj.setvalue(SOURCE)
SINK(objx.x) # $ MISSING: flow="SOURCE, l:-2 -> objx.x"
SINK_F(objx.y)
SINK_F(objy.x)
SINK(objy.y, not_present_at_runtime=True) # $ MISSING: flow="SOURCE, l:-5 -> objy.y"
SINK(obj.x) # $ flow="SOURCE, l:-7 -> obj.x"
SINK(obj.y, not_present_at_runtime=True) # $ flow="SOURCE, l:-8 -> obj.y"
# ------------------------------------------------------------------------------
# Global scope
# ------------------------------------------------------------------------------

View File

@@ -19,3 +19,5 @@ reverseRead
argHasPostUpdate
postWithInFlow
viableImplInCallContextTooLarge
uniqueParameterNodeAtPosition
uniqueParameterNodePosition

View File

@@ -19,3 +19,5 @@ reverseRead
argHasPostUpdate
postWithInFlow
viableImplInCallContextTooLarge
uniqueParameterNodeAtPosition
uniqueParameterNodePosition

View File

@@ -19,3 +19,5 @@ reverseRead
argHasPostUpdate
postWithInFlow
viableImplInCallContextTooLarge
uniqueParameterNodeAtPosition
uniqueParameterNodePosition

View File

@@ -19,3 +19,5 @@ reverseRead
argHasPostUpdate
postWithInFlow
viableImplInCallContextTooLarge
uniqueParameterNodeAtPosition
uniqueParameterNodePosition

View File

@@ -19,3 +19,5 @@ reverseRead
argHasPostUpdate
postWithInFlow
viableImplInCallContextTooLarge
uniqueParameterNodeAtPosition
uniqueParameterNodePosition

View File

@@ -19,3 +19,5 @@ reverseRead
argHasPostUpdate
postWithInFlow
viableImplInCallContextTooLarge
uniqueParameterNodeAtPosition
uniqueParameterNodePosition

View File

@@ -19,3 +19,5 @@ reverseRead
argHasPostUpdate
postWithInFlow
viableImplInCallContextTooLarge
uniqueParameterNodeAtPosition
uniqueParameterNodePosition

View File

@@ -19,3 +19,5 @@ reverseRead
argHasPostUpdate
postWithInFlow
viableImplInCallContextTooLarge
uniqueParameterNodeAtPosition
uniqueParameterNodePosition

View File

@@ -19,3 +19,5 @@ reverseRead
argHasPostUpdate
postWithInFlow
viableImplInCallContextTooLarge
uniqueParameterNodeAtPosition
uniqueParameterNodePosition

View File

@@ -19,3 +19,5 @@ reverseRead
argHasPostUpdate
postWithInFlow
viableImplInCallContextTooLarge
uniqueParameterNodeAtPosition
uniqueParameterNodePosition

View File

@@ -19,3 +19,5 @@ reverseRead
argHasPostUpdate
postWithInFlow
viableImplInCallContextTooLarge
uniqueParameterNodeAtPosition
uniqueParameterNodePosition

View File

@@ -38,9 +38,10 @@ class TestConfiguration extends DataFlow::Configuration {
}
override predicate isSink(DataFlow::Node node) {
exists(CallNode call |
call.getFunction().(NameNode).getId() in ["SINK", "SINK_F"] and
node.(DataFlow::CfgNode).getNode() = call.getAnArg()
exists(DataFlow::CallCfgNode call |
call.getFunction().asCfgNode().(NameNode).getId() in ["SINK", "SINK_F"] and
(node = call.getArg(_) or node = call.getArgByName(_)) and
not node = call.getArgByName("not_present_at_runtime")
)
}

View File

@@ -19,3 +19,5 @@ reverseRead
argHasPostUpdate
postWithInFlow
viableImplInCallContextTooLarge
uniqueParameterNodeAtPosition
uniqueParameterNodePosition

View File

@@ -6,8 +6,8 @@ test_direct_import()
def test_alias_problem():
from .alias_problem import foo # $ MISSING: tracked
print(foo) # $ MISSING: tracked
from .alias_problem import foo # $ tracked
print(foo) # $ tracked
test_alias_problem()
@@ -34,8 +34,8 @@ test_alias_only_direct()
def test_problem_absolute_import():
from pkg.problem_absolute_import import foo # $ MISSING: tracked
print(foo) # $ MISSING: tracked
from pkg.problem_absolute_import import foo # $ tracked
print(foo) # $ tracked
test_problem_absolute_import()

View File

@@ -55,6 +55,7 @@ if __name__ == "__main__":
check_tests_valid("coverage.classes")
check_tests_valid("coverage.test")
check_tests_valid("coverage.argumentPassing")
check_tests_valid("coverage.datamodel")
check_tests_valid("variable-capture.in")
check_tests_valid("variable-capture.nonlocal")
check_tests_valid("variable-capture.dict")

View File

@@ -19,3 +19,5 @@ reverseRead
argHasPostUpdate
postWithInFlow
viableImplInCallContextTooLarge
uniqueParameterNodeAtPosition
uniqueParameterNodePosition

View File

@@ -0,0 +1,6 @@
from trace import *
enter(__file__)
clashing_attr = "clashing_attr"
exit(__file__)

View File

@@ -0,0 +1,4 @@
from trace import *
enter(__file__)
exit(__file__)

View File

@@ -0,0 +1,4 @@
from trace import *
enter(__file__)
exit(__file__)

View File

@@ -0,0 +1,6 @@
from trace import *
enter(__file__)
bar_attr = "bar_attr"
exit(__file__)

View File

@@ -0,0 +1,14 @@
from trace import *
enter(__file__)
# A simple attribute. Used in main.py
foo_attr = "foo_attr"
# A private attribute. Accessible from main.py despite this.
__private_foo_attr = "__private_foo_attr"
# A reexport of bar under a new name. Used in main.py
import bar as bar_reexported #$ imports=bar as=bar_reexported
check("bar_reexported.bar_attr", bar_reexported.bar_attr, "bar_attr", globals()) #$ prints=bar_attr
exit(__file__)

View File

@@ -0,0 +1,125 @@
import python
import semmle.python.dataflow.new.DataFlow
import semmle.python.ApiGraphs
import TestUtilities.InlineExpectationsTest
import semmle.python.dataflow.new.internal.ImportResolution
/** A string that appears on the right hand side of an assignment. */
private class SourceString extends DataFlow::Node {
string contents;
SourceString() {
this.asExpr().(StrConst).getText() = contents and
this.asExpr().getParent() instanceof Assign
}
string getContents() { result = contents }
}
/** An argument that is checked using the `check` function. */
private class CheckArgument extends DataFlow::Node {
CheckArgument() { this = API::moduleImport("trace").getMember("check").getACall().getArg(1) }
}
/** A data-flow node that is a reference to a module. */
private class ModuleRef extends DataFlow::Node {
Module mod;
ModuleRef() {
this = ImportResolution::getModuleReference(mod) and
not mod.getName() in ["__future__", "trace"]
}
string getName() { result = mod.getName() }
}
/**
* A data-flow node that is guarded by a version check. Only supports checks of the form `if
*sys.version_info[0] == ...` where the right hand side is either `2` or `3`.
*/
private class VersionGuardedNode extends DataFlow::Node {
int version;
VersionGuardedNode() {
version in [2, 3] and
exists(If parent, CompareNode c | parent.getBody().contains(this.asExpr()) |
c.operands(API::moduleImport("sys")
.getMember("version_info")
.getASubscript()
.asSource()
.asCfgNode(), any(Eq eq),
any(IntegerLiteral lit | lit.getValue() = version).getAFlowNode())
)
}
int getVersion() { result = version }
}
private class ImportConfiguration extends DataFlow::Configuration {
ImportConfiguration() { this = "ImportConfiguration" }
override predicate isSource(DataFlow::Node source) { source instanceof SourceString }
override predicate isSink(DataFlow::Node sink) {
sink = API::moduleImport("trace").getMember("check").getACall().getArg(1)
}
}
class ResolutionTest extends InlineExpectationsTest {
ResolutionTest() { this = "ResolutionTest" }
override string getARelevantTag() { result = "prints" }
override predicate hasActualResult(Location location, string element, string tag, string value) {
(
exists(DataFlow::PathNode source, DataFlow::PathNode sink, ImportConfiguration config |
config.hasFlowPath(source, sink) and
not sink.getNode() instanceof VersionGuardedNode and
tag = "prints" and
location = sink.getNode().getLocation() and
value = source.getNode().(SourceString).getContents() and
element = sink.getNode().toString()
)
or
exists(ModuleRef ref |
not ref instanceof VersionGuardedNode and
ref instanceof CheckArgument and
tag = "prints" and
location = ref.getLocation() and
value = "\"<module " + ref.getName() + ">\"" and
element = ref.toString()
)
)
}
}
private string getTagForVersion(int version) {
result = "prints" + version and
version = major_version()
}
class VersionSpecificResolutionTest extends InlineExpectationsTest {
VersionSpecificResolutionTest() { this = "VersionSpecificResolutionTest" }
override string getARelevantTag() { result = getTagForVersion(_) }
override predicate hasActualResult(Location location, string element, string tag, string value) {
(
exists(DataFlow::PathNode source, DataFlow::PathNode sink, ImportConfiguration config |
config.hasFlowPath(source, sink) and
tag = getTagForVersion(sink.getNode().(VersionGuardedNode).getVersion()) and
location = sink.getNode().getLocation() and
value = source.getNode().(SourceString).getContents() and
element = sink.getNode().toString()
)
or
exists(ModuleRef ref |
ref instanceof CheckArgument and
tag = getTagForVersion(ref.(VersionGuardedNode).getVersion()) and
location = ref.getLocation() and
value = "\"<module " + ref.getName() + ">\"" and
element = ref.toString()
)
)
}
}

View File

@@ -0,0 +1,49 @@
import python
import TestUtilities.InlineExpectationsTest
import semmle.python.dataflow.new.DataFlow
import semmle.python.dataflow.new.internal.ImportResolution
private class ImmediateModuleRef extends DataFlow::Node {
Module mod;
string alias;
ImmediateModuleRef() {
this = ImportResolution::getImmediateModuleReference(mod) and
not mod.getName() in ["__future__", "trace"] and
this.asExpr() = any(Alias a | alias = a.getAsname().(Name).getId()).getAsname()
}
Module getModule() { result = mod }
string getAsname() { result = alias }
}
class ImportTest extends InlineExpectationsTest {
ImportTest() { this = "ImportTest" }
override string getARelevantTag() { result = "imports" }
override predicate hasActualResult(Location location, string element, string tag, string value) {
exists(ImmediateModuleRef ref |
tag = "imports" and
location = ref.getLocation() and
value = ref.getModule().getName() and
element = ref.toString()
)
}
}
class AliasTest extends InlineExpectationsTest {
AliasTest() { this = "AliasTest" }
override string getARelevantTag() { result = "as" }
override predicate hasActualResult(Location location, string element, string tag, string value) {
exists(ImmediateModuleRef ref |
tag = "as" and
location = ref.getLocation() and
value = ref.getAsname() and
element = ref.toString()
)
}
}

View File

@@ -0,0 +1,94 @@
#! /usr/bin/env python3
"""
A slightly complicated test setup. I wanted to both make sure I captured
the semantics of Python and also the fact that the kinds of global flow
we expect to see are indeed present.
The code is executable, and prints out both when the execution reaches
certain files, and also what values are assigned to the various
attributes that are referenced throughout the program. These values are
validated in the test as well.
My original version used introspection to avoid referencing attributes
directly (thus enabling better error diagnostics), but unfortunately
that made it so that the model couldn't follow what was going on.
The current setup is a bit clunky (and Python's scoping rules makes it
especially so -- cf. the explicit calls to `globals` and `locals`), but
I think it does the job okay.
"""
from __future__ import print_function
import sys
from trace import *
enter(__file__)
# A simple import. Binds foo to the foo module
import foo #$ imports=foo as=foo
check("foo.foo_attr", foo.foo_attr, "foo_attr", globals()) #$ prints=foo_attr
# Private attributes are still accessible.
check("foo.__private_foo_attr", foo.__private_foo_attr, "__private_foo_attr", globals()) #$ prints=__private_foo_attr
# An aliased import, binding foo to foo_alias
import foo as foo_alias #$ imports=foo as=foo_alias
check("foo_alias.foo_attr", foo_alias.foo_attr, "foo_attr", globals()) #$ prints=foo_attr
# A reference to a reexported module
check("foo.bar_reexported", foo.bar_reexported, "<module bar>", globals()) #$ prints="<module bar>"
check("foo.bar_reexported.bar_attr", foo.bar_reexported.bar_attr, "bar_attr", globals()) #$ prints=bar_attr
# A simple "import from" statement.
from bar import bar_attr
check("bar_attr", bar_attr, "bar_attr", globals()) #$ prints=bar_attr
# Importing an attribute from a subpackage of a package.
from package.subpackage import subpackage_attr
check("subpackage_attr", subpackage_attr, "subpackage_attr", globals()) #$ prints=subpackage_attr
# Importing a package attribute under an alias.
from package import package_attr as package_attr_alias
check("package_attr_alias", package_attr_alias, "package_attr", globals()) #$ prints=package_attr
# Importing a subpackage under an alias.
from package import subpackage as aliased_subpackage #$ imports=package.subpackage.__init__ as=aliased_subpackage
check("aliased_subpackage.subpackage_attr", aliased_subpackage.subpackage_attr, "subpackage_attr", globals()) #$ prints=subpackage_attr
def local_import():
# Same as above, but in a local scope.
import package.subpackage as local_subpackage #$ imports=package.subpackage.__init__ as=local_subpackage
check("local_subpackage.subpackage_attr", local_subpackage.subpackage_attr, "subpackage_attr", locals()) #$ prints=subpackage_attr
local_import()
# Importing a subpacking using `import` and binding it to a name.
import package.subpackage as aliased_subpackage #$ imports=package.subpackage.__init__ as=aliased_subpackage
check("aliased_subpackage.subpackage_attr", aliased_subpackage.subpackage_attr, "subpackage_attr", globals()) #$ prints=subpackage_attr
# Importing without binding instead binds the top level name.
import package.subpackage #$ imports=package.__init__ as=package
check("package.package_attr", package.package_attr, "package_attr", globals()) #$ prints=package_attr
# Deep imports
import package.subpackage.submodule #$ imports=package.__init__ as=package
check("package.subpackage.submodule.submodule_attr", package.subpackage.submodule.submodule_attr, "submodule_attr", globals()) #$ prints=submodule_attr
if sys.version_info[0] == 3:
# Importing from a namespace module.
from namespace_package.namespace_module import namespace_module_attr
check("namespace_module_attr", namespace_module_attr, "namespace_module_attr", globals()) #$ prints3=namespace_module_attr
from attr_clash import clashing_attr, non_clashing_submodule #$ imports=attr_clash.clashing_attr as=clashing_attr imports=attr_clash.non_clashing_submodule as=non_clashing_submodule
check("clashing_attr", clashing_attr, "clashing_attr", globals()) #$ prints=clashing_attr SPURIOUS: prints="<module attr_clash.clashing_attr>"
check("non_clashing_submodule", non_clashing_submodule, "<module attr_clash.non_clashing_submodule>", globals()) #$ prints="<module attr_clash.non_clashing_submodule>"
exit(__file__)
print()
if status() == 0:
print("PASS")
else:
print("FAIL")

View File

@@ -0,0 +1,6 @@
from trace import *
enter(__file__)
namespace_module_attr = "namespace_module_attr"
exit(__file__)

View File

@@ -0,0 +1,7 @@
from trace import *
enter(__file__)
attr_used_in_subpackage = "attr_used_in_subpackage"
package_attr = "package_attr"
exit(__file__)

View File

@@ -0,0 +1,14 @@
from trace import *
enter(__file__)
subpackage_attr = "subpackage_attr"
# Importing an attribute from the parent package.
from .. import attr_used_in_subpackage as imported_attr
check("imported_attr", imported_attr, "attr_used_in_subpackage", globals()) #$ prints=attr_used_in_subpackage
# Importing an irrelevant attribute from a sibling module binds the name to the module.
from .submodule import irrelevant_attr
check("submodule.submodule_attr", submodule.submodule_attr, "submodule_attr", globals()) #$ prints=submodule_attr
exit(__file__)

View File

@@ -0,0 +1,7 @@
from trace import *
enter(__file__)
submodule_attr = "submodule_attr"
irrelevant_attr = "irrelevant_attr"
exit(__file__)

View File

@@ -0,0 +1,52 @@
from __future__ import print_function
_indent_level = 0
_print = print
def print(*args, **kwargs):
_print(" " * _indent_level, end="")
_print(*args, **kwargs)
def enter(file_name):
global _indent_level
print("Entering {}".format(file_name))
_indent_level += 1
def exit(file_name):
global _indent_level
_indent_level -= 1
print("Leaving {}".format(file_name))
_status = 0
def status():
return _status
def check(attr_path, actual_value, expected_value, bindings):
global _status
parts = attr_path.split(".")
base, parts = parts[0], parts[1:]
if base not in bindings:
print("Error: {} not in bindings".format(base))
_status = 1
return
val = bindings[base]
for part in parts:
if not hasattr(val, part):
print("Error: Unknown attribute {}".format(part))
_status = 1
return
val = getattr(val, part)
if val != actual_value:
print("Error: Value at path {} and actual value are out of sync! {} != {}".format(attr_path, val, actual_value))
_status = 1
if str(val).startswith("<module"):
val = "<module " + val.__name__ + ">"
if val != expected_value:
print("Error: Expected {} to be {}, got {}".format(attr_path, expected_value, val))
_status = 1
return
print("OK: {} = {}".format(attr_path, val))
__all__ = ["enter", "exit", "check", "status"]

View File

@@ -0,0 +1,5 @@
failures
debug_callableNotUnique
pointsTo_found_typeTracker_notFound
| example.py:22:1:22:16 | ControlFlowNode for explicit_afunc() | explicit_afunc |
typeTracker_found_pointsTo_notFound

View File

@@ -0,0 +1 @@
../CallGraph/InlineCallGraphTest.ql

View File

@@ -1,6 +0,0 @@
debug_missingAnnotationForCallable
debug_nonUniqueAnnotationForCallable
debug_missingAnnotationForCall
expectedCallEdgeNotFound
| example.py:19:1:19:7 | afunc() | foo/bar/a.py:2:1:2:12 | Function afunc |
unexpectedCallEdgeFound

View File

@@ -1 +0,0 @@
../CallGraph/PointsTo.ql

View File

@@ -1,6 +0,0 @@
debug_missingAnnotationForCallable
debug_nonUniqueAnnotationForCallable
debug_missingAnnotationForCall
pointsTo_found_typeTracker_notFound
| example.py:22:1:22:16 | explicit_afunc() | foo_explicit/bar/a.py:2:1:2:21 | Function explicit_afunc |
pointsTo_notFound_typeTracker_found

View File

@@ -1 +0,0 @@
../CallGraph/Relative.ql

View File

@@ -1,7 +0,0 @@
debug_missingAnnotationForCallable
debug_nonUniqueAnnotationForCallable
debug_missingAnnotationForCall
expectedCallEdgeNotFound
| example.py:19:1:19:7 | afunc() | foo/bar/a.py:2:1:2:12 | Function afunc |
| example.py:22:1:22:16 | explicit_afunc() | foo_explicit/bar/a.py:2:1:2:21 | Function explicit_afunc |
unexpectedCallEdgeFound

View File

@@ -1 +0,0 @@
../CallGraph/TypeTracker.ql

View File

@@ -5,18 +5,18 @@ This is not included in the standard `CallGraph/code` folder, since we're testin
understanding import work properly, so it's better to have a clean test setup that is
obviously correct (the other one isn't in regards to imports).
Technically this is part of PEP 420 -- Implicit Namespace Packages, but does use the
Technically this is part of PEP 420 -- Implicit Namespace Packages, but does not use the
*real* namespace package feature of allowing source code for a single package to reside
in multiple places.
Maybe this should have been an import resolution test, and not a call-graph test ¯\_(ツ)_/¯
Since PEP 420 was accepted in Python 3, this test is Python 3 only.
"""
from foo.bar.a import afunc
from foo_explicit.bar.a import explicit_afunc
# calls:afunc
afunc()
afunc() # $ MISSING: pt,tt=afunc
# calls:explicit_afunc
explicit_afunc()
explicit_afunc() # $ pt=explicit_afunc MISSING: tt=explicit_afunc

View File

@@ -1,4 +1,3 @@
# name:afunc
def afunc():
print("afunc called")
return 1

View File

@@ -1,4 +1,3 @@
# name:explicit_afunc
def explicit_afunc():
print("explicit_afunc called")
return 1

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