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 |