mirror of
https://github.com/github/codeql.git
synced 2026-04-25 16:55:19 +02:00
Merge branch 'main' into amammad-python-bombs
This commit is contained in:
@@ -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())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
)
|
||||
|
||||
@@ -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>>
|
||||
|
||||
@@ -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>;
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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>
|
||||
}
|
||||
|
||||
@@ -4,7 +4,6 @@ uniqueType
|
||||
uniqueNodeLocation
|
||||
missingLocation
|
||||
uniqueNodeToString
|
||||
missingToString
|
||||
parameterCallable
|
||||
localFlowIsLocal
|
||||
readStepIsLocal
|
||||
@@ -24,3 +23,5 @@ uniqueParameterNodeAtPosition
|
||||
uniqueParameterNodePosition
|
||||
uniqueContentApprox
|
||||
identityLocalStep
|
||||
missingArgumentCall
|
||||
multipleArgumentCall
|
||||
|
||||
@@ -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 |
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
testFailures
|
||||
failures
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
testFailures
|
||||
failures
|
||||
|
||||
@@ -4,7 +4,6 @@ uniqueType
|
||||
uniqueNodeLocation
|
||||
missingLocation
|
||||
uniqueNodeToString
|
||||
missingToString
|
||||
parameterCallable
|
||||
localFlowIsLocal
|
||||
readStepIsLocal
|
||||
@@ -24,3 +23,5 @@ uniqueParameterNodeAtPosition
|
||||
uniqueParameterNodePosition
|
||||
uniqueContentApprox
|
||||
identityLocalStep
|
||||
missingArgumentCall
|
||||
multipleArgumentCall
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
failures
|
||||
testFailures
|
||||
failures
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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()
|
||||
@@ -4,7 +4,6 @@ uniqueType
|
||||
uniqueNodeLocation
|
||||
missingLocation
|
||||
uniqueNodeToString
|
||||
missingToString
|
||||
parameterCallable
|
||||
localFlowIsLocal
|
||||
readStepIsLocal
|
||||
@@ -24,3 +23,5 @@ uniqueParameterNodeAtPosition
|
||||
uniqueParameterNodePosition
|
||||
uniqueContentApprox
|
||||
identityLocalStep
|
||||
missingArgumentCall
|
||||
multipleArgumentCall
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
testFailures
|
||||
failures
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
testFailures
|
||||
failures
|
||||
|
||||
@@ -1,2 +1,3 @@
|
||||
missingAnnotationOnSink
|
||||
testFailures
|
||||
failures
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
testFailures
|
||||
failures
|
||||
|
||||
@@ -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>>>
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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 |
|
||||
|
||||
73
python/ql/test/experimental/dataflow/coverage/loops.py
Normal file
73
python/ql/test/experimental/dataflow/coverage/loops.py
Normal 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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 |
|
||||
@@ -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)
|
||||
)
|
||||
}
|
||||
@@ -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
|
||||
@@ -1,2 +1,3 @@
|
||||
missingAnnotationOnSink
|
||||
testFailures
|
||||
failures
|
||||
|
||||
@@ -4,7 +4,6 @@ uniqueType
|
||||
uniqueNodeLocation
|
||||
missingLocation
|
||||
uniqueNodeToString
|
||||
missingToString
|
||||
parameterCallable
|
||||
localFlowIsLocal
|
||||
readStepIsLocal
|
||||
@@ -24,3 +23,5 @@ uniqueParameterNodeAtPosition
|
||||
uniqueParameterNodePosition
|
||||
uniqueContentApprox
|
||||
identityLocalStep
|
||||
missingArgumentCall
|
||||
multipleArgumentCall
|
||||
|
||||
@@ -1,2 +1,3 @@
|
||||
missingAnnotationOnSink
|
||||
testFailures
|
||||
failures
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
testFailures
|
||||
failures
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -4,7 +4,6 @@ uniqueType
|
||||
uniqueNodeLocation
|
||||
missingLocation
|
||||
uniqueNodeToString
|
||||
missingToString
|
||||
parameterCallable
|
||||
localFlowIsLocal
|
||||
readStepIsLocal
|
||||
@@ -24,3 +23,5 @@ uniqueParameterNodeAtPosition
|
||||
uniqueParameterNodePosition
|
||||
uniqueContentApprox
|
||||
identityLocalStep
|
||||
missingArgumentCall
|
||||
multipleArgumentCall
|
||||
|
||||
@@ -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
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
failures
|
||||
testFailures
|
||||
failures
|
||||
|
||||
@@ -4,7 +4,6 @@ uniqueType
|
||||
uniqueNodeLocation
|
||||
missingLocation
|
||||
uniqueNodeToString
|
||||
missingToString
|
||||
parameterCallable
|
||||
localFlowIsLocal
|
||||
readStepIsLocal
|
||||
@@ -24,3 +23,5 @@ uniqueParameterNodeAtPosition
|
||||
uniqueParameterNodePosition
|
||||
uniqueContentApprox
|
||||
identityLocalStep
|
||||
missingArgumentCall
|
||||
multipleArgumentCall
|
||||
|
||||
@@ -1,2 +1,3 @@
|
||||
missingAnnotationOnSink
|
||||
testFailures
|
||||
failures
|
||||
|
||||
@@ -4,7 +4,6 @@ uniqueType
|
||||
uniqueNodeLocation
|
||||
missingLocation
|
||||
uniqueNodeToString
|
||||
missingToString
|
||||
parameterCallable
|
||||
localFlowIsLocal
|
||||
readStepIsLocal
|
||||
@@ -24,3 +23,5 @@ uniqueParameterNodeAtPosition
|
||||
uniqueParameterNodePosition
|
||||
uniqueContentApprox
|
||||
identityLocalStep
|
||||
missingArgumentCall
|
||||
multipleArgumentCall
|
||||
|
||||
@@ -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():
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
argumentToEnsureNotTaintedNotMarkedAsSpurious
|
||||
untaintedArgumentToEnsureTaintedNotMarkedAsMissing
|
||||
testFailures
|
||||
failures
|
||||
@@ -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"]
|
||||
@@ -0,0 +1,3 @@
|
||||
import python
|
||||
import experimental.meta.InlineTaintTest
|
||||
import MakeInlineTaintTest<TestTaintTrackingConfig>
|
||||
@@ -0,0 +1,3 @@
|
||||
missingAnnotationOnSink
|
||||
testFailures
|
||||
failures
|
||||
@@ -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"]
|
||||
@@ -0,0 +1,2 @@
|
||||
import python
|
||||
import experimental.dataflow.TestUtil.NormalDataflowTest
|
||||
@@ -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
|
||||
)
|
||||
@@ -0,0 +1,2 @@
|
||||
testFailures
|
||||
failures
|
||||
|
||||
@@ -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>>>
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
testFailures
|
||||
failures
|
||||
34
python/ql/test/experimental/dataflow/path-graph/PathNodes.ql
Normal file
34
python/ql/test/experimental/dataflow/path-graph/PathNodes.ql
Normal 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()
|
||||
96
python/ql/test/experimental/dataflow/path-graph/test.py
Normal file
96
python/ql/test/experimental/dataflow/path-graph/test.py
Normal 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
|
||||
@@ -4,7 +4,6 @@ uniqueType
|
||||
uniqueNodeLocation
|
||||
missingLocation
|
||||
uniqueNodeToString
|
||||
missingToString
|
||||
parameterCallable
|
||||
localFlowIsLocal
|
||||
readStepIsLocal
|
||||
@@ -24,3 +23,5 @@ uniqueParameterNodeAtPosition
|
||||
uniqueParameterNodePosition
|
||||
uniqueContentApprox
|
||||
identityLocalStep
|
||||
missingArgumentCall
|
||||
multipleArgumentCall
|
||||
|
||||
@@ -4,7 +4,6 @@ uniqueType
|
||||
uniqueNodeLocation
|
||||
missingLocation
|
||||
uniqueNodeToString
|
||||
missingToString
|
||||
parameterCallable
|
||||
localFlowIsLocal
|
||||
readStepIsLocal
|
||||
@@ -24,3 +23,5 @@ uniqueParameterNodeAtPosition
|
||||
uniqueParameterNodePosition
|
||||
uniqueContentApprox
|
||||
identityLocalStep
|
||||
missingArgumentCall
|
||||
multipleArgumentCall
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
| pointsto_regressions.py:15:18:15:21 | ControlFlowNode for self | pointsto_regressions.py:19:9:19:12 | ControlFlowNode for self |
|
||||
@@ -1,2 +1,2 @@
|
||||
failures
|
||||
testFailures
|
||||
failures
|
||||
|
||||
@@ -4,7 +4,6 @@ uniqueType
|
||||
uniqueNodeLocation
|
||||
missingLocation
|
||||
uniqueNodeToString
|
||||
missingToString
|
||||
parameterCallable
|
||||
localFlowIsLocal
|
||||
readStepIsLocal
|
||||
@@ -24,3 +23,5 @@ uniqueParameterNodeAtPosition
|
||||
uniqueParameterNodePosition
|
||||
uniqueContentApprox
|
||||
identityLocalStep
|
||||
missingArgumentCall
|
||||
multipleArgumentCall
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
# an empty file, since we want the test to run on an empty db
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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."
|
||||
@@ -1,2 +1,3 @@
|
||||
missingAnnotationOnSink
|
||||
testFailures
|
||||
failures
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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] |
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -4,7 +4,6 @@ uniqueType
|
||||
uniqueNodeLocation
|
||||
missingLocation
|
||||
uniqueNodeToString
|
||||
missingToString
|
||||
parameterCallable
|
||||
localFlowIsLocal
|
||||
readStepIsLocal
|
||||
@@ -24,3 +23,5 @@ uniqueParameterNodeAtPosition
|
||||
uniqueParameterNodePosition
|
||||
uniqueContentApprox
|
||||
identityLocalStep
|
||||
missingArgumentCall
|
||||
multipleArgumentCall
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
argumentToEnsureNotTaintedNotMarkedAsSpurious
|
||||
untaintedArgumentToEnsureTaintedNotMarkedAsMissing
|
||||
testFailures
|
||||
failures
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -4,7 +4,6 @@ uniqueType
|
||||
uniqueNodeLocation
|
||||
missingLocation
|
||||
uniqueNodeToString
|
||||
missingToString
|
||||
parameterCallable
|
||||
localFlowIsLocal
|
||||
readStepIsLocal
|
||||
@@ -24,3 +23,5 @@ uniqueParameterNodeAtPosition
|
||||
uniqueParameterNodePosition
|
||||
uniqueContentApprox
|
||||
identityLocalStep
|
||||
missingArgumentCall
|
||||
multipleArgumentCall
|
||||
|
||||
@@ -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 |
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -4,7 +4,6 @@ uniqueType
|
||||
uniqueNodeLocation
|
||||
missingLocation
|
||||
uniqueNodeToString
|
||||
missingToString
|
||||
parameterCallable
|
||||
localFlowIsLocal
|
||||
readStepIsLocal
|
||||
@@ -24,3 +23,5 @@ uniqueParameterNodeAtPosition
|
||||
uniqueParameterNodePosition
|
||||
uniqueContentApprox
|
||||
identityLocalStep
|
||||
missingArgumentCall
|
||||
multipleArgumentCall
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
argumentToEnsureNotTaintedNotMarkedAsSpurious
|
||||
untaintedArgumentToEnsureTaintedNotMarkedAsMissing
|
||||
testFailures
|
||||
failures
|
||||
|
||||
@@ -1 +1,2 @@
|
||||
import experimental.meta.InlineTaintTest
|
||||
import MakeInlineTaintTest<TestTaintTrackingConfig>
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
argumentToEnsureNotTaintedNotMarkedAsSpurious
|
||||
untaintedArgumentToEnsureTaintedNotMarkedAsMissing
|
||||
testFailures
|
||||
failures
|
||||
|
||||
@@ -1 +1,2 @@
|
||||
import experimental.meta.InlineTaintTest
|
||||
import MakeInlineTaintTest<TestTaintTrackingConfig>
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
argumentToEnsureNotTaintedNotMarkedAsSpurious
|
||||
untaintedArgumentToEnsureTaintedNotMarkedAsMissing
|
||||
testFailures
|
||||
failures
|
||||
|
||||
@@ -1 +1,2 @@
|
||||
import experimental.meta.InlineTaintTest
|
||||
import MakeInlineTaintTest<TestTaintTrackingConfig>
|
||||
|
||||
@@ -1,2 +1,3 @@
|
||||
missingAnnotationOnSink
|
||||
testFailures
|
||||
failures
|
||||
|
||||
@@ -4,7 +4,6 @@ uniqueType
|
||||
uniqueNodeLocation
|
||||
missingLocation
|
||||
uniqueNodeToString
|
||||
missingToString
|
||||
parameterCallable
|
||||
localFlowIsLocal
|
||||
readStepIsLocal
|
||||
@@ -24,3 +23,5 @@ uniqueParameterNodeAtPosition
|
||||
uniqueParameterNodePosition
|
||||
uniqueContentApprox
|
||||
identityLocalStep
|
||||
missingArgumentCall
|
||||
multipleArgumentCall
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
argumentToEnsureNotTaintedNotMarkedAsSpurious
|
||||
untaintedArgumentToEnsureTaintedNotMarkedAsMissing
|
||||
testFailures
|
||||
failures
|
||||
|
||||
@@ -1 +1,2 @@
|
||||
import experimental.meta.InlineTaintTest
|
||||
import MakeInlineTaintTest<TestTaintTrackingConfig>
|
||||
|
||||
@@ -4,7 +4,6 @@ uniqueType
|
||||
uniqueNodeLocation
|
||||
missingLocation
|
||||
uniqueNodeToString
|
||||
missingToString
|
||||
parameterCallable
|
||||
localFlowIsLocal
|
||||
readStepIsLocal
|
||||
@@ -24,3 +23,5 @@ uniqueParameterNodeAtPosition
|
||||
uniqueParameterNodePosition
|
||||
uniqueContentApprox
|
||||
identityLocalStep
|
||||
missingArgumentCall
|
||||
multipleArgumentCall
|
||||
|
||||
@@ -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>;
|
||||
|
||||
@@ -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>;
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
@@ -0,0 +1,2 @@
|
||||
testFailures
|
||||
failures
|
||||
@@ -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>
|
||||
@@ -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
Reference in New Issue
Block a user