From c4122275dc95b48568ae01304dae914bedec97ef Mon Sep 17 00:00:00 2001 From: Rasmus Wriedt Larsen Date: Mon, 31 Oct 2022 18:11:07 +0100 Subject: [PATCH] Python: Bring back support for flow-summaries Also needed to fix up `TestUtil/UnresolvedCalls.qll` after a bad merge conflict resolution. Since all calls are now DataFlowCall, and not JUST the ones that can be resolved, we need to put in the restriction that the callable can also be resolved. --- .../new/internal/DataFlowDispatch.qll | 92 ++++++++++++------- .../dataflow/new/internal/DataFlowPrivate.qll | 16 +--- .../dataflow/new/internal/DataFlowPublic.qll | 14 ++- .../new/internal/FlowSummaryImplSpecific.qll | 78 ++++++++++++---- .../dataflow/TestUtil/UnresolvedCalls.qll | 4 +- .../dataflow/basic/callGraphSinks.expected | 1 + .../dataflow/basic/callGraphSources.expected | 1 + .../dataflow/basic/global.expected | 1 + .../dataflow/basic/globalStep.expected | 1 + .../dataflow/basic/local.expected | 5 + .../dataflow/basic/localStep.expected | 1 + .../dataflow/basic/sinks.expected | 4 + .../dataflow/basic/sources.expected | 4 + .../NormalTaintTrackingTest.expected | 12 --- .../dataflow/summaries/summaries.expected | 76 ++++++++++++--- .../basic/LocalTaintStep.expected | 1 + 16 files changed, 218 insertions(+), 93 deletions(-) diff --git a/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowDispatch.qll b/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowDispatch.qll index 5f957553a76..b6524382a71 100644 --- a/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowDispatch.qll +++ b/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowDispatch.qll @@ -36,12 +36,25 @@ private import python private import DataFlowPublic private import DataFlowPrivate private import FlowSummaryImpl as FlowSummaryImpl +private import FlowSummaryImplSpecific as FlowSummaryImplSpecific newtype TParameterPosition = /** Used for `self` in methods, and `cls` in classmethods. */ TSelfParameterPosition() or - TPositionalParameterPosition(int pos) { pos = any(Parameter p).getPosition() } or - TKeywordParameterPosition(string name) { name = any(Parameter p).getName() } or + TPositionalParameterPosition(int pos) { + pos = any(Parameter p).getPosition() + or + // since synthetic parameters are made for a synthetic summary callable, based on + // what Argument positions they have flow for, we need to make sure we have such + // parameter positions available. + FlowSummaryImplSpecific::ParsePositions::isParsedPositionalArgumentPosition(_, pos) + } or + TKeywordParameterPosition(string name) { + name = any(Parameter p).getName() + or + // see comment for TPositionalParameterPosition + FlowSummaryImplSpecific::ParsePositions::isParsedKeywordArgumentPosition(_, name) + } or TStarArgsParameterPosition(int pos) { // since `.getPosition` does not work for `*args`, we need *args parameter positions // at index 1 larger than the largest positional parameter position (and 0 must be @@ -114,8 +127,20 @@ class ParameterPosition extends TParameterPosition { newtype TArgumentPosition = /** Used for `self` in methods, and `cls` in classmethods. */ TSelfArgumentPosition() or - TPositionalArgumentPosition(int pos) { exists(any(CallNode c).getArg(pos)) } or - TKeywordArgumentPosition(string name) { exists(any(CallNode c).getArgByName(name)) } or + TPositionalArgumentPosition(int pos) { + exists(any(CallNode c).getArg(pos)) + or + // since synthetic calls within a summarized callable could use a unique argument + // position, we need to ensure we make these available (these are specified as + // parameters in the flow-summary spec) + FlowSummaryImplSpecific::ParsePositions::isParsedPositionalParameterPosition(_, pos) + } or + TKeywordArgumentPosition(string name) { + exists(any(CallNode c).getArgByName(name)) + or + // see comment for TPositionalArgumentPosition + FlowSummaryImplSpecific::ParsePositions::isParsedKeywordParameterPosition(_, name) + } or TStarArgsArgumentPosition(int pos) { exists(Call c | c.getPositionalArg(pos) instanceof Starred) } or TDictSplatArgumentPosition() @@ -376,7 +401,7 @@ class LibraryCallableValue extends DataFlowCallable, TLibraryCallable { LibraryCallableValue() { this = TLibraryCallable(callable) } - override string toString() { result = callable.toString() } + override string toString() { result = "LibraryCallableValue: " + callable.toString() } override string getQualifiedName() { result = callable.toString() } @@ -1038,7 +1063,8 @@ predicate resolveCall(ControlFlowNode call, Function target, CallType type) { * Holds if the argument of `call` at position `apos` is `arg`. This is just a helper * predicate that maps ArgumentPositions to the arguments of the underlying `CallNode`. */ -private predicate normalCallArg(CallNode call, Node arg, ArgumentPosition apos) { +cached +predicate normalCallArg(CallNode call, Node arg, ArgumentPosition apos) { exists(int index | apos.isPositional(index) and arg.asCfgNode() = call.getArg(index) @@ -1170,8 +1196,8 @@ predicate getCallArg( // DataFlowCall // ============================================================================= newtype TDataFlowCall = - TNormalCall(CallNode call, Function target, CallType type) { resolveCall(call, target, type) } - or + TNormalCall(CallNode call, Function target, CallType type) { resolveCall(call, target, type) } or + TPotentialLibraryCall(CallNode call) or /** A synthesized call inside a summarized callable */ TSummaryCall(FlowSummaryImpl::Public::SummarizedCallable c, Node receiver) { FlowSummaryImpl::Private::summaryCallbackRange(c, receiver) @@ -1253,49 +1279,44 @@ class NormalCall extends ExtractedDataFlowCall, TNormalCall { } /** - * A call to a summarized callable, a `LibraryCallable`. + * A potential call to a summarized callable, a `LibraryCallable`. * * We currently exclude all resolved calls. This means that a call to, say, `map`, which * is a `ClassCall`, cannot currently be given a summary. * We hope to lift this restriction in the future and include all potential calls to summaries * in this class. */ -class LibraryCall extends DataFlowCall { - LibraryCall() { - // TODO(call-graph): implement this! - none() - } +class PotentialLibraryCall extends ExtractedDataFlowCall, TPotentialLibraryCall { + CallNode call; + + PotentialLibraryCall() { this = TPotentialLibraryCall(call) } override string toString() { - // TODO(call-graph): implement this! - none() + // note: if we used toString directly on the CallNode we would get + // `ControlFlowNode for func()` + // but the `ControlFlowNode` part is just clutter, so we go directly to the AST node + // instead. + result = call.getNode().toString() } - // We cannot refer to a `LibraryCallable` here, + // We cannot refer to a `PotentialLibraryCall` here, // as that could in turn refer to type tracking. - // This call will be tied to a `LibraryCallable` via - // `getViableCallabe` when the global data flow is assembled. + // This call will be tied to a `PotentialLibraryCall` via + // `viableCallable` when the global data flow is assembled. override DataFlowCallable getCallable() { none() } override ArgumentNode getArgument(ArgumentPosition apos) { - // TODO(call-graph): implement this! - none() + normalCallArg(call, result, apos) + or + // potential self argument, from `foo.bar()` -- note that this could also just be a + // module reference, but we really don't have a good way of knowing :| + apos.isSelf() and + result = any(MethodCallNode mc | mc.getFunction().asCfgNode() = call.getFunction()).getObject() } - override ControlFlowNode getNode() { - // TODO(call-graph): implement this! - none() - } + override ControlFlowNode getNode() { result = call } - override DataFlowCallable getEnclosingCallable() { - // TODO(call-graph): implement this! - none() - } - - override Location getLocation() { - // TODO(call-graph): implement this! - none() - } + override DataFlowCallable getEnclosingCallable() { result.getScope() = call.getScope() } } /** @@ -1433,7 +1454,8 @@ DataFlowCallable viableCallable(ExtractedDataFlowCall call) { // Instead we resolve the call from the summary. exists(LibraryCallable callable | result = TLibraryCallable(callable) and - call.getNode() = callable.getACall().getNode() + call.getNode() = callable.getACall().getNode() and + call instanceof PotentialLibraryCall ) } diff --git a/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowPrivate.qll b/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowPrivate.qll index 8d4f45bcdeb..ed611c93549 100644 --- a/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowPrivate.qll +++ b/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowPrivate.qll @@ -981,18 +981,10 @@ class LambdaCallKind = Unit; /** Holds if `creation` is an expression that creates a lambda of kind `kind` for `c`. */ predicate lambdaCreation(Node creation, LambdaCallKind kind, DataFlowCallable c) { - // TODO(call-graph): implement this! - // - // // lambda - // kind = kind and - // creation.asExpr() = c.(DataFlowLambda).getDefinition() - // or - // // normal function - // exists(FunctionDef def | - // def.defines(creation.asVar().getSourceVariable()) and - // def.getDefinedFunction() = c.(DataFlowCallableValue).getCallableValue().getScope() - // ) - // or + // lambda and plain functions + kind = kind and + creation.asExpr() = c.(DataFlowPlainFunction).getScope().getDefinition() + or // summarized function exists(kind) and // avoid warning on unused 'kind' exists(Call call | diff --git a/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowPublic.qll b/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowPublic.qll index 79b711db9e8..b03bb3de0a0 100644 --- a/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowPublic.qll +++ b/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowPublic.qll @@ -337,11 +337,19 @@ abstract class ArgumentNode extends Node { } /** - * A data flow node that represents a call argument found in the source code, - * where the call can be resolved. + * A data flow node that represents a call argument found in the source code. */ class ExtractedArgumentNode extends ArgumentNode { - ExtractedArgumentNode() { getCallArg(_, _, _, this, _) } + ExtractedArgumentNode() { + // for resolved calls, we need to allow all argument nodes + getCallArg(_, _, _, this, _) + or + // for potential summaries we allow all normal call arguments + normalCallArg(_, this, _) + or + // and self arguments + this = any(MethodCallNode mc).getObject() + } final override predicate argumentOf(DataFlowCall call, ArgumentPosition pos) { this = call.getArgument(pos) and diff --git a/python/ql/lib/semmle/python/dataflow/new/internal/FlowSummaryImplSpecific.qll b/python/ql/lib/semmle/python/dataflow/new/internal/FlowSummaryImplSpecific.qll index 5d950247369..90411e658b2 100644 --- a/python/ql/lib/semmle/python/dataflow/new/internal/FlowSummaryImplSpecific.qll +++ b/python/ql/lib/semmle/python/dataflow/new/internal/FlowSummaryImplSpecific.qll @@ -115,10 +115,34 @@ string getComponentSpecificCsv(SummaryComponent sc) { } /** Gets the textual representation of a parameter position in the format used for flow summaries. */ -string getParameterPositionCsv(ParameterPosition pos) { result = pos.toString() } +string getParameterPositionCsv(ParameterPosition pos) { + pos.isSelf() and result = "self" + or + exists(int i | + pos.isPositional(i) and + result = i.toString() + ) + or + exists(string name | + pos.isKeyword(name) and + result = name + ":" + ) +} /** Gets the textual representation of an argument position in the format used for flow summaries. */ -string getArgumentPositionCsv(ArgumentPosition pos) { result = pos.toString() } +string getArgumentPositionCsv(ArgumentPosition pos) { + pos.isSelf() and result = "self" + or + exists(int i | + pos.isPositional(i) and + result = i.toString() + ) + or + exists(string name | + pos.isKeyword(name) and + result = name + ":" + ) +} /** Holds if input specification component `c` needs a reference. */ predicate inputNeedsReferenceSpecific(string c) { none() } @@ -200,33 +224,55 @@ module ParsePositions { ) } - predicate isParsedParameterPosition(string c, int i) { + predicate isParsedPositionalParameterPosition(string c, int i) { isParamBody(c) and i = AccessPath::parseInt(c) } - predicate isParsedArgumentPosition(string c, int i) { + predicate isParsedKeywordParameterPosition(string c, string paramName) { + isParamBody(c) and + c = paramName + ":" + } + + predicate isParsedPositionalArgumentPosition(string c, int i) { isArgBody(c) and i = AccessPath::parseInt(c) } + + predicate isParsedKeywordArgumentPosition(string c, string argName) { + isArgBody(c) and + c = argName + ":" + } } /** Gets the argument position obtained by parsing `X` in `Parameter[X]`. */ ArgumentPosition parseParamBody(string s) { - none() - // TODO(call-graph): implement this! - // exists(int i | - // ParsePositions::isParsedParameterPosition(s, i) and - // result.isPositional(i) - // ) + exists(int i | + ParsePositions::isParsedPositionalParameterPosition(s, i) and + result.isPositional(i) + ) + or + exists(string name | + ParsePositions::isParsedKeywordParameterPosition(s, name) and + result.isKeyword(name) + ) + or + s = "self" and + result.isSelf() } /** Gets the parameter position obtained by parsing `X` in `Argument[X]`. */ ParameterPosition parseArgBody(string s) { - none() - // TODO(call-graph): implement this! - // exists(int i | - // ParsePositions::isParsedArgumentPosition(s, i) and - // result.isPositional(i) - // ) + exists(int i | + ParsePositions::isParsedPositionalArgumentPosition(s, i) and + result.isPositional(i) + ) + or + exists(string name | + ParsePositions::isParsedKeywordArgumentPosition(s, name) and + result.isKeyword(name) + ) + or + s = "self" and + result.isSelf() } diff --git a/python/ql/test/experimental/dataflow/TestUtil/UnresolvedCalls.qll b/python/ql/test/experimental/dataflow/TestUtil/UnresolvedCalls.qll index fbdcca3ef04..b84f8e6f165 100644 --- a/python/ql/test/experimental/dataflow/TestUtil/UnresolvedCalls.qll +++ b/python/ql/test/experimental/dataflow/TestUtil/UnresolvedCalls.qll @@ -12,7 +12,9 @@ class UnresolvedCallExpectations extends InlineExpectationsTest { override predicate hasActualResult(Location location, string element, string tag, string value) { exists(location.getFile().getRelativePath()) and exists(CallNode call | - not exists(DataFlowPrivate::DataFlowCall dfc | dfc.getNode() = call) and + not exists(DataFlowPrivate::DataFlowCall dfc | + exists(dfc.getCallable()) and dfc.getNode() = call + ) and not DataFlowPrivate::resolveClassCall(call, _) and not call = API::builtin(_).getACall().asCfgNode() and location = call.getLocation() and diff --git a/python/ql/test/experimental/dataflow/basic/callGraphSinks.expected b/python/ql/test/experimental/dataflow/basic/callGraphSinks.expected index e4b8f905530..0f87376ef1a 100644 --- a/python/ql/test/experimental/dataflow/basic/callGraphSinks.expected +++ b/python/ql/test/experimental/dataflow/basic/callGraphSinks.expected @@ -1,3 +1,4 @@ +| file://:0:0:0:0 | parameter position 0 of builtins.reversed | | test.py:1:1:1:21 | SynthDictSplatParameterNode | | test.py:1:19:1:19 | ControlFlowNode for x | | test.py:7:5:7:20 | ControlFlowNode for obfuscated_id() | diff --git a/python/ql/test/experimental/dataflow/basic/callGraphSources.expected b/python/ql/test/experimental/dataflow/basic/callGraphSources.expected index 4023ba8f3ea..0b4613c42de 100644 --- a/python/ql/test/experimental/dataflow/basic/callGraphSources.expected +++ b/python/ql/test/experimental/dataflow/basic/callGraphSources.expected @@ -1,2 +1,3 @@ +| file://:0:0:0:0 | [summary] to write: return (return) in builtins.reversed | | test.py:4:10:4:10 | ControlFlowNode for z | | test.py:7:19:7:19 | ControlFlowNode for a | diff --git a/python/ql/test/experimental/dataflow/basic/global.expected b/python/ql/test/experimental/dataflow/basic/global.expected index 8894bcc190a..800312b07be 100644 --- a/python/ql/test/experimental/dataflow/basic/global.expected +++ b/python/ql/test/experimental/dataflow/basic/global.expected @@ -1,3 +1,4 @@ +| file://:0:0:0:0 | [summary] read: argument position 0.List element in builtins.reversed | file://:0:0:0:0 | [summary] to write: return (return).List element in builtins.reversed | | 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 | diff --git a/python/ql/test/experimental/dataflow/basic/globalStep.expected b/python/ql/test/experimental/dataflow/basic/globalStep.expected index 9f228998b9c..fa5b20486c2 100644 --- a/python/ql/test/experimental/dataflow/basic/globalStep.expected +++ b/python/ql/test/experimental/dataflow/basic/globalStep.expected @@ -1,3 +1,4 @@ +| file://:0:0:0:0 | [summary] read: argument position 0.List element in builtins.reversed | file://:0:0:0:0 | [summary] to write: return (return).List element in builtins.reversed | | 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 | diff --git a/python/ql/test/experimental/dataflow/basic/local.expected b/python/ql/test/experimental/dataflow/basic/local.expected index cdf40018ed0..2354efea8e5 100644 --- a/python/ql/test/experimental/dataflow/basic/local.expected +++ b/python/ql/test/experimental/dataflow/basic/local.expected @@ -1,3 +1,8 @@ +| file://:0:0:0:0 | [summary] read: argument position 0.List element in builtins.reversed | file://:0:0:0:0 | [summary] read: argument position 0.List element in builtins.reversed | +| file://:0:0:0:0 | [summary] read: argument position 0.List element in builtins.reversed | file://:0:0:0:0 | [summary] to write: return (return).List element in builtins.reversed | +| file://:0:0:0:0 | [summary] to write: return (return) in builtins.reversed | file://:0:0:0:0 | [summary] to write: return (return) in builtins.reversed | +| file://:0:0:0:0 | [summary] to write: return (return).List element in builtins.reversed | file://:0:0:0:0 | [summary] to write: return (return).List element in builtins.reversed | +| file://:0:0:0:0 | parameter position 0 of builtins.reversed | file://:0:0:0:0 | parameter position 0 of builtins.reversed | | test.py:0:0:0:0 | GSSA Variable __name__ | test.py:0:0:0:0 | GSSA Variable __name__ | | test.py:0:0:0:0 | GSSA Variable __package__ | test.py:0:0:0:0 | GSSA Variable __package__ | | test.py:0:0:0:0 | GSSA Variable b | test.py:0:0:0:0 | GSSA Variable b | diff --git a/python/ql/test/experimental/dataflow/basic/localStep.expected b/python/ql/test/experimental/dataflow/basic/localStep.expected index e147bb9f4fc..534c31da1a6 100644 --- a/python/ql/test/experimental/dataflow/basic/localStep.expected +++ b/python/ql/test/experimental/dataflow/basic/localStep.expected @@ -1,3 +1,4 @@ +| file://:0:0:0:0 | [summary] read: argument position 0.List element in builtins.reversed | file://:0:0:0:0 | [summary] to write: return (return).List element in builtins.reversed | | test.py:1:1:1:21 | ControlFlowNode for FunctionExpr | test.py:1:5:1:17 | GSSA Variable 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 | diff --git a/python/ql/test/experimental/dataflow/basic/sinks.expected b/python/ql/test/experimental/dataflow/basic/sinks.expected index 944f8190aa5..aafff76bbe2 100644 --- a/python/ql/test/experimental/dataflow/basic/sinks.expected +++ b/python/ql/test/experimental/dataflow/basic/sinks.expected @@ -1,3 +1,7 @@ +| file://:0:0:0:0 | [summary] read: argument position 0.List element in builtins.reversed | +| file://:0:0:0:0 | [summary] to write: return (return) in builtins.reversed | +| file://:0:0:0:0 | [summary] to write: return (return).List element in builtins.reversed | +| file://:0:0:0:0 | parameter position 0 of builtins.reversed | | test.py:0:0:0:0 | GSSA Variable __name__ | | test.py:0:0:0:0 | GSSA Variable __package__ | | test.py:0:0:0:0 | GSSA Variable b | diff --git a/python/ql/test/experimental/dataflow/basic/sources.expected b/python/ql/test/experimental/dataflow/basic/sources.expected index 944f8190aa5..aafff76bbe2 100644 --- a/python/ql/test/experimental/dataflow/basic/sources.expected +++ b/python/ql/test/experimental/dataflow/basic/sources.expected @@ -1,3 +1,7 @@ +| file://:0:0:0:0 | [summary] read: argument position 0.List element in builtins.reversed | +| file://:0:0:0:0 | [summary] to write: return (return) in builtins.reversed | +| file://:0:0:0:0 | [summary] to write: return (return).List element in builtins.reversed | +| file://:0:0:0:0 | parameter position 0 of builtins.reversed | | test.py:0:0:0:0 | GSSA Variable __name__ | | test.py:0:0:0:0 | GSSA Variable __package__ | | test.py:0:0:0:0 | GSSA Variable b | diff --git a/python/ql/test/experimental/dataflow/summaries/NormalTaintTrackingTest.expected b/python/ql/test/experimental/dataflow/summaries/NormalTaintTrackingTest.expected index 8e04ba142cb..3875da4e143 100644 --- a/python/ql/test/experimental/dataflow/summaries/NormalTaintTrackingTest.expected +++ b/python/ql/test/experimental/dataflow/summaries/NormalTaintTrackingTest.expected @@ -1,14 +1,2 @@ missingAnnotationOnSink -| summaries.py:33:6:33:12 | summaries.py:33 | ERROR, you should add `# $ MISSING: flow` annotation | tainted | -| summaries.py:37:6:37:19 | summaries.py:37 | ERROR, you should add `# $ MISSING: flow` annotation | tainted_lambda | -| summaries.py:52:6:52:22 | summaries.py:52 | ERROR, you should add `# $ MISSING: flow` annotation | tainted_mapped[0] | -| summaries.py:58:6:58:31 | summaries.py:58 | ERROR, you should add `# $ MISSING: flow` annotation | tainted_mapped_explicit[0] | -| summaries.py:61:6:61:30 | summaries.py:61 | ERROR, you should add `# $ MISSING: flow` annotation | tainted_mapped_summary[0] | -| summaries.py:64:6:64:20 | summaries.py:64 | ERROR, you should add `# $ MISSING: flow` annotation | tainted_list[0] | failures -| summaries.py:33:16:33:49 | Comment # $ flow="SOURCE, l:-1 -> tainted" | Missing result:flow="SOURCE, l:-1 -> tainted" | -| summaries.py:37:23:37:63 | Comment # $ flow="SOURCE, l:-1 -> tainted_lambda" | Missing result:flow="SOURCE, l:-1 -> tainted_lambda" | -| summaries.py:52:26:52:69 | Comment # $ flow="SOURCE, l:-1 -> tainted_mapped[0]" | Missing result:flow="SOURCE, l:-1 -> tainted_mapped[0]" | -| summaries.py:58:35:58:87 | Comment # $ flow="SOURCE, l:-1 -> tainted_mapped_explicit[0]" | Missing result:flow="SOURCE, l:-1 -> tainted_mapped_explicit[0]" | -| summaries.py:61:34:61:85 | Comment # $ flow="SOURCE, l:-1 -> tainted_mapped_summary[0]" | Missing result:flow="SOURCE, l:-1 -> tainted_mapped_summary[0]" | -| summaries.py:64:24:64:65 | Comment # $ flow="SOURCE, l:-1 -> tainted_list[0]" | Missing result:flow="SOURCE, l:-1 -> tainted_list[0]" | diff --git a/python/ql/test/experimental/dataflow/summaries/summaries.expected b/python/ql/test/experimental/dataflow/summaries/summaries.expected index 8f5366ed6c2..b566cbdedc6 100644 --- a/python/ql/test/experimental/dataflow/summaries/summaries.expected +++ b/python/ql/test/experimental/dataflow/summaries/summaries.expected @@ -1,29 +1,77 @@ edges +| summaries.py:32:11:32:26 | ControlFlowNode for identity() | summaries.py:33:6:33:12 | ControlFlowNode for 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: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() [List element] | summaries.py:45:6:45:17 | ControlFlowNode for tainted_list [List element] | | summaries.py:44:25:44:32 | ControlFlowNode for List | summaries.py:45:6:45:20 | ControlFlowNode for Subscript | +| 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: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: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: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: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: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: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: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: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 | +| summaries.py:44:25:44:32 | ControlFlowNode for List [List element] | semmle.label | ControlFlowNode for List [List element] | | 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: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: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: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: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: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] | | summaries.py:68:6:68:26 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript | subpaths invalidSpecComponent -| append_to_list | Argument[0] | Argument[0] | -| append_to_list | Argument[1] | Argument[1] | -| apply_lambda | Argument[0].Parameter[0] | Argument[0] | -| apply_lambda | Argument[0].Parameter[0] | Parameter[0] | -| apply_lambda | Argument[0].ReturnValue | Argument[0] | -| apply_lambda | Argument[1] | Argument[1] | -| builtins.reversed | Argument[0].ListElement | Argument[0] | -| identity | Argument[0] | Argument[0] | -| json.loads | Argument[0] | Argument[0] | -| list_map | Argument[0].Parameter[0] | Argument[0] | -| list_map | Argument[0].Parameter[0] | Parameter[0] | -| list_map | Argument[0].ReturnValue | Argument[0] | -| list_map | Argument[1].ListElement | Argument[1] | -| reversed | Argument[0].ListElement | Argument[0] | #select +| summaries.py:33:6:33:12 | ControlFlowNode for tainted | summaries.py:32:20:32:25 | ControlFlowNode for SOURCE | summaries.py:33:6:33:12 | ControlFlowNode for tainted | $@ | summaries.py:32:20:32:25 | ControlFlowNode for SOURCE | ControlFlowNode for SOURCE | +| summaries.py:37:6:37:19 | ControlFlowNode for tainted_lambda | summaries.py:36:48:36:53 | ControlFlowNode for SOURCE | summaries.py:37:6:37:19 | ControlFlowNode for tainted_lambda | $@ | summaries.py:36:48:36:53 | ControlFlowNode for SOURCE | ControlFlowNode for SOURCE | | summaries.py:45:6:45:20 | ControlFlowNode for Subscript | summaries.py:44:26:44:31 | ControlFlowNode for SOURCE | summaries.py:45:6:45:20 | ControlFlowNode for Subscript | $@ | summaries.py:44:26:44:31 | ControlFlowNode for SOURCE | ControlFlowNode for SOURCE | +| summaries.py:52:6:52:22 | ControlFlowNode for Subscript | summaries.py:51:39:51:44 | ControlFlowNode for SOURCE | summaries.py:52:6:52:22 | ControlFlowNode for Subscript | $@ | summaries.py:51:39:51:44 | ControlFlowNode for SOURCE | ControlFlowNode for SOURCE | +| summaries.py:58:6:58:31 | ControlFlowNode for Subscript | summaries.py:57:56:57:61 | ControlFlowNode for SOURCE | summaries.py:58:6:58:31 | ControlFlowNode for Subscript | $@ | summaries.py:57:56:57:61 | ControlFlowNode for SOURCE | ControlFlowNode for SOURCE | +| summaries.py:61:6:61:30 | ControlFlowNode for Subscript | summaries.py:60:46:60:51 | ControlFlowNode for SOURCE | summaries.py:61:6:61:30 | ControlFlowNode for Subscript | $@ | summaries.py:60:46:60:51 | ControlFlowNode for SOURCE | ControlFlowNode for SOURCE | +| summaries.py:64:6:64:20 | ControlFlowNode for Subscript | summaries.py:63:35:63:40 | ControlFlowNode for SOURCE | summaries.py:64:6:64:20 | ControlFlowNode for Subscript | $@ | summaries.py:63:35:63:40 | ControlFlowNode for SOURCE | ControlFlowNode for SOURCE | | summaries.py:68:6:68:26 | ControlFlowNode for Subscript | summaries.py:67:33:67:38 | ControlFlowNode for SOURCE | summaries.py:68:6:68:26 | ControlFlowNode for Subscript | $@ | summaries.py:67:33:67:38 | ControlFlowNode for SOURCE | ControlFlowNode for SOURCE | diff --git a/python/ql/test/experimental/dataflow/tainttracking/basic/LocalTaintStep.expected b/python/ql/test/experimental/dataflow/tainttracking/basic/LocalTaintStep.expected index 3b3f18c5b9e..05b64297f71 100644 --- a/python/ql/test/experimental/dataflow/tainttracking/basic/LocalTaintStep.expected +++ b/python/ql/test/experimental/dataflow/tainttracking/basic/LocalTaintStep.expected @@ -1,3 +1,4 @@ +| file://:0:0:0:0 | [summary] read: argument position 0.List element in builtins.reversed | file://:0:0:0:0 | [summary] to write: return (return).List element in builtins.reversed | | test.py:3:1:3:7 | GSSA Variable tainted | test.py:4:6:4:12 | ControlFlowNode for tainted | | test.py:3:11:3:16 | ControlFlowNode for SOURCE | test.py:3:1:3:7 | GSSA Variable tainted | | test.py:6:1:6:11 | ControlFlowNode for FunctionExpr | test.py:6:5:6:8 | GSSA Variable func |