diff --git a/python/change-notes/2021-03-12-small-api-enhancements.md b/python/change-notes/2021-03-12-small-api-enhancements.md new file mode 100644 index 000000000000..8d3c5c67b5f7 --- /dev/null +++ b/python/change-notes/2021-03-12-small-api-enhancements.md @@ -0,0 +1,3 @@ +lgtm,codescanning +* The class ParameterNode now extends LocalSourceNode, thus making methods like flowsTo available. +* The new predicate `parameterNode` can now be used to map from a `Parameter` to a data-flow node. diff --git a/python/ql/src/semmle/python/dataflow/new/internal/DataFlowPublic.qll b/python/ql/src/semmle/python/dataflow/new/internal/DataFlowPublic.qll index 360e5e170dee..36db37a0a0f9 100644 --- a/python/ql/src/semmle/python/dataflow/new/internal/DataFlowPublic.qll +++ b/python/ql/src/semmle/python/dataflow/new/internal/DataFlowPublic.qll @@ -6,6 +6,7 @@ private import python private import DataFlowPrivate import semmle.python.dataflow.new.TypeTracker import Attributes +import LocalSources private import semmle.python.essa.SsaCompute /** @@ -135,7 +136,7 @@ class Node extends TNode { LocalSourceNode backtrack(TypeBackTracker t2, TypeBackTracker t) { t2 = t.step(result, this) } /** - * Gets a local source node from which data may flow to this node in zero or more local steps. + * Gets a local source node from which data may flow to this node in zero or more local data-flow steps. */ LocalSourceNode getALocalSource() { result.flowsTo(this) } } @@ -215,7 +216,7 @@ 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 { +class ParameterNode extends CfgNode, LocalSourceNode { ParameterDefinition def; ParameterNode() { @@ -237,6 +238,9 @@ class ParameterNode extends CfgNode { Parameter getParameter() { result = def.getParameter() } } +/** Gets a node corresponding to parameter `p`. */ +ParameterNode 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(_) } @@ -467,108 +471,6 @@ class BarrierGuard extends GuardNode { } } -private predicate comes_from_cfgnode(Node node) { - exists(CfgNode first, Node second | - simpleLocalFlowStep(first, second) and - simpleLocalFlowStep*(second, node) - ) -} - -/** - * A data flow node that is a source of local flow. This includes things like - * - Expressions - * - Function parameters - */ -class LocalSourceNode extends Node { - cached - LocalSourceNode() { - not comes_from_cfgnode(this) and - not this instanceof ModuleVariableNode - or - this = any(ModuleVariableNode mvn).getARead() - } - - /** Holds if this `LocalSourceNode` can flow to `nodeTo` in one or more local flow steps. */ - pragma[inline] - predicate flowsTo(Node nodeTo) { Cached::hasLocalSource(nodeTo, this) } - - /** - * Gets a reference (read or write) of attribute `attrName` on this node. - */ - AttrRef getAnAttributeReference(string attrName) { Cached::namedAttrRef(this, attrName, result) } - - /** - * Gets a read of attribute `attrName` on this node. - */ - AttrRead getAnAttributeRead(string attrName) { result = getAnAttributeReference(attrName) } - - /** - * Gets a reference (read or write) of any attribute on this node. - */ - AttrRef getAnAttributeReference() { - Cached::namedAttrRef(this, _, result) - or - Cached::dynamicAttrRef(this, result) - } - - /** - * Gets a read of any attribute on this node. - */ - AttrRead getAnAttributeRead() { result = getAnAttributeReference() } - - /** - * Gets a call to this node. - */ - CallCfgNode getACall() { Cached::call(this, result) } -} - -cached -private module Cached { - /** - * Holds if `source` is a `LocalSourceNode` that can reach `sink` via local flow steps. - * - * The slightly backwards parametering ordering is to force correct indexing. - */ - cached - predicate hasLocalSource(Node sink, LocalSourceNode source) { - source = sink - or - exists(Node second | - simpleLocalFlowStep(source, second) and - simpleLocalFlowStep*(second, sink) - ) - } - - /** - * Holds if `base` flows to the base of `ref` and `ref` has attribute name `attr`. - */ - cached - predicate namedAttrRef(LocalSourceNode base, string attr, AttrRef ref) { - base.flowsTo(ref.getObject()) and - ref.getAttributeName() = attr - } - - /** - * Holds if `base` flows to the base of `ref` and `ref` has no known attribute name. - */ - cached - predicate dynamicAttrRef(LocalSourceNode base, AttrRef ref) { - base.flowsTo(ref.getObject()) and - not exists(ref.getAttributeName()) - } - - /** - * Holds if `func` flows to the callee of `call`. - */ - cached - predicate call(LocalSourceNode func, CallCfgNode call) { - exists(CfgNode n | - func.flowsTo(n) and - n = call.getFunction() - ) - } -} - /** * Algebraic datatype for tracking data content associated with values. * Content can be collection elements or object attributes. diff --git a/python/ql/src/semmle/python/dataflow/new/internal/LocalSources.qll b/python/ql/src/semmle/python/dataflow/new/internal/LocalSources.qll new file mode 100644 index 000000000000..26e04aaaefb4 --- /dev/null +++ b/python/ql/src/semmle/python/dataflow/new/internal/LocalSources.qll @@ -0,0 +1,113 @@ +/** + * Provides support for intra-procedural tracking of a customizable + * set of data flow nodes. + * + * Note that unlike `TypeTracker.qll`, this library only performs + * local tracking within a function. + */ + +import python +import DataFlowPublic +private import DataFlowPrivate + +private predicate comes_from_cfgnode(Node node) { + exists(CfgNode first, Node second | + simpleLocalFlowStep(first, second) and + simpleLocalFlowStep*(second, node) + ) +} + +/** + * A data flow node that is a source of local flow. This includes things like + * - Expressions + * - Function parameters + */ +class LocalSourceNode extends Node { + cached + LocalSourceNode() { + not comes_from_cfgnode(this) and + not this instanceof ModuleVariableNode + or + this = any(ModuleVariableNode mvn).getARead() + } + + /** Holds if this `LocalSourceNode` can flow to `nodeTo` in one or more local flow steps. */ + pragma[inline] + predicate flowsTo(Node nodeTo) { Cached::hasLocalSource(nodeTo, this) } + + /** + * Gets a reference (read or write) of attribute `attrName` on this node. + */ + AttrRef getAnAttributeReference(string attrName) { Cached::namedAttrRef(this, attrName, result) } + + /** + * Gets a read of attribute `attrName` on this node. + */ + AttrRead getAnAttributeRead(string attrName) { result = getAnAttributeReference(attrName) } + + /** + * Gets a reference (read or write) of any attribute on this node. + */ + AttrRef getAnAttributeReference() { + Cached::namedAttrRef(this, _, result) + or + Cached::dynamicAttrRef(this, result) + } + + /** + * Gets a read of any attribute on this node. + */ + AttrRead getAnAttributeRead() { result = getAnAttributeReference() } + + /** + * Gets a call to this node. + */ + CallCfgNode getACall() { Cached::call(this, result) } +} + +cached +private module Cached { + /** + * Holds if `source` is a `LocalSourceNode` that can reach `sink` via local flow steps. + * + * The slightly backwards parametering ordering is to force correct indexing. + */ + cached + predicate hasLocalSource(Node sink, LocalSourceNode source) { + source = sink + or + exists(Node second | + simpleLocalFlowStep(source, second) and + simpleLocalFlowStep*(second, sink) + ) + } + + /** + * Holds if `base` flows to the base of `ref` and `ref` has attribute name `attr`. + */ + cached + predicate namedAttrRef(LocalSourceNode base, string attr, AttrRef ref) { + base.flowsTo(ref.getObject()) and + ref.getAttributeName() = attr + } + + /** + * Holds if `base` flows to the base of `ref` and `ref` has no known attribute name. + */ + cached + predicate dynamicAttrRef(LocalSourceNode base, AttrRef ref) { + base.flowsTo(ref.getObject()) and + not exists(ref.getAttributeName()) + } + + /** + * Holds if `func` flows to the callee of `call`. + */ + cached + predicate call(LocalSourceNode func, CallCfgNode call) { + exists(CfgNode n | + func.flowsTo(n) and + n = call.getFunction() + ) + } +}