diff --git a/config/identical-files.json b/config/identical-files.json index 2ff65c453f0..1c5e11a8eee 100644 --- a/config/identical-files.json +++ b/config/identical-files.json @@ -70,7 +70,8 @@ "DataFlow Java/C# Flow Summaries": [ "java/ql/lib/semmle/code/java/dataflow/internal/FlowSummaryImpl.qll", "csharp/ql/lib/semmle/code/csharp/dataflow/internal/FlowSummaryImpl.qll", - "ruby/ql/lib/codeql/ruby/dataflow/internal/FlowSummaryImpl.qll" + "ruby/ql/lib/codeql/ruby/dataflow/internal/FlowSummaryImpl.qll", + "python/ql/lib/semmle/python/dataflow/new/internal/FlowSummaryImpl.qll" ], "SsaReadPosition Java/C#": [ "java/ql/lib/semmle/code/java/dataflow/internal/rangeanalysis/SsaReadPositionCommon.qll", @@ -515,7 +516,8 @@ "csharp/ql/lib/semmle/code/csharp/dataflow/internal/AccessPathSyntax.qll", "java/ql/lib/semmle/code/java/dataflow/internal/AccessPathSyntax.qll", "javascript/ql/lib/semmle/javascript/frameworks/data/internal/AccessPathSyntax.qll", - "ruby/ql/lib/codeql/ruby/dataflow/internal/AccessPathSyntax.qll" + "ruby/ql/lib/codeql/ruby/dataflow/internal/AccessPathSyntax.qll", + "python/ql/lib/semmle/python/dataflow/new/internal/AccessPathSyntax.qll" ], "IncompleteUrlSubstringSanitization": [ "javascript/ql/src/Security/CWE-020/IncompleteUrlSubstringSanitization.qll", diff --git a/python/ql/lib/semmle/python/Concepts.qll b/python/ql/lib/semmle/python/Concepts.qll index 6b0467f4b41..c2a1d9b6b7c 100644 --- a/python/ql/lib/semmle/python/Concepts.qll +++ b/python/ql/lib/semmle/python/Concepts.qll @@ -828,7 +828,7 @@ module HTTP { } /** A parameter that will receive parts of the url when handling an incoming request. */ - private class RoutedParameter extends RemoteFlowSource::Range, DataFlow::ParameterNode { + private class RoutedParameter extends RemoteFlowSource::Range, DataFlow::SourceParameterNode { RequestHandler handler; RoutedParameter() { this.getParameter() = handler.getARoutedParameter() } diff --git a/python/ql/lib/semmle/python/dataflow/new/FlowSummary.qll b/python/ql/lib/semmle/python/dataflow/new/FlowSummary.qll new file mode 100644 index 00000000000..375c128cbc4 --- /dev/null +++ b/python/ql/lib/semmle/python/dataflow/new/FlowSummary.qll @@ -0,0 +1,117 @@ +/** Provides classes and predicates for defining flow summaries. */ + +import python +import semmle.python.dataflow.new.DataFlow +private import internal.FlowSummaryImpl as Impl +private import internal.DataFlowUtil +private import internal.DataFlowPrivate + +// import all instances below +private module Summaries { } + +class SummaryComponent = Impl::Public::SummaryComponent; + +/** Provides predicates for constructing summary components. */ +module SummaryComponent { + private import Impl::Public::SummaryComponent as SC + + predicate parameter = SC::parameter/1; + + predicate argument = SC::argument/1; + + predicate content = SC::content/1; + + /** Gets a summary component that represents a list element. */ + SummaryComponent listElement() { result = content(any(ListElementContent c)) } + + /** Gets a summary component that represents the return value of a call. */ + SummaryComponent return() { result = SC::return(any(ReturnKind rk)) } +} + +class SummaryComponentStack = Impl::Public::SummaryComponentStack; + +/** Provides predicates for constructing stacks of summary components. */ +module SummaryComponentStack { + private import Impl::Public::SummaryComponentStack as SCS + + predicate singleton = SCS::singleton/1; + + predicate push = SCS::push/2; + + predicate argument = SCS::argument/1; + + /** Gets a singleton stack representing the return value of a call. */ + SummaryComponentStack return() { result = singleton(SummaryComponent::return()) } +} + +/** A callable with a flow summary, identified by a unique string. */ +abstract class SummarizedCallable extends LibraryCallable { + bindingset[this] + SummarizedCallable() { any() } + + /** + * Holds if data may flow from `input` to `output` through this callable. + * + * `preservesValue` indicates whether this is a value-preserving step + * or a taint-step. + * + * Input specifications are restricted to stacks that end with + * `SummaryComponent::argument(_)`, preceded by zero or more + * `SummaryComponent::return()` or `SummaryComponent::content(_)` components. + * + * Output specifications are restricted to stacks that end with + * `SummaryComponent::return()` or `SummaryComponent::argument(_)`. + * + * Output stacks ending with `SummaryComponent::return()` can be preceded by zero + * or more `SummaryComponent::content(_)` components. + * + * Output stacks ending with `SummaryComponent::argument(_)` can be preceded by an + * optional `SummaryComponent::parameter(_)` component, which in turn can be preceded + * by zero or more `SummaryComponent::content(_)` components. + */ + pragma[nomagic] + predicate propagatesFlow( + SummaryComponentStack input, SummaryComponentStack output, boolean preservesValue + ) { + none() + } + + /** + * Same as + * + * ```ql + * propagatesFlow( + * SummaryComponentStack input, SummaryComponentStack output, boolean preservesValue + * ) + * ``` + * + * but uses an external (string) representation of the input and output stacks. + */ + pragma[nomagic] + predicate propagatesFlowExt(string input, string output, boolean preservesValue) { none() } + + /** + * Holds if values stored inside `content` are cleared on objects passed as + * the `i`th argument to this callable. + */ + pragma[nomagic] + predicate clearsContent(int i, DataFlow::Content content) { none() } +} + +private class SummarizedCallableAdapter extends Impl::Public::SummarizedCallable { + private SummarizedCallable sc; + + SummarizedCallableAdapter() { this = TLibraryCallable(sc) } + + final override predicate propagatesFlow( + SummaryComponentStack input, SummaryComponentStack output, boolean preservesValue + ) { + sc.propagatesFlow(input, output, preservesValue) + } + + final override predicate clearsContent(ParameterPosition pos, DataFlow::Content content) { + sc.clearsContent(pos, content) + } +} + +class RequiredSummaryComponentStack = Impl::Public::RequiredSummaryComponentStack; diff --git a/python/ql/lib/semmle/python/dataflow/new/SensitiveDataSources.qll b/python/ql/lib/semmle/python/dataflow/new/SensitiveDataSources.qll index be51b99b4c0..7686aebef73 100644 --- a/python/ql/lib/semmle/python/dataflow/new/SensitiveDataSources.qll +++ b/python/ql/lib/semmle/python/dataflow/new/SensitiveDataSources.qll @@ -305,7 +305,7 @@ private module SensitiveDataModeling { } /** A parameter where the name indicates it will receive sensitive data. */ - class SensitiveParameter extends SensitiveDataSource::Range, DataFlow::ParameterNode { + class SensitiveParameter extends SensitiveDataSource::Range, DataFlow::SourceParameterNode { SensitiveDataClassification classification; SensitiveParameter() { this.getParameter().getName() = sensitiveString(classification) } diff --git a/python/ql/lib/semmle/python/dataflow/new/internal/AccessPathSyntax.qll b/python/ql/lib/semmle/python/dataflow/new/internal/AccessPathSyntax.qll new file mode 100644 index 00000000000..8e126868cc1 --- /dev/null +++ b/python/ql/lib/semmle/python/dataflow/new/internal/AccessPathSyntax.qll @@ -0,0 +1,177 @@ +/** + * Module for parsing access paths from CSV models, both the identifying access path used + * by dynamic languages, and the input/output specifications for summary steps. + * + * This file is used by the shared data flow library and by the JavaScript libraries + * (which does not use the shared data flow libraries). + */ + +/** + * Convenience-predicate for extracting two capture groups at once. + */ +bindingset[input, regexp] +private predicate regexpCaptureTwo(string input, string regexp, string capture1, string capture2) { + capture1 = input.regexpCapture(regexp, 1) and + capture2 = input.regexpCapture(regexp, 2) +} + +/** Companion module to the `AccessPath` class. */ +module AccessPath { + /** A string that should be parsed as an access path. */ + abstract class Range extends string { + bindingset[this] + Range() { any() } + } + + /** + * Parses an integer constant `n` or interval `n1..n2` (inclusive) and gets the value + * of the constant or any value contained in the interval. + */ + bindingset[arg] + int parseInt(string arg) { + result = arg.toInt() + or + // Match "n1..n2" + exists(string lo, string hi | + regexpCaptureTwo(arg, "(-?\\d+)\\.\\.(-?\\d+)", lo, hi) and + result = [lo.toInt() .. hi.toInt()] + ) + } + + /** + * Parses a lower-bounded interval `n..` and gets the lower bound. + */ + bindingset[arg] + private int parseLowerBound(string arg) { + result = arg.regexpCapture("(-?\\d+)\\.\\.", 1).toInt() + } + + /** + * Parses an integer constant or interval (bounded or unbounded) that explicitly + * references the arity, such as `N-1` or `N-3..N-1`. + * + * Note that expressions of form `N-x` will never resolve to a negative index, + * even if `N` is zero (it will have no result in that case). + */ + bindingset[arg, arity] + private int parseIntWithExplicitArity(string arg, int arity) { + result >= 0 and // do not allow N-1 to resolve to a negative index + exists(string lo | + // N-x + lo = arg.regexpCapture("N-(\\d+)", 1) and + result = arity - lo.toInt() + or + // N-x.. + lo = arg.regexpCapture("N-(\\d+)\\.\\.", 1) and + result = [arity - lo.toInt(), arity - 1] + ) + or + exists(string lo, string hi | + // x..N-y + regexpCaptureTwo(arg, "(-?\\d+)\\.\\.N-(\\d+)", lo, hi) and + result = [lo.toInt() .. arity - hi.toInt()] + or + // N-x..N-y + regexpCaptureTwo(arg, "N-(\\d+)\\.\\.N-(\\d+)", lo, hi) and + result = [arity - lo.toInt() .. arity - hi.toInt()] and + result >= 0 + or + // N-x..y + regexpCaptureTwo(arg, "N-(\\d+)\\.\\.(\\d+)", lo, hi) and + result = [arity - lo.toInt() .. hi.toInt()] and + result >= 0 + ) + } + + /** + * Parses an integer constant or interval (bounded or unbounded) and gets any + * of the integers contained within (of which there may be infinitely many). + * + * Has no result for arguments involving an explicit arity, such as `N-1`. + */ + bindingset[arg, result] + int parseIntUnbounded(string arg) { + result = parseInt(arg) + or + result >= parseLowerBound(arg) + } + + /** + * Parses an integer constant or interval (bounded or unbounded) that + * may reference the arity of a call, such as `N-1` or `N-3..N-1`. + * + * Note that expressions of form `N-x` will never resolve to a negative index, + * even if `N` is zero (it will have no result in that case). + */ + bindingset[arg, arity] + int parseIntWithArity(string arg, int arity) { + result = parseInt(arg) + or + result in [parseLowerBound(arg) .. arity - 1] + or + result = parseIntWithExplicitArity(arg, arity) + } +} + +/** Gets the `n`th token on the access path as a string. */ +private string getRawToken(AccessPath path, int n) { + // Avoid splitting by '.' since tokens may contain dots, e.g. `Field[foo.Bar.x]`. + // Instead use regexpFind to match valid tokens, and supplement with a final length + // check (in `AccessPath.hasSyntaxError`) to ensure all characters were included in a token. + result = path.regexpFind("\\w+(?:\\[[^\\]]*\\])?(?=\\.|$)", n, _) +} + +/** + * A string that occurs as an access path (either identifying or input/output spec) + * which might be relevant for this database. + */ +class AccessPath extends string instanceof AccessPath::Range { + /** Holds if this string is not a syntactically valid access path. */ + predicate hasSyntaxError() { + // If the lengths match, all characters must haven been included in a token + // or seen by the `.` lookahead pattern. + this != "" and + not this.length() = sum(int n | | getRawToken(this, n).length() + 1) - 1 + } + + /** Gets the `n`th token on the access path (if there are no syntax errors). */ + AccessPathToken getToken(int n) { + result = getRawToken(this, n) and + not this.hasSyntaxError() + } + + /** Gets the number of tokens on the path (if there are no syntax errors). */ + int getNumToken() { + result = count(int n | exists(getRawToken(this, n))) and + not this.hasSyntaxError() + } +} + +/** + * An access part token such as `Argument[1]` or `ReturnValue`, appearing in one or more access paths. + */ +class AccessPathToken extends string { + AccessPathToken() { this = getRawToken(any(AccessPath path), _) } + + private string getPart(int part) { + result = this.regexpCapture("([^\\[]+)(?:\\[([^\\]]*)\\])?", part) + } + + /** Gets the name of the token, such as `Member` from `Member[x]` */ + string getName() { result = this.getPart(1) } + + /** + * Gets the argument list, such as `1,2` from `Member[1,2]`, + * or has no result if there are no arguments. + */ + string getArgumentList() { result = this.getPart(2) } + + /** Gets the `n`th argument to this token, such as `x` or `y` from `Member[x,y]`. */ + string getArgument(int n) { result = this.getArgumentList().splitAt(",", n).trim() } + + /** Gets an argument to this token, such as `x` or `y` from `Member[x,y]`. */ + string getAnArgument() { result = this.getArgument(_) } + + /** Gets the number of arguments to this token, such as 2 for `Member[x,y]` or zero for `ReturnValue`. */ + int getNumArgument() { result = count(int n | exists(this.getArgument(n))) } +} diff --git a/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowDispatchPointsTo.qll b/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowDispatchPointsTo.qll index 6564d4dc62e..b70b43a69c2 100644 --- a/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowDispatchPointsTo.qll +++ b/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowDispatchPointsTo.qll @@ -7,15 +7,22 @@ private import python private import DataFlowPublic private import semmle.python.SpecialMethods +private import FlowSummaryImpl as FlowSummaryImpl /** A parameter position represented by an integer. */ class ParameterPosition extends int { ParameterPosition() { exists(any(DataFlowCallable c).getParameter(this)) } + + /** Holds if this position represents a positional parameter at position `pos`. */ + predicate isPositional(int pos) { this = pos } // with the current representation, all parameters are positional } /** An argument position represented by an integer. */ class ArgumentPosition extends int { - ArgumentPosition() { exists(any(DataFlowCall c).getArg(this)) } + ArgumentPosition() { this in [-2, -1] or exists(any(Call c).getArg(this)) } + + /** Holds if this position represents a positional argument at position `pos`. */ + predicate isPositional(int pos) { this = pos } // with the current representation, all arguments are positional } /** Holds if arguments at position `apos` match parameters at position `ppos`. */ @@ -96,7 +103,7 @@ module ArgumentPassing { * Used to limit the size of predicates. */ predicate connects(CallNode call, CallableValue callable) { - exists(DataFlowCall c | + exists(DataFlowSourceCall c | call = c.getNode() and callable = c.getCallable().getCallableValue() ) @@ -268,6 +275,15 @@ module ArgumentPassing { import ArgumentPassing +/** A callable defined in library code, identified by a unique string. */ +abstract class LibraryCallable extends string { + bindingset[this] + LibraryCallable() { any() } + + /** Gets a call to this library callable. */ + abstract Call getACall(); +} + /** * IPA type for DataFlowCallable. * @@ -282,27 +298,33 @@ newtype TDataFlowCallable = callable instanceof ClassValue } or TLambda(Function lambda) { lambda.isLambda() } or - TModule(Module m) + TModule(Module m) or + TLibraryCallable(LibraryCallable callable) /** A callable. */ -abstract class DataFlowCallable extends TDataFlowCallable { +class DataFlowCallable extends TDataFlowCallable { /** Gets a textual representation of this element. */ - abstract string toString(); + string toString() { result = "DataFlowCallable" } /** Gets a call to this callable. */ - abstract CallNode getACall(); + CallNode getACall() { none() } /** Gets the scope of this callable */ - abstract Scope getScope(); + Scope getScope() { none() } /** Gets the specified parameter of this callable */ - abstract NameNode getParameter(int n); + NameNode getParameter(int n) { none() } /** Gets the name of this callable. */ - abstract string getName(); + string getName() { none() } /** Gets a callable value for this callable, if one exists. */ - abstract CallableValue getCallableValue(); + CallableValue getCallableValue() { none() } + + /** Gets the underlying library callable, if any. */ + LibraryCallable asLibraryCallable() { this = TLibraryCallable(result) } + + Location getLocation() { none() } } /** A class representing a callable value. */ @@ -383,13 +405,41 @@ newtype TDataFlowCall = /** Bound methods need to make room for the explicit self parameter */ TMethodCall(CallNode call) { call = any(FunctionValue f).getAMethodCall() } or TClassCall(CallNode call) { call = any(ClassValue c | not c.isAbsent()).getACall() } or - TSpecialCall(SpecialMethodCallNode special) + TSpecialCall(SpecialMethodCallNode special) or + TSummaryCall(FlowSummaryImpl::Public::SummarizedCallable c, Node receiver) { + FlowSummaryImpl::Private::summaryCallbackRange(c, receiver) + } + +class TDataFlowSourceCall = TFunctionCall or TMethodCall or TClassCall or TSpecialCall; /** A call. */ abstract class DataFlowCall extends TDataFlowCall { /** Gets a textual representation of this element. */ abstract string toString(); + /** Gets the enclosing callable of this call. */ + abstract DataFlowCallable getEnclosingCallable(); + + /** Gets the location of this dataflow call. */ + abstract Location getLocation(); + + /** + * Holds if this element is at the specified location. + * The location spans column `startcolumn` of line `startline` to + * column `endcolumn` of line `endline` in file `filepath`. + * For more information, see + * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). + */ + predicate hasLocationInfo( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ) { + this.getLocation().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) + } +} + +abstract class DataFlowSourceCall extends DataFlowCall, TDataFlowSourceCall { + final override Location getLocation() { result = this.getNode().getLocation() } + /** Get the callable to which this call goes. */ abstract DataFlowCallable getCallable(); @@ -401,12 +451,6 @@ abstract class DataFlowCall extends TDataFlowCall { /** Get the control flow node representing this call. */ abstract ControlFlowNode getNode(); - - /** Gets the enclosing callable of this call. */ - abstract DataFlowCallable getEnclosingCallable(); - - /** Gets the location of this dataflow call. */ - Location getLocation() { result = this.getNode().getLocation() } } /** @@ -415,7 +459,7 @@ abstract class DataFlowCall extends TDataFlowCall { * Bound method calls and class calls insert an argument for the explicit * `self` parameter, and special method calls have special argument passing. */ -class FunctionCall extends DataFlowCall, TFunctionCall { +class FunctionCall extends DataFlowSourceCall, TFunctionCall { CallNode call; DataFlowCallable callable; @@ -439,7 +483,7 @@ class FunctionCall extends DataFlowCall, TFunctionCall { * Represents a call to a bound method call. * The node representing the instance is inserted as argument to the `self` parameter. */ -class MethodCall extends DataFlowCall, TMethodCall { +class MethodCall extends DataFlowSourceCall, TMethodCall { CallNode call; FunctionValue bm; @@ -471,7 +515,7 @@ class MethodCall extends DataFlowCall, TMethodCall { * That makes the call node be the post-update node holding the value of the object * after the constructor has run. */ -class ClassCall extends DataFlowCall, TClassCall { +class ClassCall extends DataFlowSourceCall, TClassCall { CallNode call; ClassValue c; @@ -498,7 +542,7 @@ class ClassCall extends DataFlowCall, TClassCall { } /** A call to a special method. */ -class SpecialCall extends DataFlowCall, TSpecialCall { +class SpecialCall extends DataFlowSourceCall, TSpecialCall { SpecialMethodCallNode special; SpecialCall() { this = TSpecialCall(special) } @@ -518,8 +562,34 @@ class SpecialCall extends DataFlowCall, TSpecialCall { } } +/** + * A synthesized call inside a callable with a flow summary. + * + * For example, in + * ```python + * map(lambda x: x + 1, [1, 2, 3]) + * ``` + * + * there is a call to the lambda argument inside `map`. + */ +class SummaryCall extends DataFlowCall, TSummaryCall { + private FlowSummaryImpl::Public::SummarizedCallable c; + private Node receiver; + + SummaryCall() { this = TSummaryCall(c, receiver) } + + /** Gets the data flow node that this call targets. */ + Node getReceiver() { result = receiver } + + override DataFlowCallable getEnclosingCallable() { result = c } + + override string toString() { result = "[summary] call to " + receiver + " in " + c } + + override Location getLocation() { result = c.getLocation() } +} + /** Gets a viable run-time target for the call `call`. */ -DataFlowCallable viableCallable(DataFlowCall call) { result = call.getCallable() } +DataFlowCallable viableCallable(DataFlowSourceCall call) { result = call.getCallable() } private newtype TReturnKind = TNormalReturnKind() @@ -552,7 +622,7 @@ class OutNode extends CfgNode { * Gets a node that can read the value returned from `call` with return kind * `kind`. */ -OutNode getAnOutNode(DataFlowCall call, ReturnKind kind) { +OutNode getAnOutNode(DataFlowSourceCall call, ReturnKind kind) { call.getNode() = result.getNode() and kind = TNormalReturnKind() } 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 a8e812a6972..eaed43ea7b1 100644 --- a/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowPrivate.qll +++ b/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowPrivate.qll @@ -2,6 +2,7 @@ private import python private import DataFlowPublic private import semmle.python.essa.SsaCompute private import semmle.python.dataflow.new.internal.ImportStar +private import FlowSummaryImpl as FlowSummaryImpl // Since we allow extra data-flow steps from modeled frameworks, we import these // up-front, to ensure these are included. This provides a more seamless experience from // a user point of view, since they don't need to know they need to import a specific @@ -21,7 +22,7 @@ import DataFlowDispatchPointsTo DataFlowCallable nodeGetEnclosingCallable(Node n) { result = n.getEnclosingCallable() } /** Holds if `p` is a `ParameterNode` of `c` with position `pos`. */ -predicate isParameterNode(ParameterNode p, DataFlowCallable c, ParameterPosition pos) { +predicate isParameterNode(SourceParameterNode p, DataFlowCallable c, ParameterPosition pos) { p.isParameterOf(c, pos) } @@ -87,6 +88,8 @@ deprecated module syntheticPostUpdateNode = SyntheticPostUpdateNode; /** A module collecting the different reasons for synthesising a post-update node. */ module SyntheticPostUpdateNode { + private import semmle.python.SpecialMethods + /** A post-update node is synthesized for all nodes which satisfy `NeedsSyntheticPostUpdateNode`. */ class SyntheticPostUpdateNode extends PostUpdateNode, TSyntheticPostUpdateNode { NeedsSyntheticPostUpdateNode pre; @@ -145,11 +148,21 @@ module SyntheticPostUpdateNode { exists(ClassCall c, int n | n > 0 | result = c.getArg(n)) or // any argument of any call that we have not been able to resolve - exists(CallNode call | not call = any(DataFlowCall c).getNode() | + exists(CallNode call | not resolvedCall(call) | result.(CfgNode).getNode() in [call.getArg(_), call.getArgByName(_)] ) } + predicate resolvedCall(CallNode call) { + call = any(FunctionValue f).getAFunctionCall() + or + call = any(FunctionValue f).getAMethodCall() + or + call = any(ClassValue c | not c.isAbsent()).getACall() + or + call = any(SpecialMethodCallNode special) + } + /** Gets the pre-update node associated with a store. This is used for when an object might have its value changed after a store. */ CfgNode storePreUpdateNode() { exists(Attribute a | 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 12120b3e5a7..472e7635ad5 100644 --- a/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowPublic.qll +++ b/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowPublic.qll @@ -9,6 +9,7 @@ import Attributes import LocalSources private import semmle.python.essa.SsaCompute private import semmle.python.dataflow.new.internal.ImportStar +private import FlowSummaryImpl as FlowSummaryImpl /** * IPA type for data flow nodes. @@ -100,7 +101,15 @@ newtype TNode = // // So for now we live with having these synthetic ORM nodes for _all_ classes, which // is a bit wasteful, but we don't think it will hurt too much. - TSyntheticOrmModelNode(Class cls) + TSyntheticOrmModelNode(Class cls) or + TSummaryNode( + FlowSummaryImpl::Public::SummarizedCallable c, FlowSummaryImpl::Private::SummaryNodeState state + ) { + FlowSummaryImpl::Private::summaryNodeRange(c, state) + } or + TSummaryParameterNode(FlowSummaryImpl::Public::SummarizedCallable c, ParameterPosition pos) { + FlowSummaryImpl::Private::summaryParameterNodeRange(c, pos) + } /** Helper for `Node::getEnclosingCallable`. */ private DataFlowCallable getCallableScope(Scope s) { @@ -277,21 +286,26 @@ ExprNode exprNode(DataFlowExpr e) { result.getNode().getNode() = e } * The value of a parameter at function entry, viewed as a node in a data * flow graph. */ -class ParameterNode extends CfgNode, LocalSourceNode { +abstract class ParameterNode extends Node { + /** + * Holds if this node is the parameter of callable `c` at the + * (zero-based) index `i`. + */ + abstract predicate isParameterOf(DataFlowCallable c, int i); +} + +class SourceParameterNode extends ParameterNode, CfgNode { + //, LocalSourceNode { ParameterDefinition def; - ParameterNode() { + SourceParameterNode() { node = def.getDefiningNode() and // Disregard parameters that we cannot resolve // TODO: Make this unnecessary exists(DataFlowCallable c | node = c.getParameter(_)) } - /** - * Holds if this node is the parameter of callable `c` at the - * (zero-based) index `i`. - */ - predicate isParameterOf(DataFlowCallable c, int i) { node = c.getParameter(i) } + override predicate isParameterOf(DataFlowCallable c, int i) { node = c.getParameter(i) } override DataFlowCallable getEnclosingCallable() { this.isParameterOf(result, _) } @@ -300,17 +314,17 @@ class ParameterNode extends CfgNode, LocalSourceNode { } /** Gets a node corresponding to parameter `p`. */ -ParameterNode parameterNode(Parameter p) { result.getParameter() = p } +SourceParameterNode parameterNode(Parameter p) { result.getParameter() = p } /** A data flow node that represents a call argument. */ class ArgumentNode extends Node { - ArgumentNode() { this = any(DataFlowCall c).getArg(_) } + ArgumentNode() { this = any(DataFlowSourceCall c).getArg(_) } /** Holds if this argument occurs at the given position in the given call. */ - predicate argumentOf(DataFlowCall call, int pos) { this = call.getArg(pos) } + predicate argumentOf(DataFlowSourceCall call, int pos) { this = call.getArg(pos) } /** Gets the call in which this node is an argument. */ - final DataFlowCall getCall() { this.argumentOf(result, _) } + final DataFlowSourceCall getCall() { this.argumentOf(result, _) } } /** diff --git a/python/ql/lib/semmle/python/dataflow/new/internal/FlowSummaryImpl.qll b/python/ql/lib/semmle/python/dataflow/new/internal/FlowSummaryImpl.qll new file mode 100644 index 00000000000..36cfb2c98b4 --- /dev/null +++ b/python/ql/lib/semmle/python/dataflow/new/internal/FlowSummaryImpl.qll @@ -0,0 +1,1155 @@ +/** + * Provides classes and predicates for defining flow summaries. + * + * The definitions in this file are language-independent, and language-specific + * definitions are passed in via the `DataFlowImplSpecific` and + * `FlowSummaryImplSpecific` modules. + */ + +private import FlowSummaryImplSpecific +private import DataFlowImplSpecific::Private +private import DataFlowImplSpecific::Public +private import DataFlowImplCommon + +/** Provides classes and predicates for defining flow summaries. */ +module Public { + private import Private + + /** + * A component used in a flow summary. + * + * Either a parameter or an argument at a given position, a specific + * content type, or a return kind. + */ + class SummaryComponent extends TSummaryComponent { + /** Gets a textual representation of this summary component. */ + string toString() { + exists(Content c | this = TContentSummaryComponent(c) and result = c.toString()) + or + exists(ArgumentPosition pos | + this = TParameterSummaryComponent(pos) and result = "parameter " + pos + ) + or + exists(ParameterPosition pos | + this = TArgumentSummaryComponent(pos) and result = "argument " + pos + ) + or + exists(ReturnKind rk | this = TReturnSummaryComponent(rk) and result = "return (" + rk + ")") + } + } + + /** Provides predicates for constructing summary components. */ + module SummaryComponent { + /** Gets a summary component for content `c`. */ + SummaryComponent content(Content c) { result = TContentSummaryComponent(c) } + + /** Gets a summary component for a parameter at position `pos`. */ + SummaryComponent parameter(ArgumentPosition pos) { result = TParameterSummaryComponent(pos) } + + /** Gets a summary component for an argument at position `pos`. */ + SummaryComponent argument(ParameterPosition pos) { result = TArgumentSummaryComponent(pos) } + + /** Gets a summary component for a return of kind `rk`. */ + SummaryComponent return(ReturnKind rk) { result = TReturnSummaryComponent(rk) } + } + + /** + * A (non-empty) stack of summary components. + * + * A stack is used to represent where data is read from (input) or where it + * is written to (output). For example, an input stack `[Field f, Argument 0]` + * means that data is read from field `f` from the `0`th argument, while an + * output stack `[Field g, Return]` means that data is written to the field + * `g` of the returned object. + */ + class SummaryComponentStack extends TSummaryComponentStack { + /** Gets the head of this stack. */ + SummaryComponent head() { + this = TSingletonSummaryComponentStack(result) or + this = TConsSummaryComponentStack(result, _) + } + + /** Gets the tail of this stack, if any. */ + SummaryComponentStack tail() { this = TConsSummaryComponentStack(_, result) } + + /** Gets the length of this stack. */ + int length() { + this = TSingletonSummaryComponentStack(_) and result = 1 + or + result = 1 + this.tail().length() + } + + /** Gets the stack obtained by dropping the first `i` elements, if any. */ + SummaryComponentStack drop(int i) { + i = 0 and result = this + or + result = this.tail().drop(i - 1) + } + + /** Holds if this stack contains summary component `c`. */ + predicate contains(SummaryComponent c) { c = this.drop(_).head() } + + /** Gets the bottom element of this stack. */ + SummaryComponent bottom() { + this = TSingletonSummaryComponentStack(result) or result = this.tail().bottom() + } + + /** Gets a textual representation of this stack. */ + string toString() { + exists(SummaryComponent head, SummaryComponentStack tail | + head = this.head() and + tail = this.tail() and + result = tail + "." + head + ) + or + exists(SummaryComponent c | + this = TSingletonSummaryComponentStack(c) and + result = c.toString() + ) + } + } + + /** Provides predicates for constructing stacks of summary components. */ + module SummaryComponentStack { + /** Gets a singleton stack containing `c`. */ + SummaryComponentStack singleton(SummaryComponent c) { + result = TSingletonSummaryComponentStack(c) + } + + /** + * Gets the stack obtained by pushing `head` onto `tail`. + * + * Make sure to override `RequiredSummaryComponentStack::required()` in order + * to ensure that the constructed stack exists. + */ + SummaryComponentStack push(SummaryComponent head, SummaryComponentStack tail) { + result = TConsSummaryComponentStack(head, tail) + } + + /** Gets a singleton stack for an argument at position `pos`. */ + SummaryComponentStack argument(ParameterPosition pos) { + result = singleton(SummaryComponent::argument(pos)) + } + + /** Gets a singleton stack representing a return of kind `rk`. */ + SummaryComponentStack return(ReturnKind rk) { result = singleton(SummaryComponent::return(rk)) } + } + + private predicate noComponentSpecificCsv(SummaryComponent sc) { + not exists(getComponentSpecificCsv(sc)) + } + + /** Gets a textual representation of this component used for flow summaries. */ + private string getComponentCsv(SummaryComponent sc) { + result = getComponentSpecificCsv(sc) + or + noComponentSpecificCsv(sc) and + ( + exists(ArgumentPosition pos | + sc = TParameterSummaryComponent(pos) and + result = "Parameter[" + getArgumentPositionCsv(pos) + "]" + ) + or + exists(ParameterPosition pos | + sc = TArgumentSummaryComponent(pos) and + result = "Argument[" + getParameterPositionCsv(pos) + "]" + ) + or + sc = TReturnSummaryComponent(getReturnValueKind()) and result = "ReturnValue" + ) + } + + /** Gets a textual representation of this stack used for flow summaries. */ + string getComponentStackCsv(SummaryComponentStack stack) { + exists(SummaryComponent head, SummaryComponentStack tail | + head = stack.head() and + tail = stack.tail() and + result = getComponentStackCsv(tail) + "." + getComponentCsv(head) + ) + or + exists(SummaryComponent c | + stack = TSingletonSummaryComponentStack(c) and + result = getComponentCsv(c) + ) + } + + /** + * A class that exists for QL technical reasons only (the IPA type used + * to represent component stacks needs to be bounded). + */ + class RequiredSummaryComponentStack extends Unit { + /** + * Holds if the stack obtained by pushing `head` onto `tail` is required. + */ + abstract predicate required(SummaryComponent head, SummaryComponentStack tail); + } + + /** A callable with a flow summary. */ + abstract class SummarizedCallable extends DataFlowCallable { + /** + * Holds if data may flow from `input` to `output` through this callable. + * + * `preservesValue` indicates whether this is a value-preserving step + * or a taint-step. + * + * Input specifications are restricted to stacks that end with + * `SummaryComponent::argument(_)`, preceded by zero or more + * `SummaryComponent::return(_)` or `SummaryComponent::content(_)` components. + * + * Output specifications are restricted to stacks that end with + * `SummaryComponent::return(_)` or `SummaryComponent::argument(_)`. + * + * Output stacks ending with `SummaryComponent::return(_)` can be preceded by zero + * or more `SummaryComponent::content(_)` components. + * + * Output stacks ending with `SummaryComponent::argument(_)` can be preceded by an + * optional `SummaryComponent::parameter(_)` component, which in turn can be preceded + * by zero or more `SummaryComponent::content(_)` components. + */ + pragma[nomagic] + predicate propagatesFlow( + SummaryComponentStack input, SummaryComponentStack output, boolean preservesValue + ) { + none() + } + + /** + * Holds if values stored inside `content` are cleared on objects passed as + * arguments at position `pos` to this callable. + */ + pragma[nomagic] + predicate clearsContent(ParameterPosition pos, Content content) { none() } + } +} + +/** + * Provides predicates for compiling flow summaries down to atomic local steps, + * read steps, and store steps. + */ +module Private { + private import Public + import AccessPathSyntax + + newtype TSummaryComponent = + TContentSummaryComponent(Content c) or + TParameterSummaryComponent(ArgumentPosition pos) or + TArgumentSummaryComponent(ParameterPosition pos) or + TReturnSummaryComponent(ReturnKind rk) + + private TParameterSummaryComponent thisParam() { + result = TParameterSummaryComponent(instanceParameterPosition()) + } + + newtype TSummaryComponentStack = + TSingletonSummaryComponentStack(SummaryComponent c) or + TConsSummaryComponentStack(SummaryComponent head, SummaryComponentStack tail) { + any(RequiredSummaryComponentStack x).required(head, tail) + or + any(RequiredSummaryComponentStack x).required(TParameterSummaryComponent(_), tail) and + head = thisParam() + or + derivedFluentFlowPush(_, _, _, head, tail, _) + } + + pragma[nomagic] + private predicate summary( + SummarizedCallable c, SummaryComponentStack input, SummaryComponentStack output, + boolean preservesValue + ) { + c.propagatesFlow(input, output, preservesValue) + or + // observe side effects of callbacks on input arguments + c.propagatesFlow(output, input, preservesValue) and + preservesValue = true and + isCallbackParameter(input) and + isContentOfArgument(output, _) + or + // flow from the receiver of a callback into the instance-parameter + exists(SummaryComponentStack s, SummaryComponentStack callbackRef | + c.propagatesFlow(s, _, _) or c.propagatesFlow(_, s, _) + | + callbackRef = s.drop(_) and + (isCallbackParameter(callbackRef) or callbackRef.head() = TReturnSummaryComponent(_)) and + input = callbackRef.tail() and + output = TConsSummaryComponentStack(thisParam(), input) and + preservesValue = true + ) + or + exists(SummaryComponentStack arg, SummaryComponentStack return | + derivedFluentFlow(c, input, arg, return, preservesValue) + | + arg.length() = 1 and + output = return + or + exists(SummaryComponent head, SummaryComponentStack tail | + derivedFluentFlowPush(c, input, arg, head, tail, 0) and + output = SummaryComponentStack::push(head, tail) + ) + ) + or + // Chain together summaries where values get passed into callbacks along the way + exists(SummaryComponentStack mid, boolean preservesValue1, boolean preservesValue2 | + c.propagatesFlow(input, mid, preservesValue1) and + c.propagatesFlow(mid, output, preservesValue2) and + mid.drop(mid.length() - 2) = + SummaryComponentStack::push(TParameterSummaryComponent(_), + SummaryComponentStack::singleton(TArgumentSummaryComponent(_))) and + preservesValue = preservesValue1.booleanAnd(preservesValue2) + ) + } + + /** + * Holds if `c` has a flow summary from `input` to `arg`, where `arg` + * writes to (contents of) arguments at position `pos`, and `c` has a + * value-preserving flow summary from the arguments at position `pos` + * to a return value (`return`). + * + * In such a case, we derive flow from `input` to (contents of) the return + * value. + * + * As an example, this simplifies modeling of fluent methods: + * for `StringBuilder.append(x)` with a specified value flow from qualifier to + * return value and taint flow from argument 0 to the qualifier, then this + * allows us to infer taint flow from argument 0 to the return value. + */ + pragma[nomagic] + private predicate derivedFluentFlow( + SummarizedCallable c, SummaryComponentStack input, SummaryComponentStack arg, + SummaryComponentStack return, boolean preservesValue + ) { + exists(ParameterPosition pos | + summary(c, input, arg, preservesValue) and + isContentOfArgument(arg, pos) and + summary(c, SummaryComponentStack::argument(pos), return, true) and + return.bottom() = TReturnSummaryComponent(_) + ) + } + + pragma[nomagic] + private predicate derivedFluentFlowPush( + SummarizedCallable c, SummaryComponentStack input, SummaryComponentStack arg, + SummaryComponent head, SummaryComponentStack tail, int i + ) { + derivedFluentFlow(c, input, arg, tail, _) and + head = arg.drop(i).head() and + i = arg.length() - 2 + or + exists(SummaryComponent head0, SummaryComponentStack tail0 | + derivedFluentFlowPush(c, input, arg, head0, tail0, i + 1) and + head = arg.drop(i).head() and + tail = SummaryComponentStack::push(head0, tail0) + ) + } + + private predicate isCallbackParameter(SummaryComponentStack s) { + s.head() = TParameterSummaryComponent(_) and exists(s.tail()) + } + + private predicate isContentOfArgument(SummaryComponentStack s, ParameterPosition pos) { + s.head() = TContentSummaryComponent(_) and isContentOfArgument(s.tail(), pos) + or + s = SummaryComponentStack::argument(pos) + } + + private predicate outputState(SummarizedCallable c, SummaryComponentStack s) { + summary(c, _, s, _) + or + exists(SummaryComponentStack out | + outputState(c, out) and + out.head() = TContentSummaryComponent(_) and + s = out.tail() + ) + or + // Add the argument node corresponding to the requested post-update node + inputState(c, s) and isCallbackParameter(s) + } + + private predicate inputState(SummarizedCallable c, SummaryComponentStack s) { + summary(c, s, _, _) + or + exists(SummaryComponentStack inp | inputState(c, inp) and s = inp.tail()) + or + exists(SummaryComponentStack out | + outputState(c, out) and + out.head() = TParameterSummaryComponent(_) and + s = out.tail() + ) + } + + private newtype TSummaryNodeState = + TSummaryNodeInputState(SummaryComponentStack s) { inputState(_, s) } or + TSummaryNodeOutputState(SummaryComponentStack s) { outputState(_, s) } or + TSummaryNodeClearsContentState(ParameterPosition pos, boolean post) { + any(SummarizedCallable sc).clearsContent(pos, _) and post in [false, true] + } + + /** + * A state used to break up (complex) flow summaries into atomic flow steps. + * For a flow summary + * + * ```ql + * propagatesFlow( + * SummaryComponentStack input, SummaryComponentStack output, boolean preservesValue + * ) + * ``` + * + * the following states are used: + * + * - `TSummaryNodeInputState(SummaryComponentStack s)`: + * this state represents that the components in `s` _have been read_ from the + * input. + * - `TSummaryNodeOutputState(SummaryComponentStack s)`: + * this state represents that the components in `s` _remain to be written_ to + * the output. + */ + class SummaryNodeState extends TSummaryNodeState { + /** Holds if this state is a valid input state for `c`. */ + pragma[nomagic] + predicate isInputState(SummarizedCallable c, SummaryComponentStack s) { + this = TSummaryNodeInputState(s) and + inputState(c, s) + } + + /** Holds if this state is a valid output state for `c`. */ + pragma[nomagic] + predicate isOutputState(SummarizedCallable c, SummaryComponentStack s) { + this = TSummaryNodeOutputState(s) and + outputState(c, s) + } + + /** Gets a textual representation of this state. */ + string toString() { + exists(SummaryComponentStack s | + this = TSummaryNodeInputState(s) and + result = "read: " + s + ) + or + exists(SummaryComponentStack s | + this = TSummaryNodeOutputState(s) and + result = "to write: " + s + ) + or + exists(ParameterPosition pos, boolean post, string postStr | + this = TSummaryNodeClearsContentState(pos, post) and + (if post = true then postStr = " (post)" else postStr = "") and + result = "clear: " + pos + postStr + ) + } + } + + /** + * Holds if `state` represents having read from a parameter at position + * `pos` in `c`. In this case we are not synthesizing a data-flow node, + * but instead assume that a relevant parameter node already exists. + */ + private predicate parameterReadState( + SummarizedCallable c, SummaryNodeState state, ParameterPosition pos + ) { + state.isInputState(c, SummaryComponentStack::argument(pos)) + } + + /** + * Holds if a synthesized summary node is needed for the state `state` in summarized + * callable `c`. + */ + predicate summaryNodeRange(SummarizedCallable c, SummaryNodeState state) { + state.isInputState(c, _) and + not parameterReadState(c, state, _) + or + state.isOutputState(c, _) + or + exists(ParameterPosition pos | + c.clearsContent(pos, _) and + state = TSummaryNodeClearsContentState(pos, _) + ) + } + + pragma[noinline] + private Node summaryNodeInputState(SummarizedCallable c, SummaryComponentStack s) { + exists(SummaryNodeState state | state.isInputState(c, s) | + result = summaryNode(c, state) + or + exists(ParameterPosition pos | + parameterReadState(c, state, pos) and + result.(ParamNode).isParameterOf(c, pos) + ) + ) + } + + pragma[noinline] + private Node summaryNodeOutputState(SummarizedCallable c, SummaryComponentStack s) { + exists(SummaryNodeState state | + state.isOutputState(c, s) and + result = summaryNode(c, state) + ) + } + + /** + * Holds if a write targets `post`, which is a post-update node for a + * parameter at position `pos` in `c`. + */ + private predicate isParameterPostUpdate(Node post, SummarizedCallable c, ParameterPosition pos) { + post = summaryNodeOutputState(c, SummaryComponentStack::argument(pos)) + } + + /** Holds if a parameter node at position `pos` is required for `c`. */ + predicate summaryParameterNodeRange(SummarizedCallable c, ParameterPosition pos) { + parameterReadState(c, _, pos) + or + isParameterPostUpdate(_, c, pos) + or + c.clearsContent(pos, _) + } + + private predicate callbackOutput( + SummarizedCallable c, SummaryComponentStack s, Node receiver, ReturnKind rk + ) { + any(SummaryNodeState state).isInputState(c, s) and + s.head() = TReturnSummaryComponent(rk) and + receiver = summaryNodeInputState(c, s.drop(1)) + } + + private predicate callbackInput( + SummarizedCallable c, SummaryComponentStack s, Node receiver, ArgumentPosition pos + ) { + any(SummaryNodeState state).isOutputState(c, s) and + s.head() = TParameterSummaryComponent(pos) and + receiver = summaryNodeInputState(c, s.drop(1)) + } + + /** Holds if a call targeting `receiver` should be synthesized inside `c`. */ + predicate summaryCallbackRange(SummarizedCallable c, Node receiver) { + callbackOutput(c, _, receiver, _) + or + callbackInput(c, _, receiver, _) + } + + /** + * Gets the type of synthesized summary node `n`. + * + * The type is computed based on the language-specific predicates + * `getContentType()`, `getReturnType()`, `getCallbackParameterType()`, and + * `getCallbackReturnType()`. + */ + DataFlowType summaryNodeType(Node n) { + exists(Node pre | + summaryPostUpdateNode(n, pre) and + result = getNodeType(pre) + ) + or + exists(SummarizedCallable c, SummaryComponentStack s, SummaryComponent head | head = s.head() | + n = summaryNodeInputState(c, s) and + ( + exists(Content cont | + head = TContentSummaryComponent(cont) and result = getContentType(cont) + ) + or + exists(ReturnKind rk | + head = TReturnSummaryComponent(rk) and + result = + getCallbackReturnType(getNodeType(summaryNodeInputState(pragma[only_bind_out](c), + s.drop(1))), rk) + ) + ) + or + n = summaryNodeOutputState(c, s) and + ( + exists(Content cont | + head = TContentSummaryComponent(cont) and result = getContentType(cont) + ) + or + s.length() = 1 and + exists(ReturnKind rk | + head = TReturnSummaryComponent(rk) and + result = getReturnType(c, rk) + ) + or + exists(ArgumentPosition pos | head = TParameterSummaryComponent(pos) | + result = + getCallbackParameterType(getNodeType(summaryNodeInputState(pragma[only_bind_out](c), + s.drop(1))), pos) + ) + ) + ) + or + exists(SummarizedCallable c, ParameterPosition pos, ParamNode p | + n = summaryNode(c, TSummaryNodeClearsContentState(pos, false)) and + p.isParameterOf(c, pos) and + result = getNodeType(p) + ) + } + + /** Holds if summary node `out` contains output of kind `rk` from call `c`. */ + predicate summaryOutNode(DataFlowCall c, Node out, ReturnKind rk) { + exists(SummarizedCallable callable, SummaryComponentStack s, Node receiver | + callbackOutput(callable, s, receiver, rk) and + out = summaryNodeInputState(callable, s) and + c = summaryDataFlowCall(receiver) + ) + } + + /** Holds if summary node `arg` is at position `pos` in the call `c`. */ + predicate summaryArgumentNode(DataFlowCall c, Node arg, ArgumentPosition pos) { + exists(SummarizedCallable callable, SummaryComponentStack s, Node receiver | + callbackInput(callable, s, receiver, pos) and + arg = summaryNodeOutputState(callable, s) and + c = summaryDataFlowCall(receiver) + ) + } + + /** Holds if summary node `post` is a post-update node with pre-update node `pre`. */ + predicate summaryPostUpdateNode(Node post, Node pre) { + exists(SummarizedCallable c, ParameterPosition pos | + isParameterPostUpdate(post, c, pos) and + pre.(ParamNode).isParameterOf(c, pos) + or + pre = summaryNode(c, TSummaryNodeClearsContentState(pos, false)) and + post = summaryNode(c, TSummaryNodeClearsContentState(pos, true)) + ) + or + exists(SummarizedCallable callable, SummaryComponentStack s | + callbackInput(callable, s, _, _) and + pre = summaryNodeOutputState(callable, s) and + post = summaryNodeInputState(callable, s) + ) + } + + /** Holds if summary node `ret` is a return node of kind `rk`. */ + predicate summaryReturnNode(Node ret, ReturnKind rk) { + exists(SummarizedCallable callable, SummaryComponentStack s | + ret = summaryNodeOutputState(callable, s) and + s = TSingletonSummaryComponentStack(TReturnSummaryComponent(rk)) + ) + } + + /** + * Holds if flow is allowed to pass from parameter `p`, to a return + * node, and back out to `p`. + */ + predicate summaryAllowParameterReturnInSelf(ParamNode p) { + exists(SummarizedCallable c, ParameterPosition ppos | p.isParameterOf(c, ppos) | + c.clearsContent(ppos, _) + or + exists(SummaryComponentStack inputContents, SummaryComponentStack outputContents | + summary(c, inputContents, outputContents, _) and + inputContents.bottom() = pragma[only_bind_into](TArgumentSummaryComponent(ppos)) and + outputContents.bottom() = pragma[only_bind_into](TArgumentSummaryComponent(ppos)) + ) + ) + } + + /** Provides a compilation of flow summaries to atomic data-flow steps. */ + module Steps { + /** + * Holds if there is a local step from `pred` to `succ`, which is synthesized + * from a flow summary. + */ + predicate summaryLocalStep(Node pred, Node succ, boolean preservesValue) { + exists( + SummarizedCallable c, SummaryComponentStack inputContents, + SummaryComponentStack outputContents + | + summary(c, inputContents, outputContents, preservesValue) and + pred = summaryNodeInputState(c, inputContents) and + succ = summaryNodeOutputState(c, outputContents) + | + preservesValue = true + or + preservesValue = false and not summary(c, inputContents, outputContents, true) + ) + or + exists(SummarizedCallable c, ParameterPosition pos | + pred.(ParamNode).isParameterOf(c, pos) and + succ = summaryNode(c, TSummaryNodeClearsContentState(pos, _)) and + preservesValue = true + ) + } + + /** + * Holds if there is a read step of content `c` from `pred` to `succ`, which + * is synthesized from a flow summary. + */ + predicate summaryReadStep(Node pred, Content c, Node succ) { + exists(SummarizedCallable sc, SummaryComponentStack s | + pred = summaryNodeInputState(sc, s.drop(1)) and + succ = summaryNodeInputState(sc, s) and + SummaryComponent::content(c) = s.head() + ) + } + + /** + * Holds if there is a store step of content `c` from `pred` to `succ`, which + * is synthesized from a flow summary. + */ + predicate summaryStoreStep(Node pred, Content c, Node succ) { + exists(SummarizedCallable sc, SummaryComponentStack s | + pred = summaryNodeOutputState(sc, s) and + succ = summaryNodeOutputState(sc, s.drop(1)) and + SummaryComponent::content(c) = s.head() + ) + } + + /** + * Holds if values stored inside content `c` are cleared at `n`. `n` is a + * synthesized summary node, so in order for values to be cleared at calls + * to the relevant method, it is important that flow does not pass over + * the argument, either via use-use flow or def-use flow. + * + * Example: + * + * ``` + * a.b = taint; + * a.clearB(); // assume we have a flow summary for `clearB` that clears `b` on the qualifier + * sink(a.b); + * ``` + * + * In the above, flow should not pass from `a` on the first line (or the second + * line) to `a` on the third line. Instead, there will be synthesized flow from + * `a` on line 2 to the post-update node for `a` on that line (via an intermediate + * node where field `b` is cleared). + */ + predicate summaryClearsContent(Node n, Content c) { + exists(SummarizedCallable sc, ParameterPosition pos | + n = summaryNode(sc, TSummaryNodeClearsContentState(pos, true)) and + sc.clearsContent(pos, c) + ) + } + + pragma[noinline] + private predicate viableParam( + DataFlowCall call, SummarizedCallable sc, ParameterPosition ppos, ParamNode p + ) { + p.isParameterOf(sc, ppos) and + sc = viableCallable(call) + } + + /** + * Holds if values stored inside content `c` are cleared inside a + * callable to which `arg` is an argument. + * + * In such cases, it is important to prevent use-use flow out of + * `arg` (see comment for `summaryClearsContent`). + */ + predicate summaryClearsContentArg(ArgNode arg, Content c) { + exists(DataFlowCall call, SummarizedCallable sc, ParameterPosition ppos | + argumentPositionMatch(call, arg, ppos) and + viableParam(call, sc, ppos, _) and + sc.clearsContent(ppos, c) + ) + } + + pragma[nomagic] + private ParamNode summaryArgParam0(DataFlowCall call, ArgNode arg) { + exists(ParameterPosition ppos, SummarizedCallable sc | + argumentPositionMatch(call, arg, ppos) and + viableParam(call, sc, ppos, result) + ) + } + + pragma[nomagic] + private ParamNode summaryArgParam(ArgNode arg, ReturnKindExt rk, OutNodeExt out) { + exists(DataFlowCall call | + result = summaryArgParam0(call, arg) and + out = rk.getAnOutNode(call) + ) + } + + /** + * Holds if `arg` flows to `out` using a simple flow summary, that is, a flow + * summary without reads and stores. + * + * NOTE: This step should not be used in global data-flow/taint-tracking, but may + * be useful to include in the exposed local data-flow/taint-tracking relations. + */ + predicate summaryThroughStep(ArgNode arg, Node out, boolean preservesValue) { + exists(ReturnKindExt rk, ReturnNodeExt ret | + summaryLocalStep(summaryArgParam(arg, rk, out), ret, preservesValue) and + ret.getKind() = rk + ) + } + + /** + * Holds if there is a read(+taint) of `c` from `arg` to `out` using a + * flow summary. + * + * NOTE: This step should not be used in global data-flow/taint-tracking, but may + * be useful to include in the exposed local data-flow/taint-tracking relations. + */ + predicate summaryGetterStep(ArgNode arg, Content c, Node out) { + exists(ReturnKindExt rk, Node mid, ReturnNodeExt ret | + summaryReadStep(summaryArgParam(arg, rk, out), c, mid) and + summaryLocalStep(mid, ret, _) and + ret.getKind() = rk + ) + } + + /** + * Holds if there is a (taint+)store of `arg` into content `c` of `out` using a + * flow summary. + * + * NOTE: This step should not be used in global data-flow/taint-tracking, but may + * be useful to include in the exposed local data-flow/taint-tracking relations. + */ + predicate summarySetterStep(ArgNode arg, Content c, Node out) { + exists(ReturnKindExt rk, Node mid, ReturnNodeExt ret | + summaryLocalStep(summaryArgParam(arg, rk, out), mid, _) and + summaryStoreStep(mid, c, ret) and + ret.getKind() = rk + ) + } + } + + /** + * Provides a means of translating externally (e.g., CSV) defined flow + * summaries into a `SummarizedCallable`s. + */ + module External { + /** Holds if `spec` is a relevant external specification. */ + private predicate relevantSpec(string spec) { + summaryElement(_, spec, _, _) or + summaryElement(_, _, spec, _) or + sourceElement(_, spec, _) or + sinkElement(_, spec, _) + } + + private class AccessPathRange extends AccessPath::Range { + AccessPathRange() { relevantSpec(this) } + } + + /** Holds if specification component `c` parses as parameter `n`. */ + predicate parseParam(AccessPathToken token, ArgumentPosition pos) { + token.getName() = "Parameter" and + pos = parseParamBody(token.getAnArgument()) + } + + /** Holds if specification component `c` parses as argument `n`. */ + predicate parseArg(AccessPathToken token, ParameterPosition pos) { + token.getName() = "Argument" and + pos = parseArgBody(token.getAnArgument()) + } + + private SummaryComponent interpretComponent(AccessPathToken token) { + exists(ParameterPosition pos | + parseArg(token, pos) and result = SummaryComponent::argument(pos) + ) + or + exists(ArgumentPosition pos | + parseParam(token, pos) and result = SummaryComponent::parameter(pos) + ) + or + token = "ReturnValue" and result = SummaryComponent::return(getReturnValueKind()) + or + result = interpretComponentSpecific(token) + } + + /** + * Holds if `spec` specifies summary component stack `stack`. + */ + predicate interpretSpec(AccessPath spec, SummaryComponentStack stack) { + interpretSpec(spec, spec.getNumToken(), stack) + } + + /** Holds if the first `n` tokens of `spec` resolves to `stack`. */ + private predicate interpretSpec(AccessPath spec, int n, SummaryComponentStack stack) { + n = 1 and + stack = SummaryComponentStack::singleton(interpretComponent(spec.getToken(0))) + or + exists(SummaryComponent head, SummaryComponentStack tail | + interpretSpec(spec, n, head, tail) and + stack = SummaryComponentStack::push(head, tail) + ) + } + + /** Holds if the first `n` tokens of `spec` resolves to `head` followed by `tail` */ + private predicate interpretSpec( + AccessPath spec, int n, SummaryComponent head, SummaryComponentStack tail + ) { + interpretSpec(spec, n - 1, tail) and + head = interpretComponent(spec.getToken(n - 1)) + } + + private class MkStack extends RequiredSummaryComponentStack { + override predicate required(SummaryComponent head, SummaryComponentStack tail) { + interpretSpec(_, _, head, tail) + } + } + + private class SummarizedCallableExternal extends SummarizedCallable { + SummarizedCallableExternal() { summaryElement(this, _, _, _) } + + override predicate propagatesFlow( + SummaryComponentStack input, SummaryComponentStack output, boolean preservesValue + ) { + exists(AccessPath inSpec, AccessPath outSpec, string kind | + summaryElement(this, inSpec, outSpec, kind) and + interpretSpec(inSpec, input) and + interpretSpec(outSpec, output) + | + kind = "value" and preservesValue = true + or + kind = "taint" and preservesValue = false + ) + } + } + + /** Holds if component `c` of specification `spec` cannot be parsed. */ + predicate invalidSpecComponent(AccessPath spec, string c) { + c = spec.getToken(_) and + not exists(interpretComponent(c)) + } + + private predicate inputNeedsReference(AccessPathToken c) { + c.getName() = "Argument" or + inputNeedsReferenceSpecific(c) + } + + private predicate outputNeedsReference(AccessPathToken c) { + c.getName() = ["Argument", "ReturnValue"] or + outputNeedsReferenceSpecific(c) + } + + private predicate sourceElementRef(InterpretNode ref, AccessPath output, string kind) { + exists(SourceOrSinkElement e | + sourceElement(e, output, kind) and + if outputNeedsReference(output.getToken(0)) + then e = ref.getCallTarget() + else e = ref.asElement() + ) + } + + private predicate sinkElementRef(InterpretNode ref, AccessPath input, string kind) { + exists(SourceOrSinkElement e | + sinkElement(e, input, kind) and + if inputNeedsReference(input.getToken(0)) + then e = ref.getCallTarget() + else e = ref.asElement() + ) + } + + /** Holds if the first `n` tokens of `output` resolve to the given interpretation. */ + private predicate interpretOutput( + AccessPath output, int n, InterpretNode ref, InterpretNode node + ) { + sourceElementRef(ref, output, _) and + n = 0 and + ( + if output = "" + then + // Allow language-specific interpretation of the empty access path + interpretOutputSpecific("", ref, node) + else node = ref + ) + or + exists(InterpretNode mid, AccessPathToken c | + interpretOutput(output, n - 1, ref, mid) and + c = output.getToken(n - 1) + | + exists(ArgumentPosition apos, ParameterPosition ppos | + node.asNode().(PostUpdateNode).getPreUpdateNode().(ArgNode).argumentOf(mid.asCall(), apos) and + parameterMatch(ppos, apos) + | + c = "Argument" or parseArg(c, ppos) + ) + or + exists(ArgumentPosition apos, ParameterPosition ppos | + node.asNode().(ParamNode).isParameterOf(mid.asCallable(), ppos) and + parameterMatch(ppos, apos) + | + c = "Parameter" or parseParam(c, apos) + ) + or + c = "ReturnValue" and + node.asNode() = getAnOutNodeExt(mid.asCall(), TValueReturn(getReturnValueKind())) + or + interpretOutputSpecific(c, mid, node) + ) + } + + /** Holds if the first `n` tokens of `input` resolve to the given interpretation. */ + private predicate interpretInput(AccessPath input, int n, InterpretNode ref, InterpretNode node) { + sinkElementRef(ref, input, _) and + n = 0 and + ( + if input = "" + then + // Allow language-specific interpretation of the empty access path + interpretInputSpecific("", ref, node) + else node = ref + ) + or + exists(InterpretNode mid, AccessPathToken c | + interpretInput(input, n - 1, ref, mid) and + c = input.getToken(n - 1) + | + exists(ArgumentPosition apos, ParameterPosition ppos | + node.asNode().(ArgNode).argumentOf(mid.asCall(), apos) and + parameterMatch(ppos, apos) + | + c = "Argument" or parseArg(c, ppos) + ) + or + exists(ReturnNodeExt ret | + c = "ReturnValue" and + ret = node.asNode() and + ret.getKind().(ValueReturnKind).getKind() = getReturnValueKind() and + mid.asCallable() = getNodeEnclosingCallable(ret) + ) + or + interpretInputSpecific(c, mid, node) + ) + } + + /** + * Holds if `node` is specified as a source with the given kind in a CSV flow + * model. + */ + predicate isSourceNode(InterpretNode node, string kind) { + exists(InterpretNode ref, AccessPath output | + sourceElementRef(ref, output, kind) and + interpretOutput(output, output.getNumToken(), ref, node) + ) + } + + /** + * Holds if `node` is specified as a sink with the given kind in a CSV flow + * model. + */ + predicate isSinkNode(InterpretNode node, string kind) { + exists(InterpretNode ref, AccessPath input | + sinkElementRef(ref, input, kind) and + interpretInput(input, input.getNumToken(), ref, node) + ) + } + } + + /** Provides a query predicate for outputting a set of relevant flow summaries. */ + module TestOutput { + /** A flow summary to include in the `summary/3` query predicate. */ + abstract class RelevantSummarizedCallable extends SummarizedCallable { + /** Gets the string representation of this callable used by `summary/1`. */ + abstract string getCallableCsv(); + + /** Holds if flow is propagated between `input` and `output`. */ + predicate relevantSummary( + SummaryComponentStack input, SummaryComponentStack output, boolean preservesValue + ) { + this.propagatesFlow(input, output, preservesValue) + } + } + + /** Render the kind in the format used in flow summaries. */ + private string renderKind(boolean preservesValue) { + preservesValue = true and result = "value" + or + preservesValue = false and result = "taint" + } + + /** + * A query predicate for outputting flow summaries in semi-colon separated format in QL tests. + * The syntax is: "namespace;type;overrides;name;signature;ext;inputspec;outputspec;kind", + * ext is hardcoded to empty. + */ + query predicate summary(string csv) { + exists( + RelevantSummarizedCallable c, SummaryComponentStack input, SummaryComponentStack output, + boolean preservesValue + | + c.relevantSummary(input, output, preservesValue) and + csv = + c.getCallableCsv() + ";" + getComponentStackCsv(input) + ";" + + getComponentStackCsv(output) + ";" + renderKind(preservesValue) + ) + } + } + + /** + * Provides query predicates for rendering the generated data flow graph for + * a summarized callable. + * + * Import this module into a `.ql` file of `@kind graph` to render the graph. + * The graph is restricted to callables from `RelevantSummarizedCallable`. + */ + module RenderSummarizedCallable { + /** A summarized callable to include in the graph. */ + abstract class RelevantSummarizedCallable extends SummarizedCallable { } + + private newtype TNodeOrCall = + MkNode(Node n) { + exists(RelevantSummarizedCallable c | + n = summaryNode(c, _) + or + n.(ParamNode).isParameterOf(c, _) + ) + } or + MkCall(DataFlowCall call) { + call = summaryDataFlowCall(_) and + call.getEnclosingCallable() instanceof RelevantSummarizedCallable + } + + private class NodeOrCall extends TNodeOrCall { + Node asNode() { this = MkNode(result) } + + DataFlowCall asCall() { this = MkCall(result) } + + string toString() { + result = this.asNode().toString() + or + result = this.asCall().toString() + } + + /** + * Holds if this element is at the specified location. + * The location spans column `startcolumn` of line `startline` to + * column `endcolumn` of line `endline` in file `filepath`. + * For more information, see + * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). + */ + predicate hasLocationInfo( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ) { + this.asNode().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) + or + this.asCall().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) + } + } + + query predicate nodes(NodeOrCall n, string key, string val) { + key = "semmle.label" and val = n.toString() + } + + private predicate edgesComponent(NodeOrCall a, NodeOrCall b, string value) { + exists(boolean preservesValue | + Private::Steps::summaryLocalStep(a.asNode(), b.asNode(), preservesValue) and + if preservesValue = true then value = "value" else value = "taint" + ) + or + exists(Content c | + Private::Steps::summaryReadStep(a.asNode(), c, b.asNode()) and + value = "read (" + c + ")" + or + Private::Steps::summaryStoreStep(a.asNode(), c, b.asNode()) and + value = "store (" + c + ")" + or + Private::Steps::summaryClearsContent(a.asNode(), c) and + b = a and + value = "clear (" + c + ")" + ) + or + summaryPostUpdateNode(b.asNode(), a.asNode()) and + value = "post-update" + or + b.asCall() = summaryDataFlowCall(a.asNode()) and + value = "receiver" + or + exists(ArgumentPosition pos | + summaryArgumentNode(b.asCall(), a.asNode(), pos) and + value = "argument (" + pos + ")" + ) + } + + query predicate edges(NodeOrCall a, NodeOrCall b, string key, string value) { + key = "semmle.label" and + value = strictconcat(string s | edgesComponent(a, b, s) | s, " / ") + } + } +} diff --git a/python/ql/lib/semmle/python/dataflow/new/internal/FlowSummaryImplSpecific.qll b/python/ql/lib/semmle/python/dataflow/new/internal/FlowSummaryImplSpecific.qll new file mode 100644 index 00000000000..41fda732cdd --- /dev/null +++ b/python/ql/lib/semmle/python/dataflow/new/internal/FlowSummaryImplSpecific.qll @@ -0,0 +1,175 @@ +/** + * Provides Ruby specific classes and predicates for defining flow summaries. + */ + +private import python +private import DataFlowPrivate +private import DataFlowPublic +private import DataFlowImplCommon +private import FlowSummaryImpl::Private +private import FlowSummaryImpl::Public +private import semmle.python.dataflow.new.FlowSummary as FlowSummary + +/** Gets the parameter position of the instance parameter. */ +ArgumentPosition instanceParameterPosition() { none() } // disables implicit summary flow to `this` for callbacks + +/** Gets the synthesized summary data-flow node for the given values. */ +Node summaryNode(SummarizedCallable c, SummaryNodeState state) { result = TSummaryNode(c, state) } + +/** Gets the synthesized data-flow call for `receiver`. */ +SummaryCall summaryDataFlowCall(Node receiver) { receiver = result.getReceiver() } + +/** Gets the type of content `c`. */ +DataFlowType getContentType(Content c) { any() } + +/** Gets the return type of kind `rk` for callable `c`. */ +bindingset[c, rk] +DataFlowType getReturnType(SummarizedCallable c, ReturnKind rk) { any() } + +/** + * Gets the type of the `i`th parameter in a synthesized call that targets a + * callback of type `t`. + */ +bindingset[t, i] +DataFlowType getCallbackParameterType(DataFlowType t, int i) { any() } + +/** + * Gets the return type of kind `rk` in a synthesized call that targets a + * callback of type `t`. + */ +DataFlowType getCallbackReturnType(DataFlowType t, ReturnKind rk) { any() } + +/** + * Holds if an external flow summary exists for `c` with input specification + * `input`, output specification `output`, and kind `kind`. + */ +predicate summaryElement(DataFlowCallable c, string input, string output, string kind) { + exists(FlowSummary::SummarizedCallable sc, boolean preservesValue | + sc.propagatesFlowExt(input, output, preservesValue) and + c.asLibraryCallable() = sc and + if preservesValue = true then kind = "value" else kind = "taint" + ) +} + +/** + * Gets the summary component for specification component `c`, if any. + * + * This covers all the Python-specific components of a flow summary, and + * is currently empty. + */ +SummaryComponent interpretComponentSpecific(AccessPathToken c) { + c = "ListElement" and + result = FlowSummary::SummaryComponent::listElement() +} + +/** Gets the textual representation of a summary component in the format used for flow summaries. */ +string getComponentSpecificCsv(SummaryComponent sc) { + sc = TContentSummaryComponent(any(ListElementContent c)) and + result = "ListElement" +} + +/** Gets the textual representation of a parameter position in the format used for flow summaries. */ +string getParameterPositionCsv(ParameterPosition pos) { result = pos.toString() } + +/** Gets the textual representation of an argument position in the format used for flow summaries. */ +string getArgumentPositionCsv(ArgumentPosition pos) { result = pos.toString() } + +/** Holds if input specification component `c` needs a reference. */ +predicate inputNeedsReferenceSpecific(string c) { none() } + +/** Holds if output specification component `c` needs a reference. */ +predicate outputNeedsReferenceSpecific(string c) { none() } + +/** Gets the return kind corresponding to specification `"ReturnValue"`. */ +ReturnKind getReturnValueKind() { any() } + +/** + * All definitions in this module are required by the shared implementation + * (for source/sink interpretation), but they are unused for Ruby, where + * we rely on API graphs instead. + */ +private module UnusedSourceSinkInterpretation { + /** + * Holds if an external source specification exists for `e` with output specification + * `output` and kind `kind`. + */ + predicate sourceElement(AstNode n, string output, string kind) { none() } + + /** + * Holds if an external sink specification exists for `n` with input specification + * `input` and kind `kind`. + */ + predicate sinkElement(AstNode n, string input, string kind) { none() } + + class SourceOrSinkElement = AstNode; + + /** An entity used to interpret a source/sink specification. */ + class InterpretNode extends AstNode_ { + /** Gets the element that this node corresponds to, if any. */ + SourceOrSinkElement asElement() { none() } + + /** Gets the data-flow node that this node corresponds to, if any. */ + Node asNode() { none() } + + /** Gets the call that this node corresponds to, if any. */ + DataFlowCall asCall() { none() } + + /** Gets the callable that this node corresponds to, if any. */ + DataFlowCallable asCallable() { none() } + + /** Gets the target of this call, if any. */ + SourceOrSinkElement getCallTarget() { none() } + } + + /** Provides additional sink specification logic. */ + predicate interpretOutputSpecific(string c, InterpretNode mid, InterpretNode node) { none() } + + /** Provides additional source specification logic. */ + predicate interpretInputSpecific(string c, InterpretNode mid, InterpretNode node) { none() } +} + +import UnusedSourceSinkInterpretation + +module ParsePositions { + private import FlowSummaryImpl + + private predicate isParamBody(string body) { + exists(AccessPathToken tok | + tok.getName() = "Parameter" and + body = tok.getAnArgument() + ) + } + + private predicate isArgBody(string body) { + exists(AccessPathToken tok | + tok.getName() = "Argument" and + body = tok.getAnArgument() + ) + } + + predicate isParsedParameterPosition(string c, int i) { + isParamBody(c) and + i = AccessPath::parseInt(c) + } + + predicate isParsedArgumentPosition(string c, int i) { + isArgBody(c) and + i = AccessPath::parseInt(c) + } +} + +/** Gets the argument position obtained by parsing `X` in `Parameter[X]`. */ +ArgumentPosition parseParamBody(string s) { + exists(int i | + ParsePositions::isParsedParameterPosition(s, i) and + result.isPositional(i) + ) +} + +/** Gets the parameter position obtained by parsing `X` in `Argument[X]`. */ +ParameterPosition parseArgBody(string s) { + exists(int i | + ParsePositions::isParsedArgumentPosition(s, i) and + result.isPositional(i) + ) +} diff --git a/python/ql/lib/semmle/python/dataflow/new/internal/TypeTrackerSpecific.qll b/python/ql/lib/semmle/python/dataflow/new/internal/TypeTrackerSpecific.qll index e6db2839263..449c1815416 100644 --- a/python/ql/lib/semmle/python/dataflow/new/internal/TypeTrackerSpecific.qll +++ b/python/ql/lib/semmle/python/dataflow/new/internal/TypeTrackerSpecific.qll @@ -37,7 +37,7 @@ pragma[nomagic] private DataFlowPrivate::DataFlowCallable getCallableForArgument( DataFlowPublic::ArgumentNode nodeFrom, int i ) { - exists(DataFlowPrivate::DataFlowCall call | + exists(DataFlowPrivate::DataFlowSourceCall call | nodeFrom.argumentOf(call, i) and result = call.getCallable() ) @@ -54,7 +54,7 @@ predicate callStep(DataFlowPublic::ArgumentNode nodeFrom, DataFlowPublic::Parame /** Holds if `nodeFrom` steps to `nodeTo` by being returned from a call. */ predicate returnStep(DataFlowPrivate::ReturnNode nodeFrom, Node nodeTo) { - exists(DataFlowPrivate::DataFlowCall call | + exists(DataFlowPrivate::DataFlowSourceCall call | nodeFrom.getEnclosingCallable() = call.getCallable() and nodeTo.asCfgNode() = call.getNode() ) } diff --git a/python/ql/lib/semmle/python/frameworks/Aiohttp.qll b/python/ql/lib/semmle/python/frameworks/Aiohttp.qll index bacf6ea9fc4..3dfb8a78150 100644 --- a/python/ql/lib/semmle/python/frameworks/Aiohttp.qll +++ b/python/ql/lib/semmle/python/frameworks/Aiohttp.qll @@ -442,7 +442,7 @@ module AiohttpWebModel { * handler is invoked. */ class AiohttpRequestHandlerRequestParam extends Request::InstanceSource, RemoteFlowSource::Range, - DataFlow::ParameterNode { + DataFlow::SourceParameterNode { AiohttpRequestHandlerRequestParam() { exists(Function requestHandler | requestHandler = any(AiohttpCoroutineRouteSetup setup).getARequestHandler() and diff --git a/python/ql/lib/semmle/python/frameworks/Django.qll b/python/ql/lib/semmle/python/frameworks/Django.qll index 1d78804d237..c1765605126 100644 --- a/python/ql/lib/semmle/python/frameworks/Django.qll +++ b/python/ql/lib/semmle/python/frameworks/Django.qll @@ -2238,7 +2238,8 @@ module PrivateDjango { * * See https://docs.djangoproject.com/en/3.1/ref/forms/validation/#form-and-field-validation */ - private class DjangoFormFieldValueParam extends RemoteFlowSource::Range, DataFlow::ParameterNode { + private class DjangoFormFieldValueParam extends RemoteFlowSource::Range, + DataFlow::SourceParameterNode { DjangoFormFieldValueParam() { exists(DjangoFormFieldClass cls, Function meth | cls.getAMethod() = meth and @@ -2581,7 +2582,7 @@ module PrivateDjango { // --------------------------------------------------------------------------- /** A parameter that will receive the django `HttpRequest` instance when a request handler is invoked. */ private class DjangoRequestHandlerRequestParam extends DjangoImpl::Http::Request::HttpRequest::InstanceSource, - RemoteFlowSource::Range, DataFlow::ParameterNode { + RemoteFlowSource::Range, DataFlow::SourceParameterNode { DjangoRequestHandlerRequestParam() { this.getParameter() = any(DjangoRouteSetup setup).getARequestHandler().getRequestParam() or diff --git a/python/ql/lib/semmle/python/frameworks/Fabric.qll b/python/ql/lib/semmle/python/frameworks/Fabric.qll index bb1500965f4..1264c6de988 100644 --- a/python/ql/lib/semmle/python/frameworks/Fabric.qll +++ b/python/ql/lib/semmle/python/frameworks/Fabric.qll @@ -176,7 +176,7 @@ private module FabricV2 { } class FabricTaskFirstParamConnectionInstance extends Fabric::Connection::ConnectionClass::InstanceSource, - DataFlow::ParameterNode { + DataFlow::SourceParameterNode { FabricTaskFirstParamConnectionInstance() { exists(Function func | func.getADecorator() = Fabric::Tasks::task().getAUse().asExpr() and diff --git a/python/ql/lib/semmle/python/frameworks/FastApi.qll b/python/ql/lib/semmle/python/frameworks/FastApi.qll index 1c48562eb70..c4c3db565dd 100644 --- a/python/ql/lib/semmle/python/frameworks/FastApi.qll +++ b/python/ql/lib/semmle/python/frameworks/FastApi.qll @@ -88,7 +88,7 @@ private module FastApi { * Pydantic model. */ private class PydanticModelRequestHandlerParam extends Pydantic::BaseModel::InstanceSource, - DataFlow::ParameterNode { + DataFlow::SourceParameterNode { PydanticModelRequestHandlerParam() { this.getParameter().getAnnotation() = Pydantic::BaseModel::subclassRef().getAUse().asExpr() and any(FastApiRouteSetup rs).getARequestHandler().getArgByName(_) = this.getParameter() @@ -102,7 +102,7 @@ private module FastApi { * A parameter to a request handler that has a WebSocket type-annotation. */ private class WebSocketRequestHandlerParam extends Starlette::WebSocket::InstanceSource, - DataFlow::ParameterNode { + DataFlow::SourceParameterNode { WebSocketRequestHandlerParam() { this.getParameter().getAnnotation() = Starlette::WebSocket::classRef().getAUse().asExpr() and any(FastApiRouteSetup rs).getARequestHandler().getArgByName(_) = this.getParameter() @@ -308,7 +308,7 @@ private module FastApi { * A parameter to a FastAPI request-handler that has a `fastapi.Response` * type-annotation. */ - class RequestHandlerParam extends InstanceSource, DataFlow::ParameterNode { + class RequestHandlerParam extends InstanceSource, DataFlow::SourceParameterNode { RequestHandlerParam() { this.getParameter().getAnnotation() = getModeledResponseClass(_).getASubclass*().getAUse().asExpr() and diff --git a/python/ql/lib/semmle/python/frameworks/Invoke.qll b/python/ql/lib/semmle/python/frameworks/Invoke.qll index 435536dda9f..8b99797c511 100644 --- a/python/ql/lib/semmle/python/frameworks/Invoke.qll +++ b/python/ql/lib/semmle/python/frameworks/Invoke.qll @@ -40,7 +40,7 @@ private module Invoke { or exists(Function func | func.getADecorator() = invoke().getMember("task").getAUse().asExpr() and - result.(DataFlow::ParameterNode).getParameter() = func.getArg(0) + result.(DataFlow::SourceParameterNode).getParameter() = func.getArg(0) ) ) or diff --git a/python/ql/lib/semmle/python/frameworks/RestFramework.qll b/python/ql/lib/semmle/python/frameworks/RestFramework.qll index 61f031576de..7dfb5c59e6e 100644 --- a/python/ql/lib/semmle/python/frameworks/RestFramework.qll +++ b/python/ql/lib/semmle/python/frameworks/RestFramework.qll @@ -183,7 +183,7 @@ private module RestFramework { * request handler is invoked. */ private class RestFrameworkRequestHandlerRequestParam extends Request::InstanceSource, - RemoteFlowSource::Range, DataFlow::ParameterNode { + RemoteFlowSource::Range, DataFlow::SourceParameterNode { RestFrameworkRequestHandlerRequestParam() { // rest_framework.views.APIView subclass exists(RestFrameworkApiViewClass vc | diff --git a/python/ql/lib/semmle/python/frameworks/Stdlib.qll b/python/ql/lib/semmle/python/frameworks/Stdlib.qll index ef60841acd6..541f4038e08 100644 --- a/python/ql/lib/semmle/python/frameworks/Stdlib.qll +++ b/python/ql/lib/semmle/python/frameworks/Stdlib.qll @@ -1957,7 +1957,8 @@ private module StdlibPrivate { abstract class InstanceSource extends DataFlow::Node { } /** The `self` parameter in a method on the `BaseHttpRequestHandler` class or any subclass. */ - private class SelfParam extends InstanceSource, RemoteFlowSource::Range, DataFlow::ParameterNode { + private class SelfParam extends InstanceSource, RemoteFlowSource::Range, + DataFlow::SourceParameterNode { SelfParam() { exists(HttpRequestHandlerClassDef cls | cls.getAMethod().getArg(0) = this.getParameter()) } @@ -2085,7 +2086,7 @@ private module StdlibPrivate { * * See https://docs.python.org/3.10/library/wsgiref.html#wsgiref.simple_server.WSGIRequestHandler.get_environ */ - class WSGIEnvirontParameter extends RemoteFlowSource::Range, DataFlow::ParameterNode { + class WSGIEnvirontParameter extends RemoteFlowSource::Range, DataFlow::SourceParameterNode { WSGIEnvirontParameter() { exists(WsgirefSimpleServerApplication func | if func.isMethod() @@ -2109,8 +2110,8 @@ private module StdlibPrivate { t.start() and exists(WsgirefSimpleServerApplication func | if func.isMethod() - then result.(DataFlow::ParameterNode).getParameter() = func.getArg(2) - else result.(DataFlow::ParameterNode).getParameter() = func.getArg(1) + then result.(DataFlow::SourceParameterNode).getParameter() = func.getArg(2) + else result.(DataFlow::SourceParameterNode).getParameter() = func.getArg(1) ) or exists(DataFlow::TypeTracker t2 | result = startResponse(t2).track(t2, t)) diff --git a/python/ql/lib/semmle/python/frameworks/Tornado.qll b/python/ql/lib/semmle/python/frameworks/Tornado.qll index 9c604afc1ec..fe3946505d0 100644 --- a/python/ql/lib/semmle/python/frameworks/Tornado.qll +++ b/python/ql/lib/semmle/python/frameworks/Tornado.qll @@ -127,7 +127,7 @@ private module Tornado { /** The `self` parameter in a method on the `tornado.web.RequestHandler` class or any subclass. */ private class SelfParam extends InstanceSource, RemoteFlowSource::Range, - DataFlow::ParameterNode { + DataFlow::SourceParameterNode { SelfParam() { exists(RequestHandlerClass cls | cls.getAMethod().getArg(0) = this.getParameter()) } diff --git a/python/ql/lib/semmle/python/frameworks/Twisted.qll b/python/ql/lib/semmle/python/frameworks/Twisted.qll index 513f5c942d0..958e402a60b 100644 --- a/python/ql/lib/semmle/python/frameworks/Twisted.qll +++ b/python/ql/lib/semmle/python/frameworks/Twisted.qll @@ -143,7 +143,7 @@ private module Twisted { * when a twisted request handler is called. */ class TwistedResourceRequestHandlerRequestParam extends RemoteFlowSource::Range, - Request::InstanceSource, DataFlow::ParameterNode { + Request::InstanceSource, DataFlow::SourceParameterNode { TwistedResourceRequestHandlerRequestParam() { this.getParameter() = any(TwistedResourceRequestHandler handler).getRequestParameter() } @@ -156,7 +156,7 @@ private module Twisted { * that is also given remote user input. (a bit like RoutedParameter). */ class TwistedResourceRequestHandlerExtraSources extends RemoteFlowSource::Range, - DataFlow::ParameterNode { + DataFlow::SourceParameterNode { TwistedResourceRequestHandlerExtraSources() { exists(TwistedResourceRequestHandler func, int i | func.getName() in ["getChild", "getChildWithDefault"] and i = 1 diff --git a/python/ql/lib/semmle/python/frameworks/internal/PoorMansFunctionResolution.qll b/python/ql/lib/semmle/python/frameworks/internal/PoorMansFunctionResolution.qll index a7abc530230..e0f6d206c41 100644 --- a/python/ql/lib/semmle/python/frameworks/internal/PoorMansFunctionResolution.qll +++ b/python/ql/lib/semmle/python/frameworks/internal/PoorMansFunctionResolution.qll @@ -72,7 +72,7 @@ private DataFlow::Node getSimpleMethodReferenceWithinClass(Function func) { pragma[only_bind_into](cls).getAMethod() = func and pragma[only_bind_into](cls).getAMethod() = otherFunc | - selfRefOtherFunc.getALocalSource().(DataFlow::ParameterNode).getParameter() = + selfRefOtherFunc.getALocalSource().(DataFlow::SourceParameterNode).getParameter() = otherFunc.getArg(0) and result.(DataFlow::AttrRead).accesses(selfRefOtherFunc, func.getName()) ) diff --git a/python/ql/lib/semmle/python/frameworks/internal/SelfRefMixin.qll b/python/ql/lib/semmle/python/frameworks/internal/SelfRefMixin.qll index 2fd9ece8580..4c877258389 100644 --- a/python/ql/lib/semmle/python/frameworks/internal/SelfRefMixin.qll +++ b/python/ql/lib/semmle/python/frameworks/internal/SelfRefMixin.qll @@ -22,7 +22,7 @@ abstract class SelfRefMixin extends Class { */ private DataFlow::TypeTrackingNode getASelfRef(DataFlow::TypeTracker t) { t.start() and - result.(DataFlow::ParameterNode).getParameter() = this.getAMethod().getArg(0) + result.(DataFlow::SourceParameterNode).getParameter() = this.getAMethod().getArg(0) or exists(DataFlow::TypeTracker t2 | result = this.getASelfRef(t2).track(t2, t)) } diff --git a/python/ql/src/Security/CWE-020-ExternalAPIs/ExternalAPIs.qll b/python/ql/src/Security/CWE-020-ExternalAPIs/ExternalAPIs.qll index 120a7967b3a..add8df41202 100644 --- a/python/ql/src/Security/CWE-020-ExternalAPIs/ExternalAPIs.qll +++ b/python/ql/src/Security/CWE-020-ExternalAPIs/ExternalAPIs.qll @@ -65,7 +65,7 @@ private class DefaultSafeExternalApi extends SafeExternalApi { /** A node representing data being passed to an external API through a call. */ class ExternalApiDataNode extends DataFlow::Node { - DataFlowPrivate::DataFlowCall call; + DataFlowPrivate::DataFlowSourceCall call; DataFlowPrivate::DataFlowCallable callable; int i; diff --git a/python/ql/test/2/library-tests/locations/general/Locations.expected b/python/ql/test/2/library-tests/locations/general/Locations.expected index c6473cb2cc2..ffc6ff1ff22 100644 --- a/python/ql/test/2/library-tests/locations/general/Locations.expected +++ b/python/ql/test/2/library-tests/locations/general/Locations.expected @@ -187,6 +187,253 @@ | IntegerLiteral | 103 | 1 | 103 | 1 | | IntegerLiteral | 103 | 4 | 103 | 4 | | IntegerLiteral | 103 | 6 | 103 | 6 | +| InterpretNode | 0 | 0 | 0 | 0 | +| InterpretNode | 2 | 1 | 2 | 1 | +| InterpretNode | 2 | 1 | 2 | 4 | +| InterpretNode | 2 | 3 | 2 | 3 | +| InterpretNode | 3 | 1 | 3 | 1 | +| InterpretNode | 3 | 1 | 3 | 6 | +| InterpretNode | 3 | 3 | 3 | 3 | +| InterpretNode | 3 | 3 | 3 | 5 | +| InterpretNode | 3 | 5 | 3 | 5 | +| InterpretNode | 4 | 1 | 4 | 1 | +| InterpretNode | 4 | 1 | 4 | 7 | +| InterpretNode | 4 | 1 | 4 | 8 | +| InterpretNode | 4 | 3 | 4 | 3 | +| InterpretNode | 4 | 3 | 4 | 6 | +| InterpretNode | 4 | 6 | 4 | 6 | +| InterpretNode | 4 | 7 | 4 | 7 | +| InterpretNode | 5 | 1 | 5 | 1 | +| InterpretNode | 5 | 1 | 5 | 9 | +| InterpretNode | 5 | 3 | 5 | 3 | +| InterpretNode | 5 | 3 | 5 | 8 | +| InterpretNode | 5 | 6 | 5 | 6 | +| InterpretNode | 5 | 6 | 5 | 8 | +| InterpretNode | 5 | 8 | 5 | 8 | +| InterpretNode | 6 | 1 | 6 | 1 | +| InterpretNode | 6 | 1 | 6 | 13 | +| InterpretNode | 6 | 3 | 6 | 3 | +| InterpretNode | 6 | 3 | 6 | 5 | +| InterpretNode | 6 | 3 | 6 | 12 | +| InterpretNode | 6 | 5 | 6 | 5 | +| InterpretNode | 6 | 5 | 6 | 22 | +| InterpretNode | 6 | 8 | 6 | 9 | +| InterpretNode | 6 | 8 | 6 | 12 | +| InterpretNode | 6 | 9 | 6 | 13 | +| InterpretNode | 6 | 11 | 6 | 12 | +| InterpretNode | 6 | 15 | 6 | 16 | +| InterpretNode | 6 | 19 | 6 | 20 | +| InterpretNode | 7 | 1 | 7 | 1 | +| InterpretNode | 7 | 1 | 7 | 14 | +| InterpretNode | 7 | 3 | 7 | 4 | +| InterpretNode | 7 | 3 | 7 | 13 | +| InterpretNode | 7 | 7 | 7 | 9 | +| InterpretNode | 7 | 9 | 7 | 16 | +| InterpretNode | 7 | 12 | 7 | 13 | +| InterpretNode | 8 | 9 | 8 | 21 | +| InterpretNode | 8 | 16 | 8 | 16 | +| InterpretNode | 8 | 16 | 8 | 21 | +| InterpretNode | 8 | 21 | 8 | 21 | +| InterpretNode | 9 | 9 | 9 | 9 | +| InterpretNode | 9 | 9 | 9 | 13 | +| InterpretNode | 9 | 13 | 9 | 13 | +| InterpretNode | 10 | 9 | 10 | 9 | +| InterpretNode | 10 | 9 | 10 | 14 | +| InterpretNode | 10 | 14 | 10 | 14 | +| InterpretNode | 11 | 9 | 11 | 19 | +| InterpretNode | 11 | 15 | 11 | 18 | +| InterpretNode | 12 | 13 | 12 | 17 | +| InterpretNode | 13 | 9 | 13 | 20 | +| InterpretNode | 13 | 15 | 13 | 15 | +| InterpretNode | 13 | 15 | 13 | 19 | +| InterpretNode | 13 | 19 | 13 | 19 | +| InterpretNode | 14 | 13 | 14 | 13 | +| InterpretNode | 14 | 13 | 14 | 18 | +| InterpretNode | 14 | 18 | 14 | 18 | +| InterpretNode | 15 | 13 | 15 | 20 | +| InterpretNode | 16 | 9 | 16 | 35 | +| InterpretNode | 16 | 14 | 16 | 35 | +| InterpretNode | 17 | 9 | 17 | 9 | +| InterpretNode | 17 | 9 | 17 | 11 | +| InterpretNode | 18 | 9 | 18 | 19 | +| InterpretNode | 18 | 13 | 18 | 13 | +| InterpretNode | 18 | 18 | 18 | 18 | +| InterpretNode | 19 | 13 | 19 | 16 | +| InterpretNode | 20 | 9 | 20 | 13 | +| InterpretNode | 20 | 12 | 20 | 12 | +| InterpretNode | 21 | 13 | 21 | 19 | +| InterpretNode | 21 | 19 | 21 | 19 | +| InterpretNode | 22 | 9 | 22 | 16 | +| InterpretNode | 22 | 16 | 22 | 16 | +| InterpretNode | 23 | 9 | 23 | 23 | +| InterpretNode | 23 | 16 | 23 | 18 | +| InterpretNode | 23 | 23 | 23 | 23 | +| InterpretNode | 24 | 9 | 24 | 21 | +| InterpretNode | 24 | 16 | 24 | 16 | +| InterpretNode | 24 | 21 | 24 | 21 | +| InterpretNode | 25 | 9 | 25 | 25 | +| InterpretNode | 25 | 14 | 25 | 16 | +| InterpretNode | 25 | 25 | 25 | 25 | +| InterpretNode | 26 | 9 | 26 | 21 | +| InterpretNode | 26 | 15 | 26 | 21 | +| InterpretNode | 27 | 9 | 27 | 28 | +| InterpretNode | 27 | 18 | 27 | 19 | +| InterpretNode | 27 | 22 | 27 | 28 | +| InterpretNode | 28 | 9 | 28 | 31 | +| InterpretNode | 28 | 14 | 28 | 17 | +| InterpretNode | 28 | 14 | 28 | 25 | +| InterpretNode | 28 | 19 | 28 | 24 | +| InterpretNode | 28 | 30 | 28 | 30 | +| InterpretNode | 29 | 13 | 29 | 16 | +| InterpretNode | 30 | 9 | 30 | 12 | +| InterpretNode | 31 | 13 | 31 | 13 | +| InterpretNode | 31 | 13 | 31 | 15 | +| InterpretNode | 31 | 15 | 31 | 15 | +| InterpretNode | 32 | 9 | 32 | 31 | +| InterpretNode | 32 | 16 | 32 | 24 | +| InterpretNode | 32 | 29 | 32 | 30 | +| InterpretNode | 33 | 13 | 33 | 17 | +| InterpretNode | 33 | 17 | 33 | 17 | +| InterpretNode | 35 | 13 | 35 | 17 | +| InterpretNode | 35 | 17 | 35 | 17 | +| InterpretNode | 36 | 9 | 36 | 13 | +| InterpretNode | 36 | 12 | 36 | 12 | +| InterpretNode | 37 | 13 | 37 | 29 | +| InterpretNode | 37 | 19 | 37 | 27 | +| InterpretNode | 37 | 19 | 37 | 29 | +| InterpretNode | 39 | 13 | 39 | 18 | +| InterpretNode | 41 | 5 | 41 | 22 | +| InterpretNode | 41 | 9 | 41 | 13 | +| InterpretNode | 41 | 15 | 41 | 16 | +| InterpretNode | 41 | 19 | 41 | 20 | +| InterpretNode | 42 | 9 | 42 | 10 | +| InterpretNode | 42 | 9 | 42 | 12 | +| InterpretNode | 42 | 9 | 42 | 16 | +| InterpretNode | 42 | 16 | 42 | 16 | +| InterpretNode | 43 | 9 | 43 | 9 | +| InterpretNode | 43 | 9 | 43 | 16 | +| InterpretNode | 43 | 13 | 43 | 14 | +| InterpretNode | 43 | 13 | 43 | 16 | +| InterpretNode | 44 | 9 | 44 | 9 | +| InterpretNode | 44 | 9 | 44 | 17 | +| InterpretNode | 44 | 13 | 44 | 13 | +| InterpretNode | 44 | 13 | 44 | 17 | +| InterpretNode | 44 | 17 | 44 | 17 | +| InterpretNode | 45 | 9 | 45 | 9 | +| InterpretNode | 45 | 9 | 45 | 23 | +| InterpretNode | 45 | 13 | 45 | 23 | +| InterpretNode | 46 | 9 | 46 | 12 | +| InterpretNode | 46 | 9 | 46 | 56 | +| InterpretNode | 46 | 14 | 46 | 17 | +| InterpretNode | 46 | 20 | 46 | 23 | +| InterpretNode | 46 | 26 | 46 | 35 | +| InterpretNode | 46 | 32 | 46 | 35 | +| InterpretNode | 46 | 38 | 46 | 44 | +| InterpretNode | 46 | 44 | 46 | 44 | +| InterpretNode | 46 | 47 | 46 | 49 | +| InterpretNode | 46 | 48 | 46 | 49 | +| InterpretNode | 46 | 52 | 46 | 55 | +| InterpretNode | 46 | 54 | 46 | 55 | +| InterpretNode | 47 | 9 | 47 | 9 | +| InterpretNode | 47 | 9 | 47 | 13 | +| InterpretNode | 47 | 13 | 47 | 13 | +| InterpretNode | 48 | 9 | 48 | 19 | +| InterpretNode | 48 | 10 | 48 | 10 | +| InterpretNode | 48 | 10 | 48 | 12 | +| InterpretNode | 48 | 12 | 48 | 12 | +| InterpretNode | 48 | 15 | 48 | 15 | +| InterpretNode | 48 | 15 | 48 | 18 | +| InterpretNode | 48 | 18 | 48 | 18 | +| InterpretNode | 50 | 9 | 50 | 9 | +| InterpretNode | 50 | 9 | 50 | 20 | +| InterpretNode | 50 | 11 | 50 | 11 | +| InterpretNode | 50 | 11 | 50 | 19 | +| InterpretNode | 50 | 14 | 50 | 16 | +| InterpretNode | 50 | 19 | 50 | 19 | +| InterpretNode | 51 | 9 | 51 | 22 | +| InterpretNode | 51 | 10 | 51 | 10 | +| InterpretNode | 51 | 10 | 51 | 21 | +| InterpretNode | 51 | 16 | 51 | 16 | +| InterpretNode | 51 | 21 | 51 | 21 | +| InterpretNode | 52 | 9 | 52 | 10 | +| InterpretNode | 52 | 9 | 52 | 27 | +| InterpretNode | 52 | 15 | 52 | 15 | +| InterpretNode | 52 | 15 | 52 | 19 | +| InterpretNode | 52 | 19 | 52 | 19 | +| InterpretNode | 52 | 26 | 52 | 27 | +| InterpretNode | 53 | 9 | 53 | 24 | +| InterpretNode | 53 | 16 | 53 | 16 | +| InterpretNode | 53 | 20 | 53 | 20 | +| InterpretNode | 53 | 20 | 53 | 24 | +| InterpretNode | 53 | 24 | 53 | 24 | +| InterpretNode | 54 | 9 | 54 | 30 | +| InterpretNode | 54 | 11 | 54 | 11 | +| InterpretNode | 54 | 14 | 54 | 14 | +| InterpretNode | 54 | 17 | 54 | 17 | +| InterpretNode | 54 | 20 | 54 | 20 | +| InterpretNode | 54 | 20 | 54 | 22 | +| InterpretNode | 54 | 25 | 54 | 26 | +| InterpretNode | 54 | 25 | 54 | 28 | +| InterpretNode | 55 | 9 | 55 | 36 | +| InterpretNode | 55 | 11 | 55 | 11 | +| InterpretNode | 55 | 11 | 55 | 18 | +| InterpretNode | 55 | 15 | 55 | 18 | +| InterpretNode | 55 | 24 | 55 | 24 | +| InterpretNode | 55 | 29 | 55 | 31 | +| InterpretNode | 55 | 29 | 55 | 34 | +| InterpretNode | 55 | 33 | 55 | 33 | +| InterpretNode | 56 | 9 | 56 | 11 | +| InterpretNode | 56 | 10 | 56 | 10 | +| InterpretNode | 60 | 9 | 60 | 13 | +| InterpretNode | 61 | 9 | 61 | 9 | +| InterpretNode | 61 | 9 | 61 | 12 | +| InterpretNode | 61 | 11 | 61 | 11 | +| InterpretNode | 62 | 9 | 62 | 9 | +| InterpretNode | 62 | 9 | 62 | 15 | +| InterpretNode | 62 | 11 | 62 | 11 | +| InterpretNode | 62 | 11 | 62 | 14 | +| InterpretNode | 62 | 13 | 62 | 13 | +| InterpretNode | 63 | 9 | 63 | 19 | +| InterpretNode | 63 | 10 | 63 | 11 | +| InterpretNode | 63 | 10 | 63 | 18 | +| InterpretNode | 63 | 14 | 63 | 15 | +| InterpretNode | 63 | 18 | 63 | 18 | +| InterpretNode | 66 | 1 | 67 | 23 | +| InterpretNode | 69 | 1 | 71 | 9 | +| InterpretNode | 73 | 1 | 75 | 3 | +| InterpretNode | 77 | 1 | 80 | 3 | +| InterpretNode | 83 | 2 | 83 | 5 | +| InterpretNode | 84 | 1 | 84 | 8 | +| InterpretNode | 84 | 5 | 84 | 5 | +| InterpretNode | 85 | 5 | 85 | 8 | +| InterpretNode | 88 | 1 | 88 | 12 | +| InterpretNode | 88 | 5 | 88 | 9 | +| InterpretNode | 89 | 5 | 89 | 16 | +| InterpretNode | 89 | 9 | 89 | 13 | +| InterpretNode | 90 | 9 | 90 | 12 | +| InterpretNode | 93 | 1 | 98 | 1 | +| InterpretNode | 93 | 2 | 95 | 3 | +| InterpretNode | 94 | 3 | 94 | 3 | +| InterpretNode | 94 | 9 | 94 | 9 | +| InterpretNode | 94 | 14 | 94 | 14 | +| InterpretNode | 97 | 7 | 97 | 7 | +| InterpretNode | 97 | 12 | 97 | 12 | +| InterpretNode | 101 | 1 | 101 | 7 | +| InterpretNode | 101 | 1 | 101 | 13 | +| InterpretNode | 101 | 1 | 101 | 23 | +| InterpretNode | 101 | 11 | 101 | 13 | +| InterpretNode | 101 | 17 | 101 | 23 | +| InterpretNode | 102 | 1 | 102 | 1 | +| InterpretNode | 102 | 1 | 102 | 3 | +| InterpretNode | 102 | 1 | 102 | 7 | +| InterpretNode | 102 | 3 | 102 | 3 | +| InterpretNode | 102 | 5 | 102 | 5 | +| InterpretNode | 102 | 5 | 102 | 7 | +| InterpretNode | 103 | 1 | 103 | 1 | +| InterpretNode | 103 | 1 | 103 | 7 | +| InterpretNode | 103 | 4 | 103 | 4 | +| InterpretNode | 103 | 4 | 103 | 6 | +| InterpretNode | 103 | 6 | 103 | 6 | | KeyValuePair | 48 | 10 | 48 | 12 | | KeyValuePair | 48 | 15 | 48 | 18 | | Keyword | 46 | 26 | 46 | 35 | diff --git a/python/ql/test/3/library-tests/locations/general/Locations.expected b/python/ql/test/3/library-tests/locations/general/Locations.expected index 70217f5117a..80e1a74a809 100644 --- a/python/ql/test/3/library-tests/locations/general/Locations.expected +++ b/python/ql/test/3/library-tests/locations/general/Locations.expected @@ -176,6 +176,245 @@ | IntegerLiteral | 103 | 1 | 103 | 1 | | IntegerLiteral | 103 | 4 | 103 | 4 | | IntegerLiteral | 103 | 6 | 103 | 6 | +| InterpretNode | 0 | 0 | 0 | 0 | +| InterpretNode | 2 | 1 | 2 | 1 | +| InterpretNode | 2 | 1 | 2 | 4 | +| InterpretNode | 2 | 3 | 2 | 3 | +| InterpretNode | 3 | 1 | 3 | 1 | +| InterpretNode | 3 | 1 | 3 | 6 | +| InterpretNode | 3 | 3 | 3 | 3 | +| InterpretNode | 3 | 3 | 3 | 5 | +| InterpretNode | 3 | 5 | 3 | 5 | +| InterpretNode | 4 | 1 | 4 | 1 | +| InterpretNode | 4 | 1 | 4 | 7 | +| InterpretNode | 4 | 1 | 4 | 8 | +| InterpretNode | 4 | 3 | 4 | 3 | +| InterpretNode | 4 | 3 | 4 | 6 | +| InterpretNode | 4 | 6 | 4 | 6 | +| InterpretNode | 4 | 7 | 4 | 7 | +| InterpretNode | 5 | 1 | 5 | 1 | +| InterpretNode | 5 | 1 | 5 | 9 | +| InterpretNode | 5 | 3 | 5 | 3 | +| InterpretNode | 5 | 3 | 5 | 8 | +| InterpretNode | 5 | 6 | 5 | 6 | +| InterpretNode | 5 | 6 | 5 | 8 | +| InterpretNode | 5 | 8 | 5 | 8 | +| InterpretNode | 6 | 1 | 6 | 1 | +| InterpretNode | 6 | 1 | 6 | 13 | +| InterpretNode | 6 | 3 | 6 | 3 | +| InterpretNode | 6 | 3 | 6 | 5 | +| InterpretNode | 6 | 3 | 6 | 12 | +| InterpretNode | 6 | 5 | 6 | 5 | +| InterpretNode | 6 | 5 | 6 | 22 | +| InterpretNode | 6 | 8 | 6 | 9 | +| InterpretNode | 6 | 8 | 6 | 12 | +| InterpretNode | 6 | 9 | 6 | 13 | +| InterpretNode | 6 | 11 | 6 | 12 | +| InterpretNode | 6 | 15 | 6 | 16 | +| InterpretNode | 6 | 19 | 6 | 20 | +| InterpretNode | 7 | 1 | 7 | 1 | +| InterpretNode | 7 | 1 | 7 | 14 | +| InterpretNode | 7 | 3 | 7 | 4 | +| InterpretNode | 7 | 3 | 7 | 13 | +| InterpretNode | 7 | 7 | 7 | 9 | +| InterpretNode | 7 | 9 | 7 | 16 | +| InterpretNode | 7 | 12 | 7 | 13 | +| InterpretNode | 8 | 9 | 8 | 21 | +| InterpretNode | 8 | 16 | 8 | 16 | +| InterpretNode | 8 | 16 | 8 | 21 | +| InterpretNode | 8 | 21 | 8 | 21 | +| InterpretNode | 9 | 9 | 9 | 9 | +| InterpretNode | 9 | 9 | 9 | 13 | +| InterpretNode | 9 | 13 | 9 | 13 | +| InterpretNode | 10 | 9 | 10 | 9 | +| InterpretNode | 10 | 9 | 10 | 14 | +| InterpretNode | 10 | 14 | 10 | 14 | +| InterpretNode | 11 | 9 | 11 | 19 | +| InterpretNode | 11 | 15 | 11 | 18 | +| InterpretNode | 12 | 13 | 12 | 17 | +| InterpretNode | 13 | 9 | 13 | 20 | +| InterpretNode | 13 | 15 | 13 | 15 | +| InterpretNode | 13 | 15 | 13 | 19 | +| InterpretNode | 13 | 19 | 13 | 19 | +| InterpretNode | 14 | 13 | 14 | 13 | +| InterpretNode | 14 | 13 | 14 | 18 | +| InterpretNode | 14 | 18 | 14 | 18 | +| InterpretNode | 15 | 13 | 15 | 20 | +| InterpretNode | 17 | 9 | 17 | 9 | +| InterpretNode | 17 | 9 | 17 | 11 | +| InterpretNode | 18 | 9 | 18 | 19 | +| InterpretNode | 18 | 13 | 18 | 13 | +| InterpretNode | 18 | 18 | 18 | 18 | +| InterpretNode | 19 | 13 | 19 | 16 | +| InterpretNode | 20 | 9 | 20 | 13 | +| InterpretNode | 20 | 12 | 20 | 12 | +| InterpretNode | 21 | 13 | 21 | 17 | +| InterpretNode | 21 | 13 | 21 | 20 | +| InterpretNode | 21 | 19 | 21 | 19 | +| InterpretNode | 22 | 9 | 22 | 16 | +| InterpretNode | 22 | 16 | 22 | 16 | +| InterpretNode | 23 | 9 | 23 | 23 | +| InterpretNode | 23 | 16 | 23 | 18 | +| InterpretNode | 23 | 23 | 23 | 23 | +| InterpretNode | 24 | 9 | 24 | 21 | +| InterpretNode | 24 | 16 | 24 | 16 | +| InterpretNode | 24 | 21 | 24 | 21 | +| InterpretNode | 25 | 9 | 25 | 25 | +| InterpretNode | 25 | 14 | 25 | 16 | +| InterpretNode | 25 | 25 | 25 | 25 | +| InterpretNode | 28 | 9 | 28 | 31 | +| InterpretNode | 28 | 14 | 28 | 17 | +| InterpretNode | 28 | 14 | 28 | 25 | +| InterpretNode | 28 | 19 | 28 | 24 | +| InterpretNode | 28 | 30 | 28 | 30 | +| InterpretNode | 29 | 13 | 29 | 16 | +| InterpretNode | 30 | 9 | 30 | 12 | +| InterpretNode | 31 | 13 | 31 | 13 | +| InterpretNode | 31 | 13 | 31 | 15 | +| InterpretNode | 31 | 15 | 31 | 15 | +| InterpretNode | 32 | 9 | 32 | 31 | +| InterpretNode | 32 | 16 | 32 | 24 | +| InterpretNode | 32 | 29 | 32 | 30 | +| InterpretNode | 33 | 13 | 33 | 17 | +| InterpretNode | 33 | 17 | 33 | 17 | +| InterpretNode | 35 | 13 | 35 | 17 | +| InterpretNode | 35 | 17 | 35 | 17 | +| InterpretNode | 36 | 9 | 36 | 13 | +| InterpretNode | 36 | 12 | 36 | 12 | +| InterpretNode | 37 | 13 | 37 | 29 | +| InterpretNode | 37 | 19 | 37 | 27 | +| InterpretNode | 37 | 19 | 37 | 29 | +| InterpretNode | 39 | 13 | 39 | 18 | +| InterpretNode | 41 | 5 | 41 | 22 | +| InterpretNode | 41 | 9 | 41 | 13 | +| InterpretNode | 41 | 15 | 41 | 16 | +| InterpretNode | 41 | 19 | 41 | 20 | +| InterpretNode | 42 | 9 | 42 | 10 | +| InterpretNode | 42 | 9 | 42 | 12 | +| InterpretNode | 42 | 9 | 42 | 16 | +| InterpretNode | 42 | 16 | 42 | 16 | +| InterpretNode | 43 | 9 | 43 | 9 | +| InterpretNode | 43 | 9 | 43 | 16 | +| InterpretNode | 43 | 13 | 43 | 14 | +| InterpretNode | 43 | 13 | 43 | 16 | +| InterpretNode | 44 | 9 | 44 | 9 | +| InterpretNode | 44 | 9 | 44 | 17 | +| InterpretNode | 44 | 13 | 44 | 13 | +| InterpretNode | 44 | 13 | 44 | 17 | +| InterpretNode | 44 | 17 | 44 | 17 | +| InterpretNode | 45 | 9 | 45 | 9 | +| InterpretNode | 45 | 9 | 45 | 23 | +| InterpretNode | 45 | 13 | 45 | 23 | +| InterpretNode | 46 | 9 | 46 | 12 | +| InterpretNode | 46 | 9 | 46 | 56 | +| InterpretNode | 46 | 14 | 46 | 17 | +| InterpretNode | 46 | 20 | 46 | 23 | +| InterpretNode | 46 | 26 | 46 | 35 | +| InterpretNode | 46 | 32 | 46 | 35 | +| InterpretNode | 46 | 38 | 46 | 44 | +| InterpretNode | 46 | 44 | 46 | 44 | +| InterpretNode | 46 | 47 | 46 | 49 | +| InterpretNode | 46 | 48 | 46 | 49 | +| InterpretNode | 46 | 52 | 46 | 55 | +| InterpretNode | 46 | 54 | 46 | 55 | +| InterpretNode | 47 | 9 | 47 | 9 | +| InterpretNode | 47 | 9 | 47 | 13 | +| InterpretNode | 47 | 13 | 47 | 13 | +| InterpretNode | 48 | 9 | 48 | 19 | +| InterpretNode | 48 | 10 | 48 | 10 | +| InterpretNode | 48 | 10 | 48 | 12 | +| InterpretNode | 48 | 12 | 48 | 12 | +| InterpretNode | 48 | 15 | 48 | 15 | +| InterpretNode | 48 | 15 | 48 | 18 | +| InterpretNode | 48 | 18 | 48 | 18 | +| InterpretNode | 50 | 9 | 50 | 9 | +| InterpretNode | 50 | 9 | 50 | 20 | +| InterpretNode | 50 | 11 | 50 | 11 | +| InterpretNode | 50 | 11 | 50 | 19 | +| InterpretNode | 50 | 14 | 50 | 16 | +| InterpretNode | 50 | 19 | 50 | 19 | +| InterpretNode | 51 | 9 | 51 | 22 | +| InterpretNode | 51 | 10 | 51 | 10 | +| InterpretNode | 51 | 10 | 51 | 21 | +| InterpretNode | 51 | 16 | 51 | 16 | +| InterpretNode | 51 | 21 | 51 | 21 | +| InterpretNode | 52 | 9 | 52 | 10 | +| InterpretNode | 52 | 9 | 52 | 27 | +| InterpretNode | 52 | 15 | 52 | 15 | +| InterpretNode | 52 | 15 | 52 | 19 | +| InterpretNode | 52 | 19 | 52 | 19 | +| InterpretNode | 52 | 26 | 52 | 27 | +| InterpretNode | 53 | 9 | 53 | 24 | +| InterpretNode | 53 | 16 | 53 | 16 | +| InterpretNode | 53 | 20 | 53 | 20 | +| InterpretNode | 53 | 20 | 53 | 24 | +| InterpretNode | 53 | 24 | 53 | 24 | +| InterpretNode | 54 | 9 | 54 | 30 | +| InterpretNode | 54 | 11 | 54 | 11 | +| InterpretNode | 54 | 14 | 54 | 14 | +| InterpretNode | 54 | 17 | 54 | 17 | +| InterpretNode | 54 | 20 | 54 | 20 | +| InterpretNode | 54 | 20 | 54 | 22 | +| InterpretNode | 54 | 25 | 54 | 26 | +| InterpretNode | 54 | 25 | 54 | 28 | +| InterpretNode | 55 | 9 | 55 | 36 | +| InterpretNode | 55 | 11 | 55 | 11 | +| InterpretNode | 55 | 11 | 55 | 18 | +| InterpretNode | 55 | 15 | 55 | 18 | +| InterpretNode | 55 | 24 | 55 | 24 | +| InterpretNode | 55 | 29 | 55 | 31 | +| InterpretNode | 55 | 29 | 55 | 34 | +| InterpretNode | 55 | 33 | 55 | 33 | +| InterpretNode | 60 | 9 | 60 | 13 | +| InterpretNode | 61 | 9 | 61 | 9 | +| InterpretNode | 61 | 9 | 61 | 12 | +| InterpretNode | 61 | 11 | 61 | 11 | +| InterpretNode | 62 | 9 | 62 | 9 | +| InterpretNode | 62 | 9 | 62 | 15 | +| InterpretNode | 62 | 11 | 62 | 11 | +| InterpretNode | 62 | 11 | 62 | 14 | +| InterpretNode | 62 | 13 | 62 | 13 | +| InterpretNode | 63 | 9 | 63 | 19 | +| InterpretNode | 63 | 10 | 63 | 11 | +| InterpretNode | 63 | 10 | 63 | 18 | +| InterpretNode | 63 | 14 | 63 | 15 | +| InterpretNode | 63 | 18 | 63 | 18 | +| InterpretNode | 66 | 1 | 67 | 23 | +| InterpretNode | 69 | 1 | 71 | 9 | +| InterpretNode | 73 | 1 | 75 | 3 | +| InterpretNode | 77 | 1 | 80 | 3 | +| InterpretNode | 83 | 2 | 83 | 5 | +| InterpretNode | 84 | 1 | 84 | 8 | +| InterpretNode | 84 | 5 | 84 | 5 | +| InterpretNode | 85 | 5 | 85 | 8 | +| InterpretNode | 88 | 1 | 88 | 12 | +| InterpretNode | 88 | 5 | 88 | 9 | +| InterpretNode | 89 | 5 | 89 | 16 | +| InterpretNode | 89 | 9 | 89 | 13 | +| InterpretNode | 90 | 9 | 90 | 12 | +| InterpretNode | 93 | 1 | 98 | 1 | +| InterpretNode | 93 | 2 | 95 | 3 | +| InterpretNode | 94 | 3 | 94 | 3 | +| InterpretNode | 94 | 9 | 94 | 9 | +| InterpretNode | 94 | 14 | 94 | 14 | +| InterpretNode | 97 | 7 | 97 | 7 | +| InterpretNode | 97 | 12 | 97 | 12 | +| InterpretNode | 101 | 1 | 101 | 7 | +| InterpretNode | 101 | 1 | 101 | 13 | +| InterpretNode | 101 | 1 | 101 | 23 | +| InterpretNode | 101 | 11 | 101 | 13 | +| InterpretNode | 101 | 17 | 101 | 23 | +| InterpretNode | 102 | 1 | 102 | 1 | +| InterpretNode | 102 | 1 | 102 | 3 | +| InterpretNode | 102 | 1 | 102 | 7 | +| InterpretNode | 102 | 3 | 102 | 3 | +| InterpretNode | 102 | 5 | 102 | 5 | +| InterpretNode | 102 | 5 | 102 | 7 | +| InterpretNode | 103 | 1 | 103 | 1 | +| InterpretNode | 103 | 1 | 103 | 7 | +| InterpretNode | 103 | 4 | 103 | 4 | +| InterpretNode | 103 | 4 | 103 | 6 | +| InterpretNode | 103 | 6 | 103 | 6 | | KeyValuePair | 48 | 10 | 48 | 12 | | KeyValuePair | 48 | 15 | 48 | 18 | | Keyword | 46 | 26 | 46 | 35 | diff --git a/python/ql/test/experimental/dataflow/TestUtil/MaximalFlowTest.qll b/python/ql/test/experimental/dataflow/TestUtil/MaximalFlowTest.qll index 4956aecadc2..a56653dc3d7 100644 --- a/python/ql/test/experimental/dataflow/TestUtil/MaximalFlowTest.qll +++ b/python/ql/test/experimental/dataflow/TestUtil/MaximalFlowTest.qll @@ -25,7 +25,7 @@ class MaximalFlowsConfig extends DataFlow::Configuration { exists(node.getLocation().getFile().getRelativePath()) and not node.asCfgNode() instanceof CallNode and not node.asCfgNode().getNode() instanceof Return and - not node instanceof DataFlow::ParameterNode and + not node instanceof DataFlow::SourceParameterNode and not node instanceof DataFlow::PostUpdateNode and // not node.asExpr() instanceof FunctionExpr and // not node.asExpr() instanceof ClassExpr and diff --git a/python/ql/test/experimental/dataflow/TestUtil/UnresolvedCalls.qll b/python/ql/test/experimental/dataflow/TestUtil/UnresolvedCalls.qll index eaf480ec664..85eb6d2a50f 100644 --- a/python/ql/test/experimental/dataflow/TestUtil/UnresolvedCalls.qll +++ b/python/ql/test/experimental/dataflow/TestUtil/UnresolvedCalls.qll @@ -12,7 +12,7 @@ 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::DataFlowSourceCall dfc | dfc.getNode() = call) and not call = API::builtin(_).getACall().asCfgNode() and location = call.getLocation() and tag = "unresolved_call" and diff --git a/python/ql/test/experimental/dataflow/basic/maximalFlowsConfig.qll b/python/ql/test/experimental/dataflow/basic/maximalFlowsConfig.qll index e3e65c92c24..92788c0db43 100644 --- a/python/ql/test/experimental/dataflow/basic/maximalFlowsConfig.qll +++ b/python/ql/test/experimental/dataflow/basic/maximalFlowsConfig.qll @@ -9,7 +9,7 @@ class MaximalFlowsConfig extends DataFlow::Configuration { MaximalFlowsConfig() { this = "AllFlowsConfig" } override predicate isSource(DataFlow::Node node) { - node instanceof DataFlow::ParameterNode + node instanceof DataFlow::SourceParameterNode or node instanceof DataFlow::EssaNode and not exists(DataFlow::EssaNode pred | DataFlow::localFlowStep(pred, node)) diff --git a/python/ql/test/experimental/dataflow/callGraphConfig.qll b/python/ql/test/experimental/dataflow/callGraphConfig.qll index ceb39bafcd6..897f38cf76d 100644 --- a/python/ql/test/experimental/dataflow/callGraphConfig.qll +++ b/python/ql/test/experimental/dataflow/callGraphConfig.qll @@ -17,6 +17,6 @@ class CallGraphConfig extends DataFlow::Configuration { override predicate isSink(DataFlow::Node node) { node instanceof DataFlowPrivate::OutNode or - node instanceof DataFlow::ParameterNode + node instanceof DataFlow::SourceParameterNode } } diff --git a/python/ql/test/experimental/dataflow/calls/DataFlowCallTest.ql b/python/ql/test/experimental/dataflow/calls/DataFlowCallTest.ql index 4536e8f40ad..4ba1fa0036b 100644 --- a/python/ql/test/experimental/dataflow/calls/DataFlowCallTest.ql +++ b/python/ql/test/experimental/dataflow/calls/DataFlowCallTest.ql @@ -15,7 +15,7 @@ class DataFlowCallTest extends InlineExpectationsTest { override predicate hasActualResult(Location location, string element, string tag, string value) { exists(location.getFile().getRelativePath()) and - exists(DataFlowCall call | + exists(DataFlowSourceCall call | location = call.getLocation() and element = call.toString() | diff --git a/python/ql/test/experimental/dataflow/consistency/modeling-consistency.ql b/python/ql/test/experimental/dataflow/consistency/modeling-consistency.ql index a0dde59bedb..8307c9e26e2 100644 --- a/python/ql/test/experimental/dataflow/consistency/modeling-consistency.ql +++ b/python/ql/test/experimental/dataflow/consistency/modeling-consistency.ql @@ -2,6 +2,6 @@ import python import semmle.python.dataflow.new.DataFlow query predicate parameterWithoutNode(Parameter p, string msg) { - not exists(DataFlow::ParameterNode node | p = node.getParameter()) and + not exists(DataFlow::SourceParameterNode node | p = node.getParameter()) and msg = "There is no `ParameterNode` associated with this parameter." } diff --git a/python/ql/test/experimental/dataflow/coverage/argumentRoutingTest.ql b/python/ql/test/experimental/dataflow/coverage/argumentRoutingTest.ql index 1a58715fc1c..e1b44399d94 100644 --- a/python/ql/test/experimental/dataflow/coverage/argumentRoutingTest.ql +++ b/python/ql/test/experimental/dataflow/coverage/argumentRoutingTest.ql @@ -22,7 +22,7 @@ class Argument1RoutingConfig extends DataFlow::Configuration { override predicate isSource(DataFlow::Node node) { node.(DataFlow::CfgNode).getNode().(NameNode).getId() = "arg1" or - exists(AssignmentDefinition def, DataFlowPrivate::DataFlowCall call | + exists(AssignmentDefinition def, DataFlowPrivate::DataFlowSourceCall call | def.getVariable() = node.(DataFlow::EssaNode).getVar() and def.getValue() = call.getNode() and call.getNode().(CallNode).getFunction().(NameNode).getId().matches("With\\_%") diff --git a/python/ql/test/experimental/dataflow/coverage/classesCallGraph.ql b/python/ql/test/experimental/dataflow/coverage/classesCallGraph.ql index 1f8edfc1c79..e47ddc9429e 100644 --- a/python/ql/test/experimental/dataflow/coverage/classesCallGraph.ql +++ b/python/ql/test/experimental/dataflow/coverage/classesCallGraph.ql @@ -17,10 +17,10 @@ class CallGraphConfig extends DataFlow::Configuration { override predicate isSink(DataFlow::Node node) { node instanceof DataFlowPrivate::OutNode or - node instanceof DataFlow::ParameterNode and + node instanceof DataFlow::SourceParameterNode and // exclude parameters to the SINK-functions not exists(DataFlowPrivate::DataFlowCallable c | - node.(DataFlow::ParameterNode).isParameterOf(c, _) and + node.(DataFlow::SourceParameterNode).isParameterOf(c, _) and c.getName().matches("SINK_") ) } diff --git a/python/ql/test/experimental/dataflow/typetracking/tracked.ql b/python/ql/test/experimental/dataflow/typetracking/tracked.ql index ffc1d43ca72..e20c4033ec8 100644 --- a/python/ql/test/experimental/dataflow/typetracking/tracked.ql +++ b/python/ql/test/experimental/dataflow/typetracking/tracked.ql @@ -89,7 +89,7 @@ private DataFlow::TypeTrackingNode tracked_self(TypeTracker t) { exists(Function f | f.isMethod() and f.getName() = "track_self" and - result.(DataFlow::ParameterNode).getParameter() = f.getArg(0) + result.(DataFlow::SourceParameterNode).getParameter() = f.getArg(0) ) or exists(TypeTracker t2 | result = tracked_self(t2).track(t2, t)) diff --git a/python/ql/test/library-tests/locations/elif/test.expected b/python/ql/test/library-tests/locations/elif/test.expected index f6682119ad1..0f8c5226db9 100644 --- a/python/ql/test/library-tests/locations/elif/test.expected +++ b/python/ql/test/library-tests/locations/elif/test.expected @@ -4,6 +4,24 @@ | If | 10 | 1 | 10 | 5 | | If | 12 | 1 | 12 | 7 | | If | 13 | 5 | 13 | 9 | +| InterpretNode | 0 | 0 | 0 | 0 | +| InterpretNode | 3 | 1 | 3 | 5 | +| InterpretNode | 3 | 4 | 3 | 4 | +| InterpretNode | 4 | 5 | 4 | 8 | +| InterpretNode | 5 | 1 | 5 | 7 | +| InterpretNode | 5 | 6 | 5 | 6 | +| InterpretNode | 6 | 5 | 6 | 8 | +| InterpretNode | 7 | 1 | 7 | 7 | +| InterpretNode | 7 | 6 | 7 | 6 | +| InterpretNode | 8 | 5 | 8 | 8 | +| InterpretNode | 10 | 1 | 10 | 5 | +| InterpretNode | 10 | 4 | 10 | 4 | +| InterpretNode | 11 | 5 | 11 | 8 | +| InterpretNode | 12 | 1 | 12 | 7 | +| InterpretNode | 12 | 6 | 12 | 6 | +| InterpretNode | 13 | 5 | 13 | 9 | +| InterpretNode | 13 | 8 | 13 | 8 | +| InterpretNode | 14 | 9 | 14 | 12 | | ModuleMetrics | 0 | 0 | 0 | 0 | | Name | 3 | 4 | 3 | 4 | | Name | 5 | 6 | 5 | 6 |