Merge branch 'main' into amammad-python-bombs

This commit is contained in:
amammad
2023-12-07 13:50:20 +01:00
9569 changed files with 683674 additions and 332770 deletions

View File

@@ -1,46 +1,6 @@
// TODO: this should be promoted to be a REAL consistency query by being placed in
// `python/ql/consistency-queries`. For for now it resides here.
import python
import semmle.python.dataflow.new.DataFlow::DataFlow
import semmle.python.dataflow.new.internal.DataFlowPrivate
import semmle.python.dataflow.new.internal.DataFlowImplConsistency::Consistency
// TODO: this should be promoted to be a REAL consistency query by being placed in
// `python/ql/consistency-queries`. For for now it resides here.
private class MyConsistencyConfiguration extends ConsistencyConfiguration {
override predicate argHasPostUpdateExclude(ArgumentNode n) {
exists(ArgumentPosition apos | n.argumentOf(_, apos) and apos.isStarArgs(_))
or
exists(ArgumentPosition apos | n.argumentOf(_, apos) and apos.isDictSplat())
}
override predicate reverseReadExclude(Node n) {
// since `self`/`cls` parameters can be marked as implicit argument to `super()`,
// they will have PostUpdateNodes. We have a read-step from the synthetic `**kwargs`
// parameter, but dataflow-consistency queries should _not_ complain about there not
// being a post-update node for the synthetic `**kwargs` parameter.
n instanceof SynthDictSplatParameterNode
}
override predicate uniqueParameterNodePositionExclude(
DataFlowCallable c, ParameterPosition pos, Node p
) {
// For normal parameters that can both be passed as positional arguments or keyword
// arguments, we currently have parameter positions for both cases..
//
// TODO: Figure out how bad breaking this consistency check is
exists(Function func, Parameter param |
c.getScope() = func and
p = parameterNode(param) and
c.getParameter(pos) = p and
param = func.getArg(_) and
param = func.getArgByName(_)
)
}
override predicate uniqueCallEnclosingCallableExclude(DataFlowCall call) {
not exists(call.getLocation().getFile().getRelativePath())
}
override predicate identityLocalStepExclude(Node n) {
not exists(n.getLocation().getFile().getRelativePath())
}
}

View File

@@ -1,58 +1,117 @@
import python
import semmle.python.dataflow.new.DataFlow
import semmle.python.dataflow.new.TaintTracking
import TestUtilities.InlineExpectationsTest
private import semmle.python.dataflow.new.internal.PrintNode
class DataFlowQueryTest extends InlineExpectationsTest {
DataFlowQueryTest() { this = "DataFlowQueryTest" }
signature module QueryTestSig {
predicate isSink(DataFlow::Node sink);
override string getARelevantTag() { result = "result" }
predicate flowTo(DataFlow::Node sink);
}
override predicate hasActualResult(Location location, string element, string tag, string value) {
exists(DataFlow::Configuration cfg, DataFlow::Node sink | cfg.hasFlowTo(sink) |
location = sink.getLocation() and
tag = "result" and
value = "BAD" and
element = sink.toString()
)
module MakeQueryTest<QueryTestSig Impl> {
module DataFlowQueryTest implements TestSig {
string getARelevantTag() { result = "result" }
predicate hasActualResult(Location location, string element, string tag, string value) {
exists(DataFlow::Node sink | Impl::flowTo(sink) |
location = sink.getLocation() and
tag = "result" and
value = "BAD" and
element = sink.toString()
)
}
// We allow annotating any sink with `result=OK` to signal
// safe sinks.
// Sometimes a line contains both an alert and a safe sink.
// In this situation, the annotation form `OK(safe sink)`
// can be useful.
predicate hasOptionalResult(Location location, string element, string tag, string value) {
exists(DataFlow::Node sink | Impl::isSink(sink) |
location = sink.getLocation() and
tag = "result" and
value in ["OK", "OK(" + prettyNode(sink) + ")"] and
element = sink.toString()
)
}
}
// We allow annotating any sink with `result=OK` to signal
// safe sinks.
// Sometimes a line contains both an alert and a safe sink.
// In this situation, the annotation form `OK(safe sink)`
// can be useful.
override predicate hasOptionalResult(Location location, string element, string tag, string value) {
exists(DataFlow::Configuration cfg, DataFlow::Node sink |
cfg.isSink(sink) or cfg.isSink(sink, _)
|
import MakeTest<DataFlowQueryTest>
query predicate missingAnnotationOnSink(Location location, string error, string element) {
error = "ERROR, you should add `# $ MISSING: result=BAD` or `result=OK` annotation" and
exists(DataFlow::Node sink |
exists(sink.getLocation().getFile().getRelativePath()) and
Impl::isSink(sink) and
location = sink.getLocation() and
tag = "result" and
value in ["OK", "OK(" + prettyNode(sink) + ")"] and
element = sink.toString()
element = prettyExpr(sink.asExpr()) and
not Impl::flowTo(sink) and
not exists(FalseNegativeTestExpectation missingResult |
missingResult.getTag() = "result" and
missingResult.getValue() = "BAD" and
missingResult.getLocation().getFile() = location.getFile() and
missingResult.getLocation().getStartLine() = location.getStartLine()
) and
not exists(GoodTestExpectation okResult |
okResult.getTag() = "result" and
okResult.getValue() in ["OK", "OK(" + prettyNode(sink) + ")"] and
okResult.getLocation().getFile() = location.getFile() and
okResult.getLocation().getStartLine() = location.getStartLine()
)
)
}
}
query predicate missingAnnotationOnSink(Location location, string error, string element) {
error = "ERROR, you should add `# $ MISSING: result=BAD` or `result=OK` annotation" and
exists(DataFlow::Node sink |
exists(sink.getLocation().getFile().getRelativePath()) and
exists(DataFlow::Configuration cfg | cfg.isSink(sink) or cfg.isSink(sink, _)) and
location = sink.getLocation() and
element = prettyExpr(sink.asExpr()) and
not exists(DataFlow::Configuration cfg | cfg.hasFlowTo(sink)) and
not exists(FalseNegativeExpectation missingResult |
missingResult.getTag() = "result" and
missingResult.getValue() = "BAD" and
missingResult.getLocation().getFile() = location.getFile() and
missingResult.getLocation().getStartLine() = location.getStartLine()
) and
not exists(GoodExpectation okResult |
okResult.getTag() = "result" and
okResult.getValue() in ["OK", "OK(" + prettyNode(sink) + ")"] and
okResult.getLocation().getFile() = location.getFile() and
okResult.getLocation().getStartLine() = location.getStartLine()
)
)
module FromDataFlowConfig<DataFlow::ConfigSig C> {
module Impl implements QueryTestSig {
predicate isSink(DataFlow::Node sink) { C::isSink(sink) }
predicate flowTo(DataFlow::Node sink) { DataFlow::Global<C>::flowTo(sink) }
}
import MakeQueryTest<Impl>
}
module FromDataFlowStateConfig<DataFlow::StateConfigSig C> {
module Impl implements QueryTestSig {
predicate isSink(DataFlow::Node sink) { C::isSink(sink) or C::isSink(sink, _) }
predicate flowTo(DataFlow::Node sink) { DataFlow::GlobalWithState<C>::flowTo(sink) }
}
import MakeQueryTest<Impl>
}
module FromTaintTrackingConfig<DataFlow::ConfigSig C> {
module Impl implements QueryTestSig {
predicate isSink(DataFlow::Node sink) { C::isSink(sink) }
predicate flowTo(DataFlow::Node sink) { TaintTracking::Global<C>::flowTo(sink) }
}
import MakeQueryTest<Impl>
}
module FromTaintTrackingStateConfig<DataFlow::StateConfigSig C> {
module Impl implements QueryTestSig {
predicate isSink(DataFlow::Node sink) { C::isSink(sink) or C::isSink(sink, _) }
predicate flowTo(DataFlow::Node sink) { TaintTracking::GlobalWithState<C>::flowTo(sink) }
}
import MakeQueryTest<Impl>
}
signature class LegacyConfiguration extends DataFlow::Configuration;
module FromLegacyConfiguration<LegacyConfiguration C> {
module Impl implements QueryTestSig {
predicate isSink(DataFlow::Node sink) { any(C c).isSink(sink) or any(C c).isSink(sink, _) }
predicate flowTo(DataFlow::Node sink) { any(C c).hasFlowTo(sink) }
}
import MakeQueryTest<Impl>
}

View File

@@ -3,22 +3,21 @@ import semmle.python.dataflow.new.DataFlow
import TestUtilities.InlineExpectationsTest
private import semmle.python.dataflow.new.internal.PrintNode
abstract class FlowTest extends InlineExpectationsTest {
bindingset[this]
FlowTest() { any() }
signature module FlowTestSig {
string flowTag();
abstract string flowTag();
predicate relevantFlow(DataFlow::Node fromNode, DataFlow::Node toNode);
}
abstract predicate relevantFlow(DataFlow::Node fromNode, DataFlow::Node toNode);
module MakeTestSig<FlowTestSig Impl> implements TestSig {
string getARelevantTag() { result = Impl::flowTag() }
override string getARelevantTag() { result = this.flowTag() }
override predicate hasActualResult(Location location, string element, string tag, string value) {
exists(DataFlow::Node fromNode, DataFlow::Node toNode | this.relevantFlow(fromNode, toNode) |
predicate hasActualResult(Location location, string element, string tag, string value) {
exists(DataFlow::Node fromNode, DataFlow::Node toNode | Impl::relevantFlow(fromNode, toNode) |
location = toNode.getLocation() and
tag = this.flowTag() and
tag = Impl::flowTag() and
value =
"\"" + prettyNode(fromNode).replaceAll("\"", "'") + this.lineStr(fromNode, toNode) + " -> " +
"\"" + prettyNode(fromNode).replaceAll("\"", "'") + lineStr(fromNode, toNode) + " -> " +
prettyNode(toNode).replaceAll("\"", "'") + "\"" and
element = toNode.toString()
)

View File

@@ -2,12 +2,12 @@ import python
import semmle.python.dataflow.new.DataFlow
import FlowTest
class LocalFlowStepTest extends FlowTest {
LocalFlowStepTest() { this = "LocalFlowStepTest" }
module LocalFlowStepTest implements FlowTestSig {
string flowTag() { result = "step" }
override string flowTag() { result = "step" }
override predicate relevantFlow(DataFlow::Node fromNode, DataFlow::Node toNode) {
predicate relevantFlow(DataFlow::Node fromNode, DataFlow::Node toNode) {
DataFlow::localFlowStep(fromNode, toNode)
}
}
import MakeTest<MakeTestSig<LocalFlowStepTest>>

View File

@@ -3,25 +3,23 @@ import semmle.python.dataflow.new.DataFlow
private import semmle.python.dataflow.new.internal.DataFlowPrivate
import FlowTest
class MaximalFlowTest extends FlowTest {
MaximalFlowTest() { this = "MaximalFlowTest" }
module MaximalFlowTest implements FlowTestSig {
string flowTag() { result = "flow" }
override string flowTag() { result = "flow" }
override predicate relevantFlow(DataFlow::Node source, DataFlow::Node sink) {
predicate relevantFlow(DataFlow::Node source, DataFlow::Node sink) {
source != sink and
exists(MaximalFlowsConfig cfg | cfg.hasFlow(source, sink))
MaximalFlows::flow(source, sink)
}
}
import MakeTest<MakeTestSig<MaximalFlowTest>>
/**
* A configuration to find all "maximal" flows.
* To be used on small programs.
*/
class MaximalFlowsConfig extends DataFlow::Configuration {
MaximalFlowsConfig() { this = "MaximalFlowsConfig" }
override predicate isSource(DataFlow::Node node) {
module MaximalFlowsConfig implements DataFlow::ConfigSig {
predicate isSource(DataFlow::Node node) {
exists(node.getLocation().getFile().getRelativePath()) and
not node.asCfgNode() instanceof CallNode and
not node.asCfgNode().getNode() instanceof Return and
@@ -32,7 +30,7 @@ class MaximalFlowsConfig extends DataFlow::Configuration {
not DataFlow::localFlowStep(_, node)
}
override predicate isSink(DataFlow::Node node) {
predicate isSink(DataFlow::Node node) {
exists(node.getLocation().getFile().getRelativePath()) and
not any(CallNode c).getArg(_) = node.asCfgNode() and
not node instanceof DataFlow::ArgumentNode and
@@ -40,3 +38,5 @@ class MaximalFlowsConfig extends DataFlow::Configuration {
not DataFlow::localFlowStep(node, _)
}
}
module MaximalFlows = DataFlow::Global<MaximalFlowsConfig>;

View File

@@ -3,20 +3,20 @@ import experimental.dataflow.TestUtil.FlowTest
import experimental.dataflow.testConfig
private import semmle.python.dataflow.new.internal.PrintNode
class DataFlowTest extends FlowTest {
DataFlowTest() { this = "DataFlowTest" }
module DataFlowTest implements FlowTestSig {
string flowTag() { result = "flow" }
override string flowTag() { result = "flow" }
override predicate relevantFlow(DataFlow::Node source, DataFlow::Node sink) {
exists(TestConfiguration cfg | cfg.hasFlow(source, sink))
predicate relevantFlow(DataFlow::Node source, DataFlow::Node sink) {
TestFlow::flow(source, sink)
}
}
import MakeTest<MakeTestSig<DataFlowTest>>
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
TestConfig::isSink(sink) and
// note: we only care about `SINK` and not `SINK_F`, so we have to reconstruct manually.
exists(DataFlow::CallCfgNode call |
call.getFunction().asCfgNode().(NameNode).getId() = "SINK" and
@@ -24,8 +24,8 @@ query predicate missingAnnotationOnSink(Location location, string error, string
) and
location = sink.getLocation() and
element = prettyExpr(sink.asExpr()) and
not any(TestConfiguration config).hasFlow(_, sink) and
not exists(FalseNegativeExpectation missingResult |
not TestFlow::flowTo(sink) and
not exists(FalseNegativeTestExpectation missingResult |
missingResult.getTag() = "flow" and
missingResult.getLocation().getFile() = location.getFile() and
missingResult.getLocation().getStartLine() = location.getStartLine()

View File

@@ -3,16 +3,16 @@ import experimental.dataflow.TestUtil.FlowTest
import experimental.dataflow.testTaintConfig
private import semmle.python.dataflow.new.internal.PrintNode
class DataFlowTest extends FlowTest {
DataFlowTest() { this = "DataFlowTest" }
module DataFlowTest implements FlowTestSig {
string flowTag() { result = "flow" }
override string flowTag() { result = "flow" }
override predicate relevantFlow(DataFlow::Node source, DataFlow::Node sink) {
exists(TestConfiguration cfg | cfg.hasFlow(source, sink))
predicate relevantFlow(DataFlow::Node source, DataFlow::Node sink) {
TestFlow::flow(source, sink)
}
}
import MakeTest<MakeTestSig<DataFlowTest>>
query predicate missingAnnotationOnSink(Location location, string error, string element) {
error = "ERROR, you should add `# $ MISSING: flow` annotation" and
exists(DataFlow::Node sink |
@@ -23,8 +23,8 @@ query predicate missingAnnotationOnSink(Location location, string error, string
) and
location = sink.getLocation() and
element = prettyExpr(sink.asExpr()) and
not any(TestConfiguration config).hasFlow(_, sink) and
not exists(FalseNegativeExpectation missingResult |
not TestFlow::flowTo(sink) and
not exists(FalseNegativeTestExpectation missingResult |
missingResult.getTag() = "flow" and
missingResult.getLocation().getFile() = location.getFile() and
missingResult.getLocation().getStartLine() = location.getStartLine()

View File

@@ -10,22 +10,25 @@ private import semmle.python.dataflow.new.internal.DataFlowPrivate as DataFlowPr
* the functions tested sink their arguments sequentially, that is
* `SINK1(arg1)`, etc.
*/
abstract class RoutingTest extends InlineExpectationsTest {
bindingset[this]
RoutingTest() { any() }
signature module RoutingTestSig {
class Argument;
abstract string flowTag();
string flowTag(Argument arg);
abstract predicate relevantFlow(DataFlow::Node fromNode, DataFlow::Node toNode);
predicate relevantFlow(DataFlow::Node fromNode, DataFlow::Node toNode, Argument arg);
}
override string getARelevantTag() { result in ["func", this.flowTag()] }
module MakeTestSig<RoutingTestSig Impl> implements TestSig {
string getARelevantTag() { result in ["func", Impl::flowTag(_)] }
override predicate hasActualResult(Location location, string element, string tag, string value) {
exists(DataFlow::Node fromNode, DataFlow::Node toNode | this.relevantFlow(fromNode, toNode) |
predicate hasActualResult(Location location, string element, string tag, string value) {
exists(DataFlow::Node fromNode, DataFlow::Node toNode, Impl::Argument arg |
Impl::relevantFlow(fromNode, toNode, arg)
|
location = fromNode.getLocation() and
element = fromNode.toString() and
(
tag = this.flowTag() and
tag = Impl::flowTag(arg) and
if "\"" + tag + "\"" = fromValue(fromNode) then value = "" else value = fromValue(fromNode)
or
// only have result for `func` tag if the function where `arg<n>` is used, is

View File

@@ -4,11 +4,11 @@ private import semmle.python.dataflow.new.internal.DataFlowPrivate as DataFlowPr
private import semmle.python.ApiGraphs
import TestUtilities.InlineExpectationsTest
class UnresolvedCallExpectations extends InlineExpectationsTest {
UnresolvedCallExpectations() { this = "UnresolvedCallExpectations" }
override string getARelevantTag() { result = "unresolved_call" }
signature module UnresolvedCallExpectationsSig {
predicate unresolvedCall(CallNode call);
}
module DefaultUnresolvedCallExpectations implements UnresolvedCallExpectationsSig {
predicate unresolvedCall(CallNode call) {
not exists(DataFlowPrivate::DataFlowCall dfc |
exists(dfc.getCallable()) and dfc.getNode() = call
@@ -16,14 +16,22 @@ class UnresolvedCallExpectations extends InlineExpectationsTest {
not DataFlowPrivate::resolveClassCall(call, _) and
not call = API::builtin(_).getACall().asCfgNode()
}
override predicate hasActualResult(Location location, string element, string tag, string value) {
exists(location.getFile().getRelativePath()) and
exists(CallNode call | this.unresolvedCall(call) |
location = call.getLocation() and
tag = "unresolved_call" and
value = prettyExpr(call.getNode()) and
element = call.toString()
)
}
}
module MakeUnresolvedCallExpectations<UnresolvedCallExpectationsSig Impl> {
private module UnresolvedCallExpectations implements TestSig {
string getARelevantTag() { result = "unresolved_call" }
predicate hasActualResult(Location location, string element, string tag, string value) {
exists(location.getFile().getRelativePath()) and
exists(CallNode call | Impl::unresolvedCall(call) |
location = call.getLocation() and
tag = "unresolved_call" and
value = prettyExpr(call.getNode()) and
element = call.toString()
)
}
}
import MakeTest<UnresolvedCallExpectations>
}

View File

@@ -4,7 +4,6 @@ uniqueType
uniqueNodeLocation
missingLocation
uniqueNodeToString
missingToString
parameterCallable
localFlowIsLocal
readStepIsLocal
@@ -24,3 +23,5 @@ uniqueParameterNodeAtPosition
uniqueParameterNodePosition
uniqueContentApprox
identityLocalStep
missingArgumentCall
multipleArgumentCall

View File

@@ -1,6 +1,5 @@
| test.py:1:1:1:21 | ControlFlowNode for FunctionExpr | test.py:1:5:1:17 | GSSA Variable obfuscated_id |
| test.py:1:1:1:21 | ControlFlowNode for FunctionExpr | test.py:1:5:1:17 | GSSA Variable obfuscated_id |
| test.py:1:1:1:21 | ControlFlowNode for FunctionExpr | test.py:7:5:7:17 | ControlFlowNode for obfuscated_id |
| test.py:1:5:1:17 | GSSA Variable obfuscated_id | test.py:7:5:7:17 | ControlFlowNode for obfuscated_id |
| test.py:1:19:1:19 | ControlFlowNode for x | test.py:1:19:1:19 | SSA variable x |
| test.py:1:19:1:19 | ControlFlowNode for x | test.py:1:19:1:19 | SSA variable x |
@@ -14,18 +13,6 @@
| test.py:1:19:1:19 | ControlFlowNode for x | test.py:2:7:2:7 | ControlFlowNode for x |
| test.py:1:19:1:19 | ControlFlowNode for x | test.py:2:7:2:7 | ControlFlowNode for x |
| test.py:1:19:1:19 | ControlFlowNode for x | test.py:2:7:2:7 | ControlFlowNode for x |
| test.py:1:19:1:19 | ControlFlowNode for x | test.py:3:3:3:3 | SSA variable z |
| test.py:1:19:1:19 | ControlFlowNode for x | test.py:3:3:3:3 | SSA variable z |
| test.py:1:19:1:19 | ControlFlowNode for x | test.py:3:3:3:3 | SSA variable z |
| test.py:1:19:1:19 | ControlFlowNode for x | test.py:3:3:3:3 | SSA variable z |
| test.py:1:19:1:19 | ControlFlowNode for x | test.py:3:7:3:7 | ControlFlowNode for y |
| test.py:1:19:1:19 | ControlFlowNode for x | test.py:3:7:3:7 | ControlFlowNode for y |
| test.py:1:19:1:19 | ControlFlowNode for x | test.py:3:7:3:7 | ControlFlowNode for y |
| test.py:1:19:1:19 | ControlFlowNode for x | test.py:3:7:3:7 | ControlFlowNode for y |
| test.py:1:19:1:19 | ControlFlowNode for x | test.py:4:10:4:10 | ControlFlowNode for z |
| test.py:1:19:1:19 | ControlFlowNode for x | test.py:4:10:4:10 | ControlFlowNode for z |
| test.py:1:19:1:19 | ControlFlowNode for x | test.py:4:10:4:10 | ControlFlowNode for z |
| test.py:1:19:1:19 | ControlFlowNode for x | test.py:4:10:4:10 | ControlFlowNode for z |
| test.py:1:19:1:19 | SSA variable x | test.py:2:3:2:3 | SSA variable y |
| test.py:1:19:1:19 | SSA variable x | test.py:2:3:2:3 | SSA variable y |
| test.py:1:19:1:19 | SSA variable x | test.py:2:3:2:3 | SSA variable y |
@@ -34,18 +21,6 @@
| test.py:1:19:1:19 | SSA variable x | test.py:2:7:2:7 | ControlFlowNode for x |
| test.py:1:19:1:19 | SSA variable x | test.py:2:7:2:7 | ControlFlowNode for x |
| test.py:1:19:1:19 | SSA variable x | test.py:2:7:2:7 | ControlFlowNode for x |
| test.py:1:19:1:19 | SSA variable x | test.py:3:3:3:3 | SSA variable z |
| test.py:1:19:1:19 | SSA variable x | test.py:3:3:3:3 | SSA variable z |
| test.py:1:19:1:19 | SSA variable x | test.py:3:3:3:3 | SSA variable z |
| test.py:1:19:1:19 | SSA variable x | test.py:3:3:3:3 | SSA variable z |
| test.py:1:19:1:19 | SSA variable x | test.py:3:7:3:7 | ControlFlowNode for y |
| test.py:1:19:1:19 | SSA variable x | test.py:3:7:3:7 | ControlFlowNode for y |
| test.py:1:19:1:19 | SSA variable x | test.py:3:7:3:7 | ControlFlowNode for y |
| test.py:1:19:1:19 | SSA variable x | test.py:3:7:3:7 | ControlFlowNode for y |
| test.py:1:19:1:19 | SSA variable x | test.py:4:10:4:10 | ControlFlowNode for z |
| test.py:1:19:1:19 | SSA variable x | test.py:4:10:4:10 | ControlFlowNode for z |
| test.py:1:19:1:19 | SSA variable x | test.py:4:10:4:10 | ControlFlowNode for z |
| test.py:1:19:1:19 | SSA variable x | test.py:4:10:4:10 | ControlFlowNode for z |
| test.py:2:3:2:3 | SSA variable y | test.py:3:3:3:3 | SSA variable z |
| test.py:2:3:2:3 | SSA variable y | test.py:3:3:3:3 | SSA variable z |
| test.py:2:3:2:3 | SSA variable y | test.py:3:3:3:3 | SSA variable z |
@@ -54,26 +29,10 @@
| test.py:2:3:2:3 | SSA variable y | test.py:3:7:3:7 | ControlFlowNode for y |
| test.py:2:3:2:3 | SSA variable y | test.py:3:7:3:7 | ControlFlowNode for y |
| test.py:2:3:2:3 | SSA variable y | test.py:3:7:3:7 | ControlFlowNode for y |
| test.py:2:3:2:3 | SSA variable y | test.py:4:10:4:10 | ControlFlowNode for z |
| test.py:2:3:2:3 | SSA variable y | test.py:4:10:4:10 | ControlFlowNode for z |
| test.py:2:3:2:3 | SSA variable y | test.py:4:10:4:10 | ControlFlowNode for z |
| test.py:2:3:2:3 | SSA variable y | test.py:4:10:4:10 | ControlFlowNode for z |
| test.py:2:7:2:7 | ControlFlowNode for x | test.py:2:3:2:3 | SSA variable y |
| test.py:2:7:2:7 | ControlFlowNode for x | test.py:2:3:2:3 | SSA variable y |
| test.py:2:7:2:7 | ControlFlowNode for x | test.py:2:3:2:3 | SSA variable y |
| test.py:2:7:2:7 | ControlFlowNode for x | test.py:2:3:2:3 | SSA variable y |
| test.py:2:7:2:7 | ControlFlowNode for x | test.py:3:3:3:3 | SSA variable z |
| test.py:2:7:2:7 | ControlFlowNode for x | test.py:3:3:3:3 | SSA variable z |
| test.py:2:7:2:7 | ControlFlowNode for x | test.py:3:3:3:3 | SSA variable z |
| test.py:2:7:2:7 | ControlFlowNode for x | test.py:3:3:3:3 | SSA variable z |
| test.py:2:7:2:7 | ControlFlowNode for x | test.py:3:7:3:7 | ControlFlowNode for y |
| test.py:2:7:2:7 | ControlFlowNode for x | test.py:3:7:3:7 | ControlFlowNode for y |
| test.py:2:7:2:7 | ControlFlowNode for x | test.py:3:7:3:7 | ControlFlowNode for y |
| test.py:2:7:2:7 | ControlFlowNode for x | test.py:3:7:3:7 | ControlFlowNode for y |
| test.py:2:7:2:7 | ControlFlowNode for x | test.py:4:10:4:10 | ControlFlowNode for z |
| test.py:2:7:2:7 | ControlFlowNode for x | test.py:4:10:4:10 | ControlFlowNode for z |
| test.py:2:7:2:7 | ControlFlowNode for x | test.py:4:10:4:10 | ControlFlowNode for z |
| test.py:2:7:2:7 | ControlFlowNode for x | test.py:4:10:4:10 | ControlFlowNode for z |
| test.py:3:3:3:3 | SSA variable z | test.py:4:10:4:10 | ControlFlowNode for z |
| test.py:3:3:3:3 | SSA variable z | test.py:4:10:4:10 | ControlFlowNode for z |
| test.py:3:3:3:3 | SSA variable z | test.py:4:10:4:10 | ControlFlowNode for z |
@@ -82,18 +41,12 @@
| test.py:3:7:3:7 | ControlFlowNode for y | test.py:3:3:3:3 | SSA variable z |
| test.py:3:7:3:7 | ControlFlowNode for y | test.py:3:3:3:3 | SSA variable z |
| test.py:3:7:3:7 | ControlFlowNode for y | test.py:3:3:3:3 | SSA variable z |
| test.py:3:7:3:7 | ControlFlowNode for y | test.py:4:10:4:10 | ControlFlowNode for z |
| test.py:3:7:3:7 | ControlFlowNode for y | test.py:4:10:4:10 | ControlFlowNode for z |
| test.py:3:7:3:7 | ControlFlowNode for y | test.py:4:10:4:10 | ControlFlowNode for z |
| test.py:3:7:3:7 | ControlFlowNode for y | test.py:4:10:4:10 | ControlFlowNode for z |
| test.py:4:10:4:10 | ControlFlowNode for z | test.py:7:5:7:20 | ControlFlowNode for obfuscated_id() |
| test.py:4:10:4:10 | ControlFlowNode for z | test.py:7:5:7:20 | ControlFlowNode for obfuscated_id() |
| test.py:6:1:6:1 | GSSA Variable a | test.py:7:19:7:19 | ControlFlowNode for a |
| test.py:6:1:6:1 | GSSA Variable a | test.py:7:19:7:19 | ControlFlowNode for a |
| test.py:6:5:6:6 | ControlFlowNode for IntegerLiteral | test.py:6:1:6:1 | GSSA Variable a |
| test.py:6:5:6:6 | ControlFlowNode for IntegerLiteral | test.py:6:1:6:1 | GSSA Variable a |
| test.py:6:5:6:6 | ControlFlowNode for IntegerLiteral | test.py:7:19:7:19 | ControlFlowNode for a |
| test.py:6:5:6:6 | ControlFlowNode for IntegerLiteral | test.py:7:19:7:19 | ControlFlowNode for a |
| test.py:7:5:7:20 | ControlFlowNode for obfuscated_id() | test.py:7:1:7:1 | GSSA Variable b |
| test.py:7:19:7:19 | ControlFlowNode for a | test.py:1:19:1:19 | ControlFlowNode for x |
| test.py:7:19:7:19 | ControlFlowNode for a | test.py:1:19:1:19 | ControlFlowNode for x |

View File

@@ -0,0 +1,2 @@
testFailures
failures

View File

@@ -0,0 +1,2 @@
testFailures
failures

View File

@@ -4,7 +4,6 @@ uniqueType
uniqueNodeLocation
missingLocation
uniqueNodeToString
missingToString
parameterCallable
localFlowIsLocal
readStepIsLocal
@@ -24,3 +23,5 @@ uniqueParameterNodeAtPosition
uniqueParameterNodePosition
uniqueContentApprox
identityLocalStep
missingArgumentCall
multipleArgumentCall

View File

@@ -1,2 +1,2 @@
failures
testFailures
failures

View File

@@ -1,14 +1,9 @@
uniqueEnclosingCallable
uniqueCallEnclosingCallable
| new_cls_param.py:14:6:14:16 | classmethod() | Call should have one enclosing callable but has 0. |
| test.py:21:6:21:17 | staticmethod() | Call should have one enclosing callable but has 0. |
| test.py:25:6:25:16 | classmethod() | Call should have one enclosing callable but has 0. |
| test.py:29:6:29:16 | classmethod() | Call should have one enclosing callable but has 0. |
uniqueType
uniqueNodeLocation
missingLocation
uniqueNodeToString
missingToString
parameterCallable
localFlowIsLocal
readStepIsLocal
@@ -28,3 +23,5 @@ uniqueParameterNodeAtPosition
uniqueParameterNodePosition
uniqueContentApprox
identityLocalStep
missingArgumentCall
multipleArgumentCall

View File

@@ -0,0 +1,25 @@
# Originally we had module and functions as `DataFlowCallable``, and any call inside a
# class scope would not have a result for getEnclosingCallable. Since this was only a
# consistency error for calls, originally we added a new `DataFlowClassScope` only for
# those classes that had a call in their scope. That's why all the class definitions in
# this test do a call to the dummy function `func`.
#
# Note: this was shortsighted, since most DataFlow::Node use `getCallableScope` helper
# to define their .getEnclosingCallable(), which picks the first DataFlowCallable to
# contain the node. (so for some classes that would be DataFlowClassScope, and for some
# it would be the module/function containing the class definition)
def func(*args, **kwargs):
print("func()")
class Cls:
func()
class Inner:
func()
def other_func():
class Cls2:
func()
return Cls2
x = other_func()

View File

@@ -4,7 +4,6 @@ uniqueType
uniqueNodeLocation
missingLocation
uniqueNodeToString
missingToString
parameterCallable
localFlowIsLocal
readStepIsLocal
@@ -24,3 +23,5 @@ uniqueParameterNodeAtPosition
uniqueParameterNodePosition
uniqueContentApprox
identityLocalStep
missingArgumentCall
multipleArgumentCall

View File

@@ -0,0 +1,2 @@
testFailures
failures

View File

@@ -0,0 +1,2 @@
testFailures
failures

View File

@@ -1,2 +1,3 @@
missingAnnotationOnSink
testFailures
failures

View File

@@ -5,6 +5,7 @@ import functools
sys.path.append(os.path.dirname(os.path.dirname((__file__))))
from testlib import expects
SOURCE = "source"
arg = "source"
arg1 = "source1"
arg2 = "source2"
@@ -269,3 +270,68 @@ def test_stararg_mixed():
starargs_mixed(arg1, *args, *empty_args) # $ arg1
args = (arg2, "safe")
starargs_mixed(arg1, *empty_args, *args) # $ arg1 MISSING: arg2
# ------------------------------------------------------------------------------
# Test updating field of argument
# ------------------------------------------------------------------------------
class MyClass: pass
def kwargsSideEffect(**kwargs):
kwargs["a"].foo = kwargs["b"]
@expects(2)
def test_kwargsSideEffect():
a = MyClass()
kwargs = {"a": a, "b": SOURCE}
kwargsSideEffect(**kwargs)
SINK(a.foo) # $ MISSING: flow
a = MyClass()
kwargsSideEffect(a=a, b=SOURCE)
SINK(a.foo) # $ MISSING: flow
def keywordArgSideEffect(a, b):
a.foo = b
@expects(2)
def test_keywordArgSideEffect():
a = MyClass()
kwargs = {"a": a, "b": SOURCE}
keywordArgSideEffect(**kwargs)
SINK(a.foo) # $ MISSING: flow
a = MyClass()
keywordArgSideEffect(a=a, b=SOURCE)
SINK(a.foo) # $ flow="SOURCE, l:-1 -> a.foo"
def starargsSideEffect(*args):
args[0].foo = args[1]
@expects(2)
def test_starargsSideEffect():
a = MyClass()
args = (a, SOURCE)
starargsSideEffect(*args)
SINK(a.foo) # $ MISSING: flow
a = MyClass()
starargsSideEffect(a, SOURCE)
SINK(a.foo) # $ MISSING: flow
def positionalArgSideEffect(a, b):
a.foo = b
@expects(2)
def test_positionalArgSideEffect():
a = MyClass()
args = (a, SOURCE)
positionalArgSideEffect(*args)
SINK(a.foo) # $ MISSING: flow
a = MyClass()
positionalArgSideEffect(a, SOURCE)
SINK(a.foo) # $ flow="SOURCE, l:-1 -> a.foo"

View File

@@ -0,0 +1,2 @@
testFailures
failures

View File

@@ -3,19 +3,22 @@ import semmle.python.dataflow.new.DataFlow
private import semmle.python.dataflow.new.internal.DataFlowPrivate as DataFlowPrivate
import experimental.dataflow.TestUtil.RoutingTest
class Argument1RoutingTest extends RoutingTest {
Argument1RoutingTest() { this = "Argument1RoutingTest" }
module Argument1RoutingTest implements RoutingTestSig {
class Argument = Unit;
override string flowTag() { result = "arg1" }
string flowTag(Argument arg) { result = "arg1" and exists(arg) }
override predicate relevantFlow(DataFlow::Node source, DataFlow::Node sink) {
exists(Argument1ExtraRoutingConfig cfg | cfg.hasFlow(source, sink))
or
exists(ArgumentRoutingConfig cfg |
cfg.hasFlow(source, sink) and
cfg.isArgSource(source, 1) and
cfg.isGoodSink(sink, 1)
)
predicate relevantFlow(DataFlow::Node source, DataFlow::Node sink, Argument arg) {
(
exists(Argument1ExtraRoutingConfig cfg | cfg.hasFlow(source, sink))
or
exists(ArgumentRoutingConfig cfg |
cfg.hasFlow(source, sink) and
cfg.isArgSource(source, 1) and
cfg.isGoodSink(sink, 1)
)
) and
exists(arg)
}
}
@@ -87,59 +90,54 @@ class Argument1ExtraRoutingConfig extends DataFlow::Configuration {
override predicate isBarrierIn(DataFlow::Node node) { this.isSource(node) }
}
class RestArgumentRoutingTest extends RoutingTest {
ArgNumber argNumber;
module RestArgumentRoutingTest implements RoutingTestSig {
class Argument = ArgNumber;
RestArgumentRoutingTest() {
argNumber > 1 and
this = "Argument" + argNumber + "RoutingTest"
}
string flowTag(Argument arg) { result = "arg" + arg }
override string flowTag() { result = "arg" + argNumber }
override predicate relevantFlow(DataFlow::Node source, DataFlow::Node sink) {
predicate relevantFlow(DataFlow::Node source, DataFlow::Node sink, Argument arg) {
exists(ArgumentRoutingConfig cfg |
cfg.hasFlow(source, sink) and
cfg.isArgSource(source, argNumber) and
cfg.isGoodSink(sink, argNumber)
)
cfg.isArgSource(source, arg) and
cfg.isGoodSink(sink, arg)
) and
arg > 1
}
}
/** Bad flow from `arg<n>` to `SINK<N>_F` */
class BadArgumentRoutingTestSinkF extends RoutingTest {
ArgNumber argNumber;
module BadArgumentRoutingTestSinkF implements RoutingTestSig {
class Argument = ArgNumber;
BadArgumentRoutingTestSinkF() { this = "BadArgumentRoutingTestSinkF" + argNumber }
string flowTag(Argument arg) { result = "bad" + arg }
override string flowTag() { result = "bad" + argNumber }
override predicate relevantFlow(DataFlow::Node source, DataFlow::Node sink) {
predicate relevantFlow(DataFlow::Node source, DataFlow::Node sink, Argument arg) {
exists(ArgumentRoutingConfig cfg |
cfg.hasFlow(source, sink) and
cfg.isArgSource(source, argNumber) and
cfg.isBadSink(sink, argNumber)
cfg.isArgSource(source, arg) and
cfg.isBadSink(sink, arg)
)
}
}
/** Bad flow from `arg<n>` to `SINK<M>` or `SINK<M>_F`, where `n != m`. */
class BadArgumentRoutingTestWrongSink extends RoutingTest {
ArgNumber argNumber;
module BadArgumentRoutingTestWrongSink implements RoutingTestSig {
class Argument = ArgNumber;
BadArgumentRoutingTestWrongSink() { this = "BadArgumentRoutingTestWrongSink" + argNumber }
string flowTag(Argument arg) { result = "bad" + arg }
override string flowTag() { result = "bad" + argNumber }
override predicate relevantFlow(DataFlow::Node source, DataFlow::Node sink) {
predicate relevantFlow(DataFlow::Node source, DataFlow::Node sink, Argument arg) {
exists(ArgumentRoutingConfig cfg |
cfg.hasFlow(source, sink) and
cfg.isArgSource(source, any(ArgNumber i | not i = argNumber)) and
cfg.isArgSource(source, any(ArgNumber i | not i = arg)) and
(
cfg.isGoodSink(sink, argNumber)
cfg.isGoodSink(sink, arg)
or
cfg.isBadSink(sink, argNumber)
cfg.isBadSink(sink, arg)
)
)
}
}
import MakeTest<MergeTests4<MakeTestSig<Argument1RoutingTest>, MakeTestSig<RestArgumentRoutingTest>,
MakeTestSig<BadArgumentRoutingTestSinkF>, MakeTestSig<BadArgumentRoutingTestWrongSink>>>

View File

@@ -1,12 +1,9 @@
uniqueEnclosingCallable
uniqueCallEnclosingCallable
| datamodel.py:71:6:71:16 | classmethod() | Call should have one enclosing callable but has 0. |
| datamodel.py:76:6:76:17 | staticmethod() | Call should have one enclosing callable but has 0. |
uniqueType
uniqueNodeLocation
missingLocation
uniqueNodeToString
missingToString
parameterCallable
localFlowIsLocal
readStepIsLocal
@@ -26,16 +23,5 @@ uniqueParameterNodeAtPosition
uniqueParameterNodePosition
uniqueContentApprox
identityLocalStep
| datamodel.py:84:15:84:15 | ControlFlowNode for x | Node steps to itself |
| datamodel.py:166:11:166:11 | ControlFlowNode for x | Node steps to itself |
| test.py:103:10:103:15 | ControlFlowNode for SOURCE | Node steps to itself |
| test.py:130:10:130:15 | ControlFlowNode for SOURCE | Node steps to itself |
| test.py:162:13:162:18 | ControlFlowNode for SOURCE | Node steps to itself |
| test.py:167:13:167:18 | ControlFlowNode for SOURCE | Node steps to itself |
| test.py:216:10:216:15 | ControlFlowNode for SOURCE | Node steps to itself |
| test.py:242:9:242:12 | ControlFlowNode for SINK | Node steps to itself |
| test.py:669:9:669:12 | ControlFlowNode for SINK | Node steps to itself |
| test.py:670:9:670:14 | ControlFlowNode for SINK_F | Node steps to itself |
| test.py:678:9:678:12 | ControlFlowNode for SINK | Node steps to itself |
| test.py:686:9:686:12 | ControlFlowNode for SINK | Node steps to itself |
| test.py:692:5:692:8 | ControlFlowNode for SINK | Node steps to itself |
missingArgumentCall
multipleArgumentCall

View File

@@ -186,14 +186,20 @@ SINK(asyncio.run(c.coro(SOURCE))) # $ MISSING: flow
class A:
def __await__(self):
# yield SOURCE -- see https://groups.google.com/g/dev-python/c/_lrrc-vp9TI?pli=1
return (yield from asyncio.coroutine(lambda: SOURCE)())
fut = asyncio.Future()
fut.set_result(SOURCE)
yield from fut
async def agen(x):
async def atest_custom_await_impl():
a = A()
return await a
x = await a
# TODO: Figure out how to actually return something from our custom __await__
# implementation. The problem is we have to play nicely with the asyncio framework,
# which have their own expectations on what a return value from __await__ should look
# like.
assert x is None
SINK_F(x)
SINK(asyncio.run(agen(SOURCE))) # $ MISSING: flow
# Asynchronous generator functions
# A function or method which is defined using async def and which uses the yield statement is called a asynchronous generator function. Such a function, when called, returns an asynchronous iterator object which can be used in an async for statement to execute the body of the function.

View File

@@ -5,13 +5,13 @@
| test.py:42:10:42:26 | ControlFlowNode for Tuple | test.py:42:5:42:5 | SSA variable x |
| test.py:43:5:43:5 | SSA variable y | test.py:44:10:44:10 | ControlFlowNode for y |
| test.py:43:9:43:12 | ControlFlowNode for Subscript | test.py:43:5:43:5 | SSA variable y |
| test.py:187:1:187:53 | GSSA Variable SINK | test.py:189:5:189:8 | ControlFlowNode for SINK |
| test.py:187:1:187:53 | GSSA Variable SOURCE | test.py:188:25:188:30 | ControlFlowNode for SOURCE |
| test.py:188:5:188:5 | SSA variable x | test.py:189:10:189:10 | ControlFlowNode for x |
| test.py:188:9:188:68 | ControlFlowNode for .0 | test.py:188:9:188:68 | SSA variable .0 |
| test.py:188:9:188:68 | ControlFlowNode for ListComp | test.py:188:5:188:5 | SSA variable x |
| test.py:188:9:188:68 | SSA variable .0 | test.py:188:9:188:68 | ControlFlowNode for .0 |
| test.py:188:16:188:16 | SSA variable v | test.py:188:45:188:45 | ControlFlowNode for v |
| test.py:188:40:188:40 | SSA variable u | test.py:188:56:188:56 | ControlFlowNode for u |
| test.py:188:51:188:51 | SSA variable z | test.py:188:67:188:67 | ControlFlowNode for z |
| test.py:188:62:188:62 | SSA variable y | test.py:188:10:188:10 | ControlFlowNode for y |
| test.py:208:1:208:53 | GSSA Variable SINK | test.py:210:5:210:8 | ControlFlowNode for SINK |
| test.py:208:1:208:53 | GSSA Variable SOURCE | test.py:209:25:209:30 | ControlFlowNode for SOURCE |
| test.py:209:5:209:5 | SSA variable x | test.py:210:10:210:10 | ControlFlowNode for x |
| test.py:209:9:209:68 | ControlFlowNode for .0 | test.py:209:9:209:68 | SSA variable .0 |
| test.py:209:9:209:68 | ControlFlowNode for ListComp | test.py:209:5:209:5 | SSA variable x |
| test.py:209:9:209:68 | SSA variable .0 | test.py:209:9:209:68 | ControlFlowNode for .0 |
| test.py:209:16:209:16 | SSA variable v | test.py:209:45:209:45 | ControlFlowNode for v |
| test.py:209:40:209:40 | SSA variable u | test.py:209:56:209:56 | ControlFlowNode for u |
| test.py:209:51:209:51 | SSA variable z | test.py:209:67:209:67 | ControlFlowNode for z |
| test.py:209:62:209:62 | SSA variable y | test.py:209:10:209:10 | ControlFlowNode for y |

View File

@@ -0,0 +1,73 @@
# All functions starting with "test_" should run and execute `print("OK")` exactly once.
# This can be checked by running validTest.py.
import sys
import os
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"
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):
print("OK")
else:
print("Unexpected flow", x)
def SINK_F(x):
if is_source(x):
print("Unexpected flow", x)
else:
print("OK")
# ------------------------------------------------------------------------------
# Actual tests
# ------------------------------------------------------------------------------
def test_while():
x = NONSOURCE
n = 2
while n > 0:
if n == 1:
SINK(x) #$ flow="SOURCE, l:+1 -> x"
x = SOURCE
n -= 1
class MyObj(object):
def __init__(self, foo):
self.foo = foo
def setFoo(obj, x):
obj.foo = x
def test_while_obj():
myobj = MyObj(NONSOURCE)
n = 2
while n > 0:
if n == 1:
SINK(myobj.foo) #$ flow="SOURCE, l:+1 -> myobj.foo"
setFoo(myobj, SOURCE)
n -= 1
def setAndTestFoo(obj, x, test):
if test:
# This flow is not found, if self-loops are broken at the SSA level.
SINK(obj.foo) #$ flow="SOURCE, l:+7 -> obj.foo"
obj.foo = x
def test_while_obj_sink():
myobj = MyObj(NONSOURCE)
n = 2
while n > 0:
setAndTestFoo(myobj, SOURCE, n == 1)
n -= 1

View File

@@ -120,6 +120,27 @@ def test_nested_list_display():
SINK(x[0]) #$ MISSING:flow="SOURCE, l:-1 -> x[0]"
@expects(6)
def test_list_comprehension_with_tuple_result():
# confirms that this simple case works as expected
t = (SOURCE, NONSOURCE)
SINK(t[0]) # $ flow="SOURCE, l:-1 -> t[0]"
SINK_F(t[1])
# confirms that this simple case works as expected
l1 = [(SOURCE, NONSOURCE) for _ in [1]]
SINK(l1[0][0]) # $ flow="SOURCE, l:-1 -> l1[0][0]"
SINK_F(l1[0][1])
# stops working when using reference to variable, due to variables being captured by
# the function we internally generate for the comprehension body.
s = SOURCE
ns = NONSOURCE
l3 = [(s, ns) for _ in [1]]
SINK(l3[0][0]) # $ MISSING: flow="SOURCE, l:-3 -> l3[0][0]"
SINK_F(l3[0][1])
# 6.2.6. Set displays
def test_set_display():
x = {SOURCE}
@@ -338,9 +359,9 @@ class C:
@expects(2)
def test_attribute_reference():
SINK(C.a) #$ MISSING:flow="SOURCE, l:-4 -> C.a"
SINK(C.a) # $ flow="SOURCE, l:-5 -> C.a"
c = C()
SINK(c.a) #$ MISSING:flow="SOURCE, l:-6 -> c.a"
SINK(c.a) # $ MISSING: flow="SOURCE, l:-7 -> c.a"
# overriding __getattr__ should be tested by the class coverage tests
@@ -435,10 +456,14 @@ def test_and(x = True):
# 6.12. Assignment expressions
def test_assignment_expression():
def test_assignment_expression_flow_lhs():
x = NONSOURCE
SINK(x := SOURCE) #$ MISSING:flow="SOURCE -> x"
if x := SOURCE:
SINK(x) #$ flow="SOURCE, l:-1 -> x"
def test_assignment_expression_flow_out():
x = NONSOURCE
SINK(x := SOURCE) #$ flow="SOURCE -> AssignExpr"
# 6.13. Conditional expressions
def test_conditional_true():
@@ -460,13 +485,13 @@ def test_conditional_false_guards():
# Condition is evaluated first, so x is SOURCE once chosen
def test_conditional_evaluation_true():
x = NONSOURCE
SINK(x if (SOURCE == (x := SOURCE)) else NONSOURCE) #$ MISSING:flow="SOURCE -> IfExp"
SINK(x if (SOURCE == (x := SOURCE)) else NONSOURCE) #$ flow="SOURCE -> IfExp"
# Condition is evaluated first, so x is SOURCE once chosen
def test_conditional_evaluation_false():
x = NONSOURCE
SINK(NONSOURCE if (NONSOURCE == (x := SOURCE)) else x) #$ MISSING:flow="SOURCE -> IfExp"
SINK(NONSOURCE if (NONSOURCE == (x := SOURCE)) else x) #$ flow="SOURCE -> IfExp"
# 6.14. Lambdas

View File

@@ -0,0 +1,31 @@
def_count
| 4 |
def
| def_use_flow.py:10:5:10:5 | Essa node definition |
| def_use_flow.py:17:11:17:11 | Essa node definition |
| def_use_flow.py:19:9:19:9 | Essa node definition |
| def_use_flow.py:21:7:21:7 | Essa node definition |
implicit_use_count
| 0 |
implicit_use
source_use_count
| 3 |
source_use
| def_use_flow.py:28:15:28:15 | ControlFlowNode for x |
| def_use_flow.py:30:13:30:13 | ControlFlowNode for x |
| def_use_flow.py:32:11:32:11 | ControlFlowNode for x |
def_use_edge_count
| 12 |
def_use_edge
| def_use_flow.py:10:5:10:5 | SSA variable x | def_use_flow.py:28:15:28:15 | ControlFlowNode for x |
| def_use_flow.py:10:5:10:5 | SSA variable x | def_use_flow.py:30:13:30:13 | ControlFlowNode for x |
| def_use_flow.py:10:5:10:5 | SSA variable x | def_use_flow.py:32:11:32:11 | ControlFlowNode for x |
| def_use_flow.py:17:11:17:11 | SSA variable x | def_use_flow.py:28:15:28:15 | ControlFlowNode for x |
| def_use_flow.py:17:11:17:11 | SSA variable x | def_use_flow.py:30:13:30:13 | ControlFlowNode for x |
| def_use_flow.py:17:11:17:11 | SSA variable x | def_use_flow.py:32:11:32:11 | ControlFlowNode for x |
| def_use_flow.py:19:9:19:9 | SSA variable x | def_use_flow.py:28:15:28:15 | ControlFlowNode for x |
| def_use_flow.py:19:9:19:9 | SSA variable x | def_use_flow.py:30:13:30:13 | ControlFlowNode for x |
| def_use_flow.py:19:9:19:9 | SSA variable x | def_use_flow.py:32:11:32:11 | ControlFlowNode for x |
| def_use_flow.py:21:7:21:7 | SSA variable x | def_use_flow.py:28:15:28:15 | ControlFlowNode for x |
| def_use_flow.py:21:7:21:7 | SSA variable x | def_use_flow.py:30:13:30:13 | ControlFlowNode for x |
| def_use_flow.py:21:7:21:7 | SSA variable x | def_use_flow.py:32:11:32:11 | ControlFlowNode for x |

View File

@@ -0,0 +1,47 @@
import python
private import semmle.python.dataflow.new.internal.DataFlowPrivate
query int def_count() {
exists(SsaSourceVariable x | x.getName() = "x" |
result = count(EssaNodeDefinition def | def.getSourceVariable() = x)
)
}
query EssaNodeDefinition def() {
exists(SsaSourceVariable x | x.getName() = "x" | result.getSourceVariable() = x)
}
query int implicit_use_count() {
exists(SsaSourceVariable x | x.getName() = "x" | result = count(x.getAnImplicitUse()))
}
query ControlFlowNode implicit_use() {
exists(SsaSourceVariable x | x.getName() = "x" | result = x.getAnImplicitUse())
}
query int source_use_count() {
exists(SsaSourceVariable x | x.getName() = "x" | result = count(x.getASourceUse()))
}
query ControlFlowNode source_use() {
exists(SsaSourceVariable x | x.getName() = "x" | result = x.getASourceUse())
}
query int def_use_edge_count() {
exists(SsaSourceVariable x | x.getName() = "x" |
result =
count(EssaVariable v, NameNode use |
v.getSourceVariable() = x and
use = x.getAUse() and
LocalFlow::defToFirstUse(v, use)
)
)
}
query predicate def_use_edge(EssaVariable v, NameNode use) {
exists(SsaSourceVariable x | x.getName() = "x" |
v.getSourceVariable() = x and
use = x.getAUse() and
LocalFlow::defToFirstUse(v, use)
)
}

View File

@@ -0,0 +1,36 @@
# This test file is inspired by
# `csharp/ql/test/library-tests/dataflow/local/UseUseExplosion.cs`
# but with `n=3` kept small, since we do have the explosion.
cond = ...
# global variables are slightly special,
# so we go into a function scope
def scope():
x = 0
if(cond > 3):
if(cond > 2):
if(cond > 1):
pass
else:
x = 1
else:
x = 2
else:
x = 3
if(cond > 3):
if(cond > 2):
if(cond > 1):
pass
else:
use(x)
else:
use(x)
else:
use(x)
def use(v):
# this could just be `pass` but we do not want it optimized away.
y = v+2

View File

@@ -1,2 +1,3 @@
missingAnnotationOnSink
testFailures
failures

View File

@@ -4,7 +4,6 @@ uniqueType
uniqueNodeLocation
missingLocation
uniqueNodeToString
missingToString
parameterCallable
localFlowIsLocal
readStepIsLocal
@@ -24,3 +23,5 @@ uniqueParameterNodeAtPosition
uniqueParameterNodePosition
uniqueContentApprox
identityLocalStep
missingArgumentCall
multipleArgumentCall

View File

@@ -1,2 +1,3 @@
missingAnnotationOnSink
testFailures
failures

View File

@@ -0,0 +1,2 @@
testFailures
failures

View File

@@ -2,11 +2,13 @@ import python
import experimental.dataflow.TestUtil.UnresolvedCalls
private import semmle.python.dataflow.new.DataFlow
class IgnoreDictMethod extends UnresolvedCallExpectations {
override predicate unresolvedCall(CallNode call) {
super.unresolvedCall(call) and
module IgnoreDictMethod implements UnresolvedCallExpectationsSig {
predicate unresolvedCall(CallNode call) {
DefaultUnresolvedCallExpectations::unresolvedCall(call) and
not any(DataFlow::MethodCallNode methodCall |
methodCall.getMethodName() in ["get", "setdefault"]
).asCfgNode() = call
}
}
import MakeUnresolvedCallExpectations<IgnoreDictMethod>

View File

@@ -4,7 +4,6 @@ uniqueType
uniqueNodeLocation
missingLocation
uniqueNodeToString
missingToString
parameterCallable
localFlowIsLocal
readStepIsLocal
@@ -24,3 +23,5 @@ uniqueParameterNodeAtPosition
uniqueParameterNodePosition
uniqueContentApprox
identityLocalStep
missingArgumentCall
multipleArgumentCall

View File

@@ -237,6 +237,124 @@ def test_field_on_compound_arg(cond_true=True, cond_false=False):
SINK(y.attr) # $ MISSING: flow
SINK_F(z.attr) # $ MISSING: flow
# ------------------------------------------------------------------------------
# Content in class attribute
# ------------------------------------------------------------------------------
class WithTuple:
my_tuple = (SOURCE, NONSOURCE)
def test_inst(self):
SINK(self.my_tuple[0]) # $ MISSING: flow
SINK_F(self.my_tuple[1])
def test_inst_no_call(self):
SINK(self.my_tuple[0]) # $ MISSING: flow
SINK_F(self.my_tuple[1])
@classmethod
def test_cm(cls):
SINK(cls.my_tuple[0]) # $ flow="SOURCE, l:-12 -> cls.my_tuple[0]"
SINK_F(cls.my_tuple[1])
@classmethod
def test_cm_no_call(cls):
SINK(cls.my_tuple[0]) # $ MISSING: flow="SOURCE, l:-8 -> cls.my_tuple[0]"
SINK_F(cls.my_tuple[1])
@expects(2*4) # $ unresolved_call=expects(..) unresolved_call=expects(..)(..)
def test_WithTuple():
SINK(WithTuple.my_tuple[0]) # $ flow="SOURCE, l:-23 -> WithTuple.my_tuple[0]"
SINK_F(WithTuple.my_tuple[1])
WithTuple.test_cm()
inst = WithTuple()
inst.test_inst()
SINK(inst.my_tuple[0]) # $ MISSING: flow
SINK_F(inst.my_tuple[1])
@expects(4) # $ unresolved_call=expects(..) unresolved_call=expects(..)(..)
def test_inst_override():
inst = WithTuple()
# setting attribute on instance does not override class attribute, it's only on the
# instance!
inst.my_tuple = (NONSOURCE, SOURCE)
SINK_F(inst.my_tuple[0])
SINK(inst.my_tuple[1]) # $ flow="SOURCE, l:-3 -> inst.my_tuple[1]"
SINK(WithTuple.my_tuple[0]) # $ flow="SOURCE, l:-46 -> WithTuple.my_tuple[0]"
SINK_F(WithTuple.my_tuple[1])
class WithTuple2:
my_tuple = (NONSOURCE,)
def set_to_source():
WithTuple2.my_tuple = (SOURCE,)
@expects(4) # $ unresolved_call=expects(..) unresolved_call=expects(..)(..)
def test_global_flow_to_class_attribute():
inst = WithTuple2()
SINK_F(WithTuple2.my_tuple[0])
SINK_F(inst.my_tuple[0])
set_to_source()
SINK(WithTuple2.my_tuple[0]) # $ MISSING: flow="SOURCE, l:-10 -> WithTuple2.my_tuple[0]"
SINK(inst.my_tuple[0]) # $ MISSING: flow="SOURCE, l:-11 -> inst.my_tuple[0]"
class Outer:
src = SOURCE
class Inner:
src = SOURCE
@expects(2) # $ unresolved_call=expects(..) unresolved_call=expects(..)(..)
def test_nested_class():
SINK(Outer.src) # $ flow="SOURCE, l:-6 -> Outer.src"
SINK(Outer.Inner.src) # $ flow="SOURCE, l:-5 -> Outer.Inner.src"
# --------------------------------------
# unique classes from functions
# --------------------------------------
def make_class():
# a fresh class is returned each time this function is called
class C:
my_tuple = (NONSOURCE,)
return C
@expects(8) # $ unresolved_call=expects(..) unresolved_call=expects(..)(..)
def test_unique_class():
# This test highlights that if we use the _ClassExpr_ itself as the target/source
# for jumpsteps, we will end up with spurious flow (that is, we will think that
# x_cls and y_cls are the same, so by updating .my_tuple on x_cls we might propagate
# that to y_cls as well -- it might not matter too much in reality, but certainly an
# interesting corner case)
x_cls = make_class()
y_cls = make_class()
assert x_cls != y_cls
x_inst = x_cls()
y_inst = y_cls()
SINK_F(x_cls.my_tuple[0])
SINK_F(x_inst.my_tuple[0])
SINK_F(y_cls.my_tuple[0])
SINK_F(y_inst.my_tuple[0])
x_cls.my_tuple = (SOURCE,)
SINK(x_cls.my_tuple[0]) # $ flow="SOURCE, l:-1 -> x_cls.my_tuple[0]"
SINK(x_inst.my_tuple[0]) # $ MISSING: flow="SOURCE, l:-2 -> x_inst.my_tuple[0]"
SINK_F(y_cls.my_tuple[0])
SINK_F(y_inst.my_tuple[0])
# ------------------------------------------------------------------------------
# Crosstalk test -- using different function based on conditional
# ------------------------------------------------------------------------------

View File

@@ -1,2 +1,2 @@
failures
testFailures
failures

View File

@@ -4,7 +4,6 @@ uniqueType
uniqueNodeLocation
missingLocation
uniqueNodeToString
missingToString
parameterCallable
localFlowIsLocal
readStepIsLocal
@@ -24,3 +23,5 @@ uniqueParameterNodeAtPosition
uniqueParameterNodePosition
uniqueContentApprox
identityLocalStep
missingArgumentCall
multipleArgumentCall

View File

@@ -1,2 +1,3 @@
missingAnnotationOnSink
testFailures
failures

View File

@@ -4,7 +4,6 @@ uniqueType
uniqueNodeLocation
missingLocation
uniqueNodeToString
missingToString
parameterCallable
localFlowIsLocal
readStepIsLocal
@@ -24,3 +23,5 @@ uniqueParameterNodeAtPosition
uniqueParameterNodePosition
uniqueContentApprox
identityLocalStep
missingArgumentCall
multipleArgumentCall

View File

@@ -64,7 +64,7 @@ class Unsafe:
def test_value_pattern():
match SOURCE:
case Unsafe.VALUE as x:
SINK(x) #$ flow="SOURCE, l:-2 -> x" MISSING: flow="SOURCE, l:-5 -> x"
SINK(x) #$ flow="SOURCE, l:-2 -> x" flow="SOURCE, l:-5 -> x"
@expects(2)
def test_sequence_pattern_tuple():

View File

@@ -0,0 +1,4 @@
argumentToEnsureNotTaintedNotMarkedAsSpurious
untaintedArgumentToEnsureTaintedNotMarkedAsMissing
testFailures
failures

View File

@@ -0,0 +1,18 @@
extensions:
- addsTo:
pack: codeql/python-all
extensible: summaryModel
data:
- ["foo", "Member[MS_identity]", "Argument[0]", "ReturnValue", "value"]
- ["foo", "Member[MS_apply_lambda]", "Argument[1]", "Argument[0].Parameter[0]", "value"]
- ["foo", "Member[MS_apply_lambda]", "Argument[0].ReturnValue", "ReturnValue", "value"]
- ["foo", "Member[MS_reversed]", "Argument[0].ListElement", "ReturnValue.ListElement", "value"]
- ["foo", "Member[MS_reversed]", "Argument[0]", "ReturnValue", "taint"]
- ["foo", "Member[MS_list_map]", "Argument[1].ListElement", "Argument[0].Parameter[0]", "value"]
- ["foo", "Member[MS_list_map]", "Argument[0].ReturnValue", "ReturnValue.ListElement", "value"]
- ["foo", "Member[MS_list_map]", "Argument[1]", "ReturnValue", "taint"]
- ["foo", "Member[MS_append_to_list]", "Argument[0].ListElement", "ReturnValue.ListElement", "value"]
- ["foo", "Member[MS_append_to_list]", "Argument[1]", "ReturnValue.ListElement", "value"]
- ["foo", "Member[MS_append_to_list]", "Argument[0]", "ReturnValue", "taint"]
- ["foo", "Member[MS_append_to_list]", "Argument[1]", "ReturnValue", "taint"]
- ["json", "Member[MS_loads]", "Argument[0]", "ReturnValue", "taint"]

View File

@@ -0,0 +1,3 @@
import python
import experimental.meta.InlineTaintTest
import MakeInlineTaintTest<TestTaintTrackingConfig>

View File

@@ -0,0 +1,3 @@
missingAnnotationOnSink
testFailures
failures

View File

@@ -0,0 +1,18 @@
extensions:
- addsTo:
pack: codeql/python-all
extensible: summaryModel
data:
- ["foo", "Member[MS_identity]", "Argument[0]", "ReturnValue", "value"]
- ["foo", "Member[MS_apply_lambda]", "Argument[1]", "Argument[0].Parameter[0]", "value"]
- ["foo", "Member[MS_apply_lambda]", "Argument[0].ReturnValue", "ReturnValue", "value"]
- ["foo", "Member[MS_reversed]", "Argument[0].ListElement", "ReturnValue.ListElement", "value"]
- ["foo", "Member[MS_reversed]", "Argument[0]", "ReturnValue", "taint"]
- ["foo", "Member[MS_list_map]", "Argument[1].ListElement", "Argument[0].Parameter[0]", "value"]
- ["foo", "Member[MS_list_map]", "Argument[0].ReturnValue", "ReturnValue.ListElement", "value"]
- ["foo", "Member[MS_list_map]", "Argument[1]", "ReturnValue", "taint"]
- ["foo", "Member[MS_append_to_list]", "Argument[0].ListElement", "ReturnValue.ListElement", "value"]
- ["foo", "Member[MS_append_to_list]", "Argument[1]", "ReturnValue.ListElement", "value"]
- ["foo", "Member[MS_append_to_list]", "Argument[0]", "ReturnValue", "taint"]
- ["foo", "Member[MS_append_to_list]", "Argument[1]", "ReturnValue", "taint"]
- ["json", "Member[MS_loads]", "Argument[0]", "ReturnValue", "taint"]

View File

@@ -0,0 +1,2 @@
import python
import experimental.dataflow.TestUtil.NormalDataflowTest

View File

@@ -0,0 +1,122 @@
import sys
import os
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"
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):
print("OK")
else:
print("Unexpected flow", x)
def SINK_F(x):
if is_source(x):
print("Unexpected flow", x)
else:
print("OK")
ensure_tainted = ensure_not_tainted = print
TAINTED_STRING = "TAINTED_STRING"
from foo import MS_identity, MS_apply_lambda, MS_reversed, MS_list_map, MS_append_to_list
# Simple summary
via_identity = MS_identity(SOURCE)
SINK(via_identity) # $ flow="SOURCE, l:-1 -> via_identity"
# Lambda summary
via_lambda = MS_apply_lambda(lambda x: [x], SOURCE)
SINK(via_lambda[0]) # $ flow="SOURCE, l:-1 -> via_lambda[0]"
# A lambda that breaks the flow
not_via_lambda = MS_apply_lambda(lambda x: 1, SOURCE)
SINK_F(not_via_lambda)
# Collection summaries
via_reversed = MS_reversed([SOURCE])
SINK(via_reversed[0]) # $ flow="SOURCE, l:-1 -> via_reversed[0]"
tainted_list = MS_reversed(TAINTED_LIST)
ensure_tainted(
tainted_list, # $ tainted
tainted_list[0], # $ tainted
)
# Complex summaries
def box(x):
return [x]
via_map = MS_list_map(box, [SOURCE])
SINK(via_map[0][0]) # $ flow="SOURCE, l:-1 -> via_map[0][0]"
tainted_mapped = MS_list_map(box, TAINTED_LIST)
ensure_tainted(
tainted_mapped, # $ tainted
tainted_mapped[0][0], # $ tainted
)
def explicit_identity(x):
return x
via_map_explicit = MS_list_map(explicit_identity, [SOURCE])
SINK(via_map_explicit[0]) # $ flow="SOURCE, l:-1 -> via_map_explicit[0]"
tainted_mapped_explicit = MS_list_map(explicit_identity, TAINTED_LIST)
ensure_tainted(
tainted_mapped_explicit, # $ tainted
tainted_mapped_explicit[0], # $ tainted
)
via_map_summary = MS_list_map(MS_identity, [SOURCE])
SINK(via_map_summary[0]) # $ flow="SOURCE, l:-1 -> via_map_summary[0]"
tainted_mapped_summary = MS_list_map(MS_identity, TAINTED_LIST)
ensure_tainted(
tainted_mapped_summary, # $ tainted
tainted_mapped_summary[0], # $ tainted
)
via_append_el = MS_append_to_list([], SOURCE)
SINK(via_append_el[0]) # $ flow="SOURCE, l:-1 -> via_append_el[0]"
tainted_list_el = MS_append_to_list([], TAINTED_STRING)
ensure_tainted(
tainted_list_el, # $ tainted
tainted_list_el[0], # $ tainted
)
via_append = MS_append_to_list([SOURCE], NONSOURCE)
SINK(via_append[0]) # $ flow="SOURCE, l:-1 -> via_append[0]"
tainted_list_implicit = MS_append_to_list(TAINTED_LIST, NONSOURCE)
ensure_tainted(
tainted_list, # $ tainted
tainted_list[0], # $ tainted
)
# Modeled flow-summary is not value preserving
from json import MS_loads as json_loads
# so no data-flow
SINK_F(json_loads(SOURCE))
SINK_F(json_loads(SOURCE)[0])
# but has taint-flow
tainted_resultlist = json_loads(TAINTED_STRING)
ensure_tainted(
tainted_resultlist, # $ tainted
tainted_resultlist[0], # $ tainted
)

View File

@@ -0,0 +1,2 @@
testFailures
failures

View File

@@ -4,26 +4,22 @@ import experimental.dataflow.TestUtil.FlowTest
private import semmle.python.dataflow.new.internal.PrintNode
private import semmle.python.dataflow.new.internal.DataFlowPrivate as DP
class ImportTimeLocalFlowTest extends FlowTest {
ImportTimeLocalFlowTest() { this = "ImportTimeLocalFlowTest" }
module ImportTimeLocalFlowTest implements FlowTestSig {
string flowTag() { result = "importTimeFlow" }
override string flowTag() { result = "importTimeFlow" }
override predicate relevantFlow(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
predicate relevantFlow(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
nodeFrom.getLocation().getFile().getBaseName() = "multiphase.py" and
// results are displayed next to `nodeTo`, so we need a line to write on
nodeTo.getLocation().getStartLine() > 0 and
nodeTo.asVar() instanceof GlobalSsaVariable and
DP::importTimeLocalFlowStep(nodeFrom, nodeTo)
DP::PhaseDependentFlow<DP::LocalFlow::localFlowStep/2>::importTimeStep(nodeFrom, nodeTo)
}
}
class RuntimeLocalFlowTest extends FlowTest {
RuntimeLocalFlowTest() { this = "RuntimeLocalFlowTest" }
module RuntimeLocalFlowTest implements FlowTestSig {
string flowTag() { result = "runtimeFlow" }
override string flowTag() { result = "runtimeFlow" }
override predicate relevantFlow(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
predicate relevantFlow(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
nodeFrom.getLocation().getFile().getBaseName() = "multiphase.py" and
// results are displayed next to `nodeTo`, so we need a line to write on
nodeTo.getLocation().getStartLine() > 0 and
@@ -34,3 +30,5 @@ class RuntimeLocalFlowTest extends FlowTest {
DP::runtimeJumpStep(nodeFrom, nodeTo)
}
}
import MakeTest<MergeTests<MakeTestSig<ImportTimeLocalFlowTest>, MakeTestSig<RuntimeLocalFlowTest>>>

View File

@@ -0,0 +1,2 @@
testFailures
failures

View File

@@ -0,0 +1,34 @@
/**
* @kind path-problem
*/
import python
import semmle.python.dataflow.new.DataFlow
import semmle.python.dataflow.new.TaintTracking
import experimental.dataflow.testConfig
import TestUtilities.InlineExpectationsTest
module TestTaintFlow = TaintTracking::Global<TestConfig>;
module PathNodeTest implements TestSig {
string getARelevantTag() { result = "path-node" }
predicate hasActualResult(Location location, string element, string tag, string value) {
exists(TestTaintFlow::PathNode pn |
location = pn.getNode().getLocation() and
tag = "path-node" and
value = "" and
element = pn.toString()
)
}
}
import MakeTest<PathNodeTest>
// running the query to inspect the results can be quite nice!
// just uncomment these lines!
// import TestTaintFlow::PathGraph
// from TestTaintFlow::PathNode source, TestTaintFlow::PathNode sink
// where TestTaintFlow::flowPath(source, sink)
// select sink.getNode(), source, sink,
// sink.getNode().getEnclosingCallable().toString() + ": --> " +
// sink.getNode().getLocation().getStartLine().toString()

View File

@@ -0,0 +1,96 @@
def assign():
x = SOURCE # $ path-node
y = x # $ path-node
SINK(y) # $ path-node
def aug_assign():
x = SOURCE # $ path-node
z = ""
z += x # $ path-node
SINK(z) # $ path-node
def dont_use_rhs(cond):
# like noted in the original Ruby PR: https://github.com/github/codeql/pull/12566
x = SOURCE # $ path-node
if cond:
y = x
SINK(x) # $ path-node
def flow_through_function():
def identify(x): # $ path-node
return x # $ path-node
x = SOURCE # $ path-node
y = identify(x) # $ path-node
SINK(y) # $ path-node
def attribute():
class X: pass
x = X()
x.attr = SOURCE # $ path-node
y = x # $ path-node
SINK(y.attr) # $ path-node
def list_loop():
x = SOURCE # $ path-node
l = list()
l.append(x) # $ path-node
for y in l: # $ path-node
SINK(y) # $ path-node
def list_index():
x = SOURCE # $ path-node
l = list()
l.append(x) # $ path-node
z = l[0] # $ path-node
SINK(z) # $ path-node
def test_tuple():
x = SOURCE # $ path-node
y = ((x, 1), 2) # $ path-node
(z, _), _ = y # $ path-node
SINK(z) # $ path-node
def test_with():
x = SOURCE # $ path-node
with x as y: # $ path-node
SINK(y) # $ path-node
def test_match():
x = SOURCE # $ path-node
match x:
case y: # $ path-node
SINK(y) # $ path-node

View File

@@ -4,7 +4,6 @@ uniqueType
uniqueNodeLocation
missingLocation
uniqueNodeToString
missingToString
parameterCallable
localFlowIsLocal
readStepIsLocal
@@ -24,3 +23,5 @@ uniqueParameterNodeAtPosition
uniqueParameterNodePosition
uniqueContentApprox
identityLocalStep
missingArgumentCall
multipleArgumentCall

View File

@@ -4,7 +4,6 @@ uniqueType
uniqueNodeLocation
missingLocation
uniqueNodeToString
missingToString
parameterCallable
localFlowIsLocal
readStepIsLocal
@@ -24,3 +23,5 @@ uniqueParameterNodeAtPosition
uniqueParameterNodePosition
uniqueContentApprox
identityLocalStep
missingArgumentCall
multipleArgumentCall

View File

@@ -9,5 +9,5 @@ import python
import experimental.dataflow.testConfig
from DataFlow::Node source, DataFlow::Node sink
where exists(TestConfiguration cfg | cfg.hasFlow(source, sink))
where TestFlow::flow(source, sink)
select source, sink

View File

@@ -1 +0,0 @@
| pointsto_regressions.py:15:18:15:21 | ControlFlowNode for self | pointsto_regressions.py:19:9:19:12 | ControlFlowNode for self |

View File

@@ -1,2 +1,2 @@
failures
testFailures
failures

View File

@@ -4,7 +4,6 @@ uniqueType
uniqueNodeLocation
missingLocation
uniqueNodeToString
missingToString
parameterCallable
localFlowIsLocal
readStepIsLocal
@@ -24,3 +23,5 @@ uniqueParameterNodeAtPosition
uniqueParameterNodePosition
uniqueContentApprox
identityLocalStep
missingArgumentCall
multipleArgumentCall

View File

@@ -33,5 +33,5 @@ query predicate jumpStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
query predicate essaFlowStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
os_import(nodeFrom) and
DataFlowPrivate::EssaFlow::essaFlowStep(nodeFrom, nodeTo)
DataFlowPrivate::LocalFlow::localFlowStep(nodeFrom, nodeTo)
}

View File

@@ -0,0 +1 @@
# an empty file, since we want the test to run on an empty db

View File

@@ -0,0 +1,8 @@
import python
import semmle.python.dataflow.new.FlowSummary
import semmle.python.dataflow.new.internal.FlowSummaryImpl
query predicate invalidSpecComponent(SummarizedCallable sc, string s, string c) {
(sc.propagatesFlowExt(s, _, _) or sc.propagatesFlowExt(_, s, _)) and
Private::External::invalidSpecComponent(s, c)
}

View File

@@ -0,0 +1,11 @@
import python
import semmle.python.dataflow.new.FlowSummary
import semmle.python.dataflow.new.internal.FlowSummaryImpl
from SummarizedCallable sc, string s, string c, string attr
where
(sc.propagatesFlowExt(s, _, _) or sc.propagatesFlowExt(_, s, _)) and
Private::External::invalidSpecComponent(s, c) and
c = "Attribute[" + attr + "]"
select "The attribute \"" + attr +
"\" is not a valid TAttributeContent, please add it to the hardcoded list of TAttributeContent in the dataflow library."

View File

@@ -1,2 +1,3 @@
missingAnnotationOnSink
testFailures
failures

View File

@@ -60,7 +60,7 @@ private class SummarizedCallableApplyLambda extends SummarizedCallable {
}
private class SummarizedCallableReversed extends SummarizedCallable {
SummarizedCallableReversed() { this = "reversed" }
SummarizedCallableReversed() { this = "list_reversed" }
override DataFlow::CallCfgNode getACall() {
result.getFunction().asCfgNode().(NameNode).getId() = this

View File

@@ -1,41 +1,55 @@
edges
| summaries.py:32:11:32:26 | ControlFlowNode for identity() | summaries.py:33:6:33:12 | ControlFlowNode for tainted |
| summaries.py:32:1:32:7 | GSSA Variable tainted | summaries.py:33:6:33:12 | ControlFlowNode for tainted |
| summaries.py:32:11:32:26 | ControlFlowNode for identity() | summaries.py:32:1:32:7 | GSSA Variable tainted |
| summaries.py:32:20:32:25 | ControlFlowNode for SOURCE | summaries.py:32:11:32:26 | ControlFlowNode for identity() |
| summaries.py:36:18:36:54 | ControlFlowNode for apply_lambda() | summaries.py:37:6:37:19 | ControlFlowNode for tainted_lambda |
| summaries.py:36:1:36:14 | GSSA Variable tainted_lambda | summaries.py:37:6:37:19 | ControlFlowNode for tainted_lambda |
| summaries.py:36:18:36:54 | ControlFlowNode for apply_lambda() | summaries.py:36:1:36:14 | GSSA Variable tainted_lambda |
| summaries.py:36:48:36:53 | ControlFlowNode for SOURCE | summaries.py:36:18:36:54 | ControlFlowNode for apply_lambda() |
| summaries.py:44:16:44:33 | ControlFlowNode for reversed() | summaries.py:45:6:45:20 | ControlFlowNode for Subscript |
| summaries.py:44:16:44:33 | ControlFlowNode for reversed() [List element] | summaries.py:45:6:45:17 | ControlFlowNode for tainted_list [List element] |
| summaries.py:44:1:44:12 | GSSA Variable tainted_list | summaries.py:45:6:45:20 | ControlFlowNode for Subscript |
| summaries.py:44:1:44:12 | GSSA Variable tainted_list [List element] | summaries.py:45:6:45:17 | ControlFlowNode for tainted_list [List element] |
| summaries.py:44:16:44:33 | ControlFlowNode for reversed() | summaries.py:44:1:44:12 | GSSA Variable tainted_list |
| summaries.py:44:16:44:33 | ControlFlowNode for reversed() [List element] | summaries.py:44:1:44:12 | GSSA Variable tainted_list [List element] |
| summaries.py:44:25:44:32 | ControlFlowNode for List | summaries.py:44:16:44:33 | ControlFlowNode for reversed() |
| summaries.py:44:25:44:32 | ControlFlowNode for List [List element] | summaries.py:44:16:44:33 | ControlFlowNode for reversed() [List element] |
| summaries.py:44:26:44:31 | ControlFlowNode for SOURCE | summaries.py:44:25:44:32 | ControlFlowNode for List |
| summaries.py:44:26:44:31 | ControlFlowNode for SOURCE | summaries.py:44:25:44:32 | ControlFlowNode for List [List element] |
| summaries.py:45:6:45:17 | ControlFlowNode for tainted_list [List element] | summaries.py:45:6:45:20 | ControlFlowNode for Subscript |
| summaries.py:51:18:51:46 | ControlFlowNode for list_map() [List element] | summaries.py:52:6:52:19 | ControlFlowNode for tainted_mapped [List element] |
| summaries.py:51:1:51:14 | GSSA Variable tainted_mapped [List element] | summaries.py:52:6:52:19 | ControlFlowNode for tainted_mapped [List element] |
| summaries.py:51:18:51:46 | ControlFlowNode for list_map() [List element] | summaries.py:51:1:51:14 | GSSA Variable tainted_mapped [List element] |
| summaries.py:51:38:51:45 | ControlFlowNode for List [List element] | summaries.py:51:18:51:46 | ControlFlowNode for list_map() [List element] |
| summaries.py:51:39:51:44 | ControlFlowNode for SOURCE | summaries.py:51:38:51:45 | ControlFlowNode for List [List element] |
| summaries.py:52:6:52:19 | ControlFlowNode for tainted_mapped [List element] | summaries.py:52:6:52:22 | ControlFlowNode for Subscript |
| summaries.py:57:27:57:63 | ControlFlowNode for list_map() [List element] | summaries.py:58:6:58:28 | ControlFlowNode for tainted_mapped_explicit [List element] |
| summaries.py:57:1:57:23 | GSSA Variable tainted_mapped_explicit [List element] | summaries.py:58:6:58:28 | ControlFlowNode for tainted_mapped_explicit [List element] |
| summaries.py:57:27:57:63 | ControlFlowNode for list_map() [List element] | summaries.py:57:1:57:23 | GSSA Variable tainted_mapped_explicit [List element] |
| summaries.py:57:55:57:62 | ControlFlowNode for List [List element] | summaries.py:57:27:57:63 | ControlFlowNode for list_map() [List element] |
| summaries.py:57:56:57:61 | ControlFlowNode for SOURCE | summaries.py:57:55:57:62 | ControlFlowNode for List [List element] |
| summaries.py:58:6:58:28 | ControlFlowNode for tainted_mapped_explicit [List element] | summaries.py:58:6:58:31 | ControlFlowNode for Subscript |
| summaries.py:60:26:60:53 | ControlFlowNode for list_map() [List element] | summaries.py:61:6:61:27 | ControlFlowNode for tainted_mapped_summary [List element] |
| summaries.py:60:1:60:22 | GSSA Variable tainted_mapped_summary [List element] | summaries.py:61:6:61:27 | ControlFlowNode for tainted_mapped_summary [List element] |
| summaries.py:60:26:60:53 | ControlFlowNode for list_map() [List element] | summaries.py:60:1:60:22 | GSSA Variable tainted_mapped_summary [List element] |
| summaries.py:60:45:60:52 | ControlFlowNode for List [List element] | summaries.py:60:26:60:53 | ControlFlowNode for list_map() [List element] |
| summaries.py:60:46:60:51 | ControlFlowNode for SOURCE | summaries.py:60:45:60:52 | ControlFlowNode for List [List element] |
| summaries.py:61:6:61:27 | ControlFlowNode for tainted_mapped_summary [List element] | summaries.py:61:6:61:30 | ControlFlowNode for Subscript |
| summaries.py:63:16:63:41 | ControlFlowNode for append_to_list() [List element] | summaries.py:64:6:64:17 | ControlFlowNode for tainted_list [List element] |
| summaries.py:63:1:63:12 | GSSA Variable tainted_list [List element] | summaries.py:64:6:64:17 | ControlFlowNode for tainted_list [List element] |
| summaries.py:63:16:63:41 | ControlFlowNode for append_to_list() [List element] | summaries.py:63:1:63:12 | GSSA Variable tainted_list [List element] |
| summaries.py:63:35:63:40 | ControlFlowNode for SOURCE | summaries.py:63:16:63:41 | ControlFlowNode for append_to_list() [List element] |
| summaries.py:64:6:64:17 | ControlFlowNode for tainted_list [List element] | summaries.py:64:6:64:20 | ControlFlowNode for Subscript |
| summaries.py:67:22:67:39 | ControlFlowNode for json_loads() [List element] | summaries.py:68:6:68:23 | ControlFlowNode for tainted_resultlist [List element] |
| summaries.py:67:1:67:18 | GSSA Variable tainted_resultlist | summaries.py:68:6:68:26 | ControlFlowNode for Subscript |
| summaries.py:67:1:67:18 | GSSA Variable tainted_resultlist [List element] | summaries.py:68:6:68:23 | ControlFlowNode for tainted_resultlist [List element] |
| summaries.py:67:22:67:39 | ControlFlowNode for json_loads() [List element] | summaries.py:67:1:67:18 | GSSA Variable tainted_resultlist [List element] |
| summaries.py:67:33:67:38 | ControlFlowNode for SOURCE | summaries.py:67:1:67:18 | GSSA Variable tainted_resultlist |
| summaries.py:67:33:67:38 | ControlFlowNode for SOURCE | summaries.py:67:22:67:39 | ControlFlowNode for json_loads() [List element] |
| summaries.py:67:33:67:38 | ControlFlowNode for SOURCE | summaries.py:68:6:68:26 | ControlFlowNode for Subscript |
| summaries.py:68:6:68:23 | ControlFlowNode for tainted_resultlist [List element] | summaries.py:68:6:68:26 | ControlFlowNode for Subscript |
nodes
| summaries.py:32:1:32:7 | GSSA Variable tainted | semmle.label | GSSA Variable tainted |
| summaries.py:32:11:32:26 | ControlFlowNode for identity() | semmle.label | ControlFlowNode for identity() |
| summaries.py:32:20:32:25 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
| summaries.py:33:6:33:12 | ControlFlowNode for tainted | semmle.label | ControlFlowNode for tainted |
| summaries.py:36:1:36:14 | GSSA Variable tainted_lambda | semmle.label | GSSA Variable tainted_lambda |
| summaries.py:36:18:36:54 | ControlFlowNode for apply_lambda() | semmle.label | ControlFlowNode for apply_lambda() |
| summaries.py:36:48:36:53 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
| summaries.py:37:6:37:19 | ControlFlowNode for tainted_lambda | semmle.label | ControlFlowNode for tainted_lambda |
| summaries.py:44:1:44:12 | GSSA Variable tainted_list | semmle.label | GSSA Variable tainted_list |
| summaries.py:44:1:44:12 | GSSA Variable tainted_list [List element] | semmle.label | GSSA Variable tainted_list [List element] |
| summaries.py:44:16:44:33 | ControlFlowNode for reversed() | semmle.label | ControlFlowNode for reversed() |
| summaries.py:44:16:44:33 | ControlFlowNode for reversed() [List element] | semmle.label | ControlFlowNode for reversed() [List element] |
| summaries.py:44:25:44:32 | ControlFlowNode for List | semmle.label | ControlFlowNode for List |
@@ -43,25 +57,31 @@ nodes
| summaries.py:44:26:44:31 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
| summaries.py:45:6:45:17 | ControlFlowNode for tainted_list [List element] | semmle.label | ControlFlowNode for tainted_list [List element] |
| summaries.py:45:6:45:20 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript |
| summaries.py:51:1:51:14 | GSSA Variable tainted_mapped [List element] | semmle.label | GSSA Variable tainted_mapped [List element] |
| summaries.py:51:18:51:46 | ControlFlowNode for list_map() [List element] | semmle.label | ControlFlowNode for list_map() [List element] |
| summaries.py:51:38:51:45 | ControlFlowNode for List [List element] | semmle.label | ControlFlowNode for List [List element] |
| summaries.py:51:39:51:44 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
| summaries.py:52:6:52:19 | ControlFlowNode for tainted_mapped [List element] | semmle.label | ControlFlowNode for tainted_mapped [List element] |
| summaries.py:52:6:52:22 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript |
| summaries.py:57:1:57:23 | GSSA Variable tainted_mapped_explicit [List element] | semmle.label | GSSA Variable tainted_mapped_explicit [List element] |
| summaries.py:57:27:57:63 | ControlFlowNode for list_map() [List element] | semmle.label | ControlFlowNode for list_map() [List element] |
| summaries.py:57:55:57:62 | ControlFlowNode for List [List element] | semmle.label | ControlFlowNode for List [List element] |
| summaries.py:57:56:57:61 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
| summaries.py:58:6:58:28 | ControlFlowNode for tainted_mapped_explicit [List element] | semmle.label | ControlFlowNode for tainted_mapped_explicit [List element] |
| summaries.py:58:6:58:31 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript |
| summaries.py:60:1:60:22 | GSSA Variable tainted_mapped_summary [List element] | semmle.label | GSSA Variable tainted_mapped_summary [List element] |
| summaries.py:60:26:60:53 | ControlFlowNode for list_map() [List element] | semmle.label | ControlFlowNode for list_map() [List element] |
| summaries.py:60:45:60:52 | ControlFlowNode for List [List element] | semmle.label | ControlFlowNode for List [List element] |
| summaries.py:60:46:60:51 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
| summaries.py:61:6:61:27 | ControlFlowNode for tainted_mapped_summary [List element] | semmle.label | ControlFlowNode for tainted_mapped_summary [List element] |
| summaries.py:61:6:61:30 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript |
| summaries.py:63:1:63:12 | GSSA Variable tainted_list [List element] | semmle.label | GSSA Variable tainted_list [List element] |
| summaries.py:63:16:63:41 | ControlFlowNode for append_to_list() [List element] | semmle.label | ControlFlowNode for append_to_list() [List element] |
| summaries.py:63:35:63:40 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
| summaries.py:64:6:64:17 | ControlFlowNode for tainted_list [List element] | semmle.label | ControlFlowNode for tainted_list [List element] |
| summaries.py:64:6:64:20 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript |
| summaries.py:67:1:67:18 | GSSA Variable tainted_resultlist | semmle.label | GSSA Variable tainted_resultlist |
| summaries.py:67:1:67:18 | GSSA Variable tainted_resultlist [List element] | semmle.label | GSSA Variable tainted_resultlist [List element] |
| summaries.py:67:22:67:39 | ControlFlowNode for json_loads() [List element] | semmle.label | ControlFlowNode for json_loads() [List element] |
| summaries.py:67:33:67:38 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
| summaries.py:68:6:68:23 | ControlFlowNode for tainted_resultlist [List element] | semmle.label | ControlFlowNode for tainted_resultlist [List element] |

View File

@@ -66,3 +66,21 @@ SINK(tainted_list[0]) # $ flow="SOURCE, l:-1 -> tainted_list[0]"
from json import loads as json_loads
tainted_resultlist = json_loads(SOURCE)
SINK(tainted_resultlist[0]) # $ flow="SOURCE, l:-1 -> tainted_resultlist[0]"
# Class methods are not handled right now
class MyClass:
@staticmethod
def foo(x):
return x
def bar(self, x):
return x
through_staticmethod = apply_lambda(MyClass.foo, SOURCE)
through_staticmethod # $ MISSING: flow
mc = MyClass()
through_method = apply_lambda(mc.bar, SOURCE)
through_method # $ MISSING: flow

View File

@@ -4,7 +4,7 @@
import python
import semmle.python.dataflow.new.FlowSummary
import DataFlow::PathGraph
import TestFlow::PathGraph
import semmle.python.dataflow.new.TaintTracking
import semmle.python.dataflow.new.internal.FlowSummaryImpl
import semmle.python.ApiGraphs
@@ -16,6 +16,6 @@ query predicate invalidSpecComponent(SummarizedCallable sc, string s, string c)
Private::External::invalidSpecComponent(s, c)
}
from DataFlow::PathNode source, DataFlow::PathNode sink, TestConfiguration conf
where conf.hasFlowPath(source, sink)
from TestFlow::PathNode source, TestFlow::PathNode sink
where TestFlow::flowPath(source, sink)
select sink, source, sink, "$@", source, source.toString()

View File

@@ -4,7 +4,6 @@ uniqueType
uniqueNodeLocation
missingLocation
uniqueNodeToString
missingToString
parameterCallable
localFlowIsLocal
readStepIsLocal
@@ -24,3 +23,5 @@ uniqueParameterNodeAtPosition
uniqueParameterNodePosition
uniqueContentApprox
identityLocalStep
missingArgumentCall
multipleArgumentCall

View File

@@ -1,3 +1,4 @@
argumentToEnsureNotTaintedNotMarkedAsSpurious
untaintedArgumentToEnsureTaintedNotMarkedAsMissing
testFailures
failures

View File

@@ -1,6 +1,12 @@
import experimental.meta.InlineTaintTest
import semmle.python.dataflow.new.BarrierGuards
class CustomSanitizerOverrides extends TestTaintTrackingConfiguration {
override predicate isSanitizer(DataFlow::Node node) { node instanceof StringConstCompareBarrier }
module CustomSanitizerOverridesConfig implements DataFlow::ConfigSig {
predicate isSource = TestTaintTrackingConfig::isSource/1;
predicate isSink = TestTaintTrackingConfig::isSink/1;
predicate isBarrier(DataFlow::Node node) { node instanceof StringConstCompareBarrier }
}
import MakeInlineTaintTest<CustomSanitizerOverridesConfig>

View File

@@ -4,7 +4,6 @@ uniqueType
uniqueNodeLocation
missingLocation
uniqueNodeToString
missingToString
parameterCallable
localFlowIsLocal
readStepIsLocal
@@ -24,3 +23,5 @@ uniqueParameterNodeAtPosition
uniqueParameterNodePosition
uniqueContentApprox
identityLocalStep
missingArgumentCall
multipleArgumentCall

View File

@@ -1,25 +1,26 @@
argumentToEnsureNotTaintedNotMarkedAsSpurious
untaintedArgumentToEnsureTaintedNotMarkedAsMissing
testFailures
failures
isSanitizer
| TestTaintTrackingConfiguration | test.py:21:39:21:39 | ControlFlowNode for s |
| TestTaintTrackingConfiguration | test.py:34:39:34:39 | ControlFlowNode for s |
| TestTaintTrackingConfiguration | test.py:52:28:52:28 | ControlFlowNode for s |
| TestTaintTrackingConfiguration | test.py:66:10:66:29 | ControlFlowNode for emulated_escaping() |
| TestTaintTrackingConfiguration | test_logical.py:33:28:33:28 | ControlFlowNode for s |
| TestTaintTrackingConfiguration | test_logical.py:40:28:40:28 | ControlFlowNode for s |
| TestTaintTrackingConfiguration | test_logical.py:48:28:48:28 | ControlFlowNode for s |
| TestTaintTrackingConfiguration | test_logical.py:53:28:53:28 | ControlFlowNode for s |
| TestTaintTrackingConfiguration | test_logical.py:92:28:92:28 | ControlFlowNode for s |
| TestTaintTrackingConfiguration | test_logical.py:103:28:103:28 | ControlFlowNode for s |
| TestTaintTrackingConfiguration | test_logical.py:111:28:111:28 | ControlFlowNode for s |
| TestTaintTrackingConfiguration | test_logical.py:130:28:130:28 | ControlFlowNode for s |
| TestTaintTrackingConfiguration | test_logical.py:137:28:137:28 | ControlFlowNode for s |
| TestTaintTrackingConfiguration | test_logical.py:148:28:148:28 | ControlFlowNode for s |
| TestTaintTrackingConfiguration | test_logical.py:151:28:151:28 | ControlFlowNode for s |
| TestTaintTrackingConfiguration | test_logical.py:158:28:158:28 | ControlFlowNode for s |
| TestTaintTrackingConfiguration | test_logical.py:167:24:167:24 | ControlFlowNode for s |
| TestTaintTrackingConfiguration | test_logical.py:176:24:176:24 | ControlFlowNode for s |
| TestTaintTrackingConfiguration | test_logical.py:185:24:185:24 | ControlFlowNode for s |
| TestTaintTrackingConfiguration | test_logical.py:193:24:193:24 | ControlFlowNode for s |
| TestTaintTrackingConfiguration | test_reference.py:31:28:31:28 | ControlFlowNode for s |
| test.py:21:39:21:39 | ControlFlowNode for s |
| test.py:34:39:34:39 | ControlFlowNode for s |
| test.py:52:28:52:28 | ControlFlowNode for s |
| test.py:66:10:66:29 | ControlFlowNode for emulated_escaping() |
| test_logical.py:33:28:33:28 | ControlFlowNode for s |
| test_logical.py:40:28:40:28 | ControlFlowNode for s |
| test_logical.py:48:28:48:28 | ControlFlowNode for s |
| test_logical.py:53:28:53:28 | ControlFlowNode for s |
| test_logical.py:92:28:92:28 | ControlFlowNode for s |
| test_logical.py:103:28:103:28 | ControlFlowNode for s |
| test_logical.py:111:28:111:28 | ControlFlowNode for s |
| test_logical.py:130:28:130:28 | ControlFlowNode for s |
| test_logical.py:137:28:137:28 | ControlFlowNode for s |
| test_logical.py:148:28:148:28 | ControlFlowNode for s |
| test_logical.py:151:28:151:28 | ControlFlowNode for s |
| test_logical.py:158:28:158:28 | ControlFlowNode for s |
| test_logical.py:167:24:167:24 | ControlFlowNode for s |
| test_logical.py:176:24:176:24 | ControlFlowNode for s |
| test_logical.py:185:24:185:24 | ControlFlowNode for s |
| test_logical.py:193:24:193:24 | ControlFlowNode for s |
| test_reference.py:31:28:31:28 | ControlFlowNode for s |

View File

@@ -12,8 +12,12 @@ predicate isUnsafeCheck(DataFlow::GuardNode g, ControlFlowNode node, boolean bra
branch = false
}
class CustomSanitizerOverrides extends TestTaintTrackingConfiguration {
override predicate isSanitizer(DataFlow::Node node) {
module CustomSanitizerOverridesConfig implements DataFlow::ConfigSig {
predicate isSource = TestTaintTrackingConfig::isSource/1;
predicate isSink = TestTaintTrackingConfig::isSink/1;
predicate isBarrier(DataFlow::Node node) {
exists(Call call |
call.getFunc().(Name).getId() = "emulated_authentication_check" and
call.getArg(0) = node.asExpr()
@@ -27,7 +31,9 @@ class CustomSanitizerOverrides extends TestTaintTrackingConfiguration {
}
}
query predicate isSanitizer(TestTaintTrackingConfiguration conf, DataFlow::Node node) {
import MakeInlineTaintTest<CustomSanitizerOverridesConfig>
query predicate isSanitizer(DataFlow::Node node) {
exists(node.getLocation().getFile().getRelativePath()) and
conf.isSanitizer(node)
CustomSanitizerOverridesConfig::isBarrier(node)
}

View File

@@ -4,7 +4,6 @@ uniqueType
uniqueNodeLocation
missingLocation
uniqueNodeToString
missingToString
parameterCallable
localFlowIsLocal
readStepIsLocal
@@ -24,3 +23,5 @@ uniqueParameterNodeAtPosition
uniqueParameterNodePosition
uniqueContentApprox
identityLocalStep
missingArgumentCall
multipleArgumentCall

View File

@@ -1,3 +1,4 @@
argumentToEnsureNotTaintedNotMarkedAsSpurious
untaintedArgumentToEnsureTaintedNotMarkedAsMissing
testFailures
failures

View File

@@ -1 +1,2 @@
import experimental.meta.InlineTaintTest
import MakeInlineTaintTest<TestTaintTrackingConfig>

View File

@@ -4,7 +4,6 @@ uniqueType
uniqueNodeLocation
missingLocation
uniqueNodeToString
missingToString
parameterCallable
localFlowIsLocal
readStepIsLocal
@@ -24,5 +23,5 @@ uniqueParameterNodeAtPosition
uniqueParameterNodePosition
uniqueContentApprox
identityLocalStep
| test_collections.py:20:9:20:22 | ControlFlowNode for ensure_tainted | Node steps to itself |
| test_unpacking.py:31:9:31:22 | ControlFlowNode for ensure_tainted | Node steps to itself |
missingArgumentCall
multipleArgumentCall

View File

@@ -1,3 +1,4 @@
argumentToEnsureNotTaintedNotMarkedAsSpurious
untaintedArgumentToEnsureTaintedNotMarkedAsMissing
testFailures
failures

View File

@@ -1 +1,2 @@
import experimental.meta.InlineTaintTest
import MakeInlineTaintTest<TestTaintTrackingConfig>

View File

@@ -4,7 +4,6 @@ uniqueType
uniqueNodeLocation
missingLocation
uniqueNodeToString
missingToString
parameterCallable
localFlowIsLocal
readStepIsLocal
@@ -24,18 +23,5 @@ uniqueParameterNodeAtPosition
uniqueParameterNodePosition
uniqueContentApprox
identityLocalStep
| test_async.py:48:9:48:22 | ControlFlowNode for ensure_tainted | Node steps to itself |
| test_collections.py:64:10:64:21 | ControlFlowNode for tainted_list | Node steps to itself |
| test_collections.py:71:9:71:22 | ControlFlowNode for ensure_tainted | Node steps to itself |
| test_collections.py:73:9:73:22 | ControlFlowNode for ensure_tainted | Node steps to itself |
| test_collections.py:88:10:88:21 | ControlFlowNode for tainted_list | Node steps to itself |
| test_collections.py:89:10:89:23 | ControlFlowNode for TAINTED_STRING | Node steps to itself |
| test_collections.py:97:9:97:22 | ControlFlowNode for ensure_tainted | Node steps to itself |
| test_collections.py:99:9:99:22 | ControlFlowNode for ensure_tainted | Node steps to itself |
| test_collections.py:112:9:112:22 | ControlFlowNode for ensure_tainted | Node steps to itself |
| test_collections.py:114:9:114:22 | ControlFlowNode for ensure_tainted | Node steps to itself |
| test_collections.py:147:9:147:22 | ControlFlowNode for ensure_tainted | Node steps to itself |
| test_collections.py:149:9:149:22 | ControlFlowNode for ensure_tainted | Node steps to itself |
| test_collections.py:246:9:246:15 | ControlFlowNode for my_dict | Node steps to itself |
| test_collections.py:246:22:246:33 | ControlFlowNode for tainted_dict | Node steps to itself |
| test_for.py:24:9:24:22 | ControlFlowNode for ensure_tainted | Node steps to itself |
missingArgumentCall
multipleArgumentCall

View File

@@ -1,3 +1,4 @@
argumentToEnsureNotTaintedNotMarkedAsSpurious
untaintedArgumentToEnsureTaintedNotMarkedAsMissing
testFailures
failures

View File

@@ -1 +1,2 @@
import experimental.meta.InlineTaintTest
import MakeInlineTaintTest<TestTaintTrackingConfig>

View File

@@ -1,2 +1,3 @@
missingAnnotationOnSink
testFailures
failures

View File

@@ -4,7 +4,6 @@ uniqueType
uniqueNodeLocation
missingLocation
uniqueNodeToString
missingToString
parameterCallable
localFlowIsLocal
readStepIsLocal
@@ -24,3 +23,5 @@ uniqueParameterNodeAtPosition
uniqueParameterNodePosition
uniqueContentApprox
identityLocalStep
missingArgumentCall
multipleArgumentCall

View File

@@ -1,3 +1,4 @@
argumentToEnsureNotTaintedNotMarkedAsSpurious
untaintedArgumentToEnsureTaintedNotMarkedAsMissing
testFailures
failures

View File

@@ -1 +1,2 @@
import experimental.meta.InlineTaintTest
import MakeInlineTaintTest<TestTaintTrackingConfig>

View File

@@ -4,7 +4,6 @@ uniqueType
uniqueNodeLocation
missingLocation
uniqueNodeToString
missingToString
parameterCallable
localFlowIsLocal
readStepIsLocal
@@ -24,3 +23,5 @@ uniqueParameterNodeAtPosition
uniqueParameterNodePosition
uniqueContentApprox
identityLocalStep
missingArgumentCall
multipleArgumentCall

View File

@@ -23,10 +23,8 @@
private import python
import semmle.python.dataflow.new.DataFlow
class TestConfiguration extends DataFlow::Configuration {
TestConfiguration() { this = "TestConfiguration" }
override predicate isSource(DataFlow::Node node) {
module TestConfig implements DataFlow::ConfigSig {
predicate isSource(DataFlow::Node node) {
node.(DataFlow::CfgNode).getNode().(NameNode).getId() = "SOURCE"
or
node.(DataFlow::CfgNode).getNode().getNode().(StrConst).getS() = "source"
@@ -37,7 +35,7 @@ class TestConfiguration extends DataFlow::Configuration {
// No support for complex numbers
}
override predicate isSink(DataFlow::Node node) {
predicate isSink(DataFlow::Node node) {
exists(DataFlow::CallCfgNode call |
call.getFunction().asCfgNode().(NameNode).getId() in ["SINK", "SINK_F"] and
(node = call.getArg(_) or node = call.getArgByName(_)) and
@@ -45,5 +43,7 @@ class TestConfiguration extends DataFlow::Configuration {
)
}
override predicate isBarrierIn(DataFlow::Node node) { this.isSource(node) }
predicate isBarrierIn(DataFlow::Node node) { isSource(node) }
}
module TestFlow = DataFlow::Global<TestConfig>;

View File

@@ -24,10 +24,8 @@ private import python
import semmle.python.dataflow.new.DataFlow
import semmle.python.dataflow.new.TaintTracking
class TestConfiguration extends TaintTracking::Configuration {
TestConfiguration() { this = "TestConfiguration" }
override predicate isSource(DataFlow::Node node) {
module TestConfig implements DataFlow::ConfigSig {
predicate isSource(DataFlow::Node node) {
node.(DataFlow::CfgNode).getNode().(NameNode).getId() = "SOURCE"
or
node.(DataFlow::CfgNode).getNode().getNode().(StrConst).getS() = "source"
@@ -38,12 +36,14 @@ class TestConfiguration extends TaintTracking::Configuration {
// No support for complex numbers
}
override predicate isSink(DataFlow::Node node) {
predicate isSink(DataFlow::Node node) {
exists(CallNode call |
call.getFunction().(NameNode).getId() in ["SINK", "SINK_F"] and
node.(DataFlow::CfgNode).getNode() = call.getAnArg()
)
}
override predicate isSanitizerIn(DataFlow::Node node) { this.isSource(node) }
predicate isBarrierIn(DataFlow::Node node) { isSource(node) }
}
module TestFlow = TaintTracking::Global<TestConfig>;

View File

@@ -0,0 +1,189 @@
private import python
private import semmle.python.dataflow.new.FlowSummary
private import semmle.python.ApiGraphs
/**
* This module ensures that the `callStep` predicate in
* our type tracker implementation does not refer to the
* `getACall` predicate on `SummarizedCallable`.
*/
module RecursionGuard {
private import semmle.python.dataflow.new.internal.TypeTrackerSpecific as TT
private class RecursionGuard extends SummarizedCallable {
RecursionGuard() { this = "TypeTrackingSummariesRecursionGuard" }
override DataFlow::CallCfgNode getACall() {
result.getFunction().asCfgNode().(NameNode).getId() = this and
(TT::callStep(_, _) implies any())
}
override DataFlow::CallCfgNode getACallSimple() { none() }
override DataFlow::ArgumentNode getACallback() { result.asExpr().(Name).getId() = this }
}
predicate test(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
TT::levelStepNoCall(nodeFrom, nodeTo)
}
}
private class SummarizedCallableIdentity extends SummarizedCallable {
SummarizedCallableIdentity() { this = "TTS_identity" }
override DataFlow::CallCfgNode getACall() { none() }
override DataFlow::CallCfgNode getACallSimple() {
result.getFunction().asCfgNode().(NameNode).getId() = this
}
override DataFlow::ArgumentNode getACallback() { result.asExpr().(Name).getId() = this }
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
input = "Argument[0]" and
output = "ReturnValue" and
preservesValue = true
}
}
// For lambda flow to work, implement lambdaCall and lambdaCreation
private class SummarizedCallableApplyLambda extends SummarizedCallable {
SummarizedCallableApplyLambda() { this = "TTS_apply_lambda" }
override DataFlow::CallCfgNode getACall() { none() }
override DataFlow::CallCfgNode getACallSimple() {
result.getFunction().asCfgNode().(NameNode).getId() = this
}
override DataFlow::ArgumentNode getACallback() { result.asExpr().(Name).getId() = this }
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
input = "Argument[1]" and
output = "Argument[0].Parameter[0]" and
preservesValue = true
or
input = "Argument[0].ReturnValue" and
output = "ReturnValue" and
preservesValue = true
}
}
private class SummarizedCallableReversed extends SummarizedCallable {
SummarizedCallableReversed() { this = "TTS_reversed" }
override DataFlow::CallCfgNode getACall() { none() }
override DataFlow::CallCfgNode getACallSimple() {
result.getFunction().asCfgNode().(NameNode).getId() = this
}
override DataFlow::ArgumentNode getACallback() { result.asExpr().(Name).getId() = this }
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
input = "Argument[0].ListElement" and
output = "ReturnValue.ListElement" and
preservesValue = true
}
}
private class SummarizedCallableMap extends SummarizedCallable {
SummarizedCallableMap() { this = "TTS_list_map" }
override DataFlow::CallCfgNode getACall() { none() }
override DataFlow::CallCfgNode getACallSimple() {
result.getFunction().asCfgNode().(NameNode).getId() = this
}
override DataFlow::ArgumentNode getACallback() { result.asExpr().(Name).getId() = this }
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
input = "Argument[1].ListElement" and
output = "Argument[0].Parameter[0]" and
preservesValue = true
or
input = "Argument[0].ReturnValue" and
output = "ReturnValue.ListElement" and
preservesValue = true
}
}
private class SummarizedCallableAppend extends SummarizedCallable {
SummarizedCallableAppend() { this = "TTS_append_to_list" }
override DataFlow::CallCfgNode getACall() { none() }
override DataFlow::CallCfgNode getACallSimple() {
result.getFunction().asCfgNode().(NameNode).getId() = this
}
override DataFlow::ArgumentNode getACallback() { result.asExpr().(Name).getId() = this }
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
input = "Argument[0]" and
output = "ReturnValue" and
preservesValue = false
or
input = "Argument[1]" and
output = "ReturnValue.ListElement" and
preservesValue = true
}
}
private class SummarizedCallableJsonLoads extends SummarizedCallable {
SummarizedCallableJsonLoads() { this = "TTS_json.loads" }
override DataFlow::CallCfgNode getACall() {
result = API::moduleImport("json").getMember("loads").getACall()
}
override DataFlow::CallCfgNode getACallSimple() { none() }
override DataFlow::ArgumentNode getACallback() {
result = API::moduleImport("json").getMember("loads").getAValueReachableFromSource()
}
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
input = "Argument[0]" and
output = "ReturnValue.ListElement" and
preservesValue = true
}
}
// read and store
private class SummarizedCallableReadSecret extends SummarizedCallable {
SummarizedCallableReadSecret() { this = "TTS_read_secret" }
override DataFlow::CallCfgNode getACall() { none() }
override DataFlow::CallCfgNode getACallSimple() {
result.getFunction().asCfgNode().(NameNode).getId() = this
}
override DataFlow::ArgumentNode getACallback() { result.asExpr().(Name).getId() = this }
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
input = "Argument[0].Attribute[secret]" and
output = "ReturnValue" and
preservesValue = true
}
}
private class SummarizedCallableSetSecret extends SummarizedCallable {
SummarizedCallableSetSecret() { this = "TTS_set_secret" }
override DataFlow::CallCfgNode getACall() { none() }
override DataFlow::CallCfgNode getACallSimple() {
result.getFunction().asCfgNode().(NameNode).getId() = this
}
override DataFlow::ArgumentNode getACallback() { result.asExpr().(Name).getId() = this }
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
input = "Argument[1]" and
output = "Argument[0].Attribute[secret]" and
preservesValue = true
}
}

View File

@@ -0,0 +1,78 @@
import sys
import os
# Simple summary
tainted = TTS_identity(tracked) # $ tracked
tainted # $ tracked
# Lambda summary
# I think the missing result is expected because type tracking
# is not allowed to flow back out of a call.
tainted_lambda = TTS_apply_lambda(lambda x: x, tracked) # $ tracked
tainted_lambda # $ MISSING: tracked
# A lambda that directly introduces taint
bad_lambda = TTS_apply_lambda(lambda x: tracked, 1) # $ tracked
bad_lambda # $ tracked
# A lambda that breaks the flow
untainted_lambda = TTS_apply_lambda(lambda x: 1, tracked) # $ tracked
untainted_lambda
# Collection summaries
tainted_list = TTS_reversed([tracked]) # $ tracked
tl = tainted_list[0]
tl # $ MISSING: tracked
# Complex summaries
def add_colon(x):
return x + ":"
tainted_mapped = TTS_list_map(add_colon, [tracked]) # $ tracked
tm = tainted_mapped[0]
tm # $ MISSING: tracked
def explicit_identity(x):
return x
tainted_mapped_explicit = TTS_list_map(explicit_identity, [tracked]) # $ tracked
tainted_mapped_explicit[0] # $ MISSING: tracked
tainted_mapped_summary = TTS_list_map(identity, [tracked]) # $ tracked
tms = tainted_mapped_summary[0]
tms # $ MISSING: tracked
another_tainted_list = TTS_append_to_list([], tracked) # $ tracked
atl = another_tainted_list[0]
atl # $ MISSING: tracked
# This will not work, as the call is not found by `getACallSimple`.
from json import loads as json_loads
tainted_resultlist = json_loads(tracked) # $ tracked
tr = tainted_resultlist[0]
tr # $ MISSING: tracked
x.secret = tracked # $ tracked=secret tracked
r = TTS_read_secret(x) # $ tracked=secret tracked
r # $ tracked
y # $ tracked=secret
TTS_set_secret(y, tracked) # $ tracked tracked=secret
y.secret # $ tracked tracked=secret
# Class methods are not handled right now
class MyClass:
@staticmethod
def foo(x):
return x
def bar(self, x):
return x
through_staticmethod = TTS_apply_lambda(MyClass.foo, tracked) # $ tracked
through_staticmethod # $ MISSING: tracked
mc = MyClass()
through_method = TTS_apply_lambda(mc.bar, tracked) # $ tracked
through_method # $ MISSING: tracked

View File

@@ -0,0 +1,2 @@
testFailures
failures

View File

@@ -0,0 +1,36 @@
import python
import semmle.python.dataflow.new.DataFlow
import semmle.python.dataflow.new.TypeTracker
import TestUtilities.InlineExpectationsTest
import semmle.python.ApiGraphs
import TestSummaries
// -----------------------------------------------------------------------------
// tracked
// -----------------------------------------------------------------------------
private DataFlow::TypeTrackingNode tracked(TypeTracker t) {
t.start() and
result.asCfgNode() = any(NameNode n | n.getId() = "tracked")
or
exists(TypeTracker t2 | result = tracked(t2).track(t2, t))
}
module TrackedTest implements TestSig {
string getARelevantTag() { result = "tracked" }
predicate hasActualResult(Location location, string element, string tag, string value) {
exists(DataFlow::Node e, TypeTracker t |
exists(e.getLocation().getFile().getRelativePath()) and
e.getLocation().getStartLine() > 0 and
tracked(t).flowsTo(e) and
// Module variables have no sensible location, and hence can't be annotated.
not e instanceof DataFlow::ModuleVariableNode and
tag = "tracked" and
location = e.getLocation() and
value = t.getAttr() and
element = e.toString()
)
}
}
import MakeTest<TrackedTest>

View File

@@ -4,7 +4,6 @@ uniqueType
uniqueNodeLocation
missingLocation
uniqueNodeToString
missingToString
parameterCallable
localFlowIsLocal
readStepIsLocal
@@ -24,3 +23,5 @@ uniqueParameterNodeAtPosition
uniqueParameterNodePosition
uniqueContentApprox
identityLocalStep
missingArgumentCall
multipleArgumentCall

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