Python: Add small api enhancements

determined useful during documentation work.
This commit is contained in:
Rasmus Lerchedahl Petersen
2021-03-12 18:04:13 +01:00
parent 1ecee2da0d
commit 4d856d4461
3 changed files with 146 additions and 98 deletions

View File

@@ -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
/**
@@ -138,6 +139,11 @@ class Node extends TNode {
* Gets a local source node from which data may flow to this node in zero or more local steps.
*/
LocalSourceNode getALocalSource() { result.flowsTo(this) }
/**
* Gets a local source node from which data may flow to this node in zero or more local steps.
*/
LocalSourceNode getALocalTaintSource() { result.taintFlowsTo(this) }
}
/** A data-flow node corresponding to an SSA variable. */
@@ -215,7 +221,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 +243,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,103 +476,6 @@ class BarrierGuard extends GuardNode {
}
}
/**
* A data flow node that is a source of local flow. This includes things like
* - Expressions
* - Function parameters
*/
class LocalSourceNode extends Node {
LocalSourceNode() {
not simpleLocalFlowStep+(any(CfgNode n), 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, Node source) {
// Declaring `source` to be a `SourceNode` currently causes a redundant check in the
// recursive case, so instead we check it explicitly here.
source = sink and
source instanceof LocalSourceNode
or
exists(Node mid |
hasLocalSource(mid, source) and
simpleLocalFlowStep(mid, 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.

View File

@@ -0,0 +1,131 @@
/**
* 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 import TaintTrackingPublic
/**
* A data flow node that is a source of local flow. This includes things like
* - Expressions
* - Function parameters
*/
class LocalSourceNode extends Node {
LocalSourceNode() {
not simpleLocalFlowStep+(any(CfgNode n), 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) }
/** Holds if this `LocalSourceNode` can flow to `nodeTo` in one or more local taint steps. */
pragma[inline]
predicate taintFlowsTo(Node nodeTo) { Cached::hasLocalTaintSource(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, Node source) {
// Declaring `source` to be a `SourceNode` currently causes a redundant check in the
// recursive case, so instead we check it explicitly here.
source = sink and
source instanceof LocalSourceNode
or
exists(Node mid |
hasLocalSource(mid, source) and
simpleLocalFlowStep(mid, sink)
)
}
/**
* Holds if `source` is a `LocalSourceNode` that can reach `sink` via local taint steps.
*
* The slightly backwards parametering ordering is to force correct indexing.
*/
cached
predicate hasLocalTaintSource(Node sink, Node source) {
// Declaring `source` to be a `SourceNode` currently causes a redundant check in the
// recursive case, so instead we check it explicitly here.
source = sink and
source instanceof LocalSourceNode
or
exists(Node mid |
hasLocalTaintSource(mid, source) and
localTaintStep(mid, 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()
)
}
}