Skip to content

Commit 4d856d4

Browse files
committed
Python: Add small api enhancements
determined useful during documentation work.
1 parent 1ecee2d commit 4d856d4

File tree

3 files changed

+146
-98
lines changed

3 files changed

+146
-98
lines changed
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
lgtm,codescanning
2+
* Make `ParameterNode` extend `LocalSourceNode`, thus making members like `flowsTo` available.
3+
* Add member predicate `taintFlowsTo` to `LocalSourceNode` facilitating smoother syntax for local taint tracking.
4+
* Add member `getALocalTaintSource` to `DataFlow::Node` facilitating smoother syntax for local taint tracking.
5+
* Add predicate `parameterNode` to map from a `Parameter` to a data-flow node.

python/ql/src/semmle/python/dataflow/new/internal/DataFlowPublic.qll

Lines changed: 10 additions & 98 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ private import python
66
private import DataFlowPrivate
77
import semmle.python.dataflow.new.TypeTracker
88
import Attributes
9+
import LocalSources
910
private import semmle.python.essa.SsaCompute
1011

1112
/**
@@ -138,6 +139,11 @@ class Node extends TNode {
138139
* Gets a local source node from which data may flow to this node in zero or more local steps.
139140
*/
140141
LocalSourceNode getALocalSource() { result.flowsTo(this) }
142+
143+
/**
144+
* Gets a local source node from which data may flow to this node in zero or more local steps.
145+
*/
146+
LocalSourceNode getALocalTaintSource() { result.taintFlowsTo(this) }
141147
}
142148

143149
/** A data-flow node corresponding to an SSA variable. */
@@ -215,7 +221,7 @@ ExprNode exprNode(DataFlowExpr e) { result.getNode().getNode() = e }
215221
* The value of a parameter at function entry, viewed as a node in a data
216222
* flow graph.
217223
*/
218-
class ParameterNode extends CfgNode {
224+
class ParameterNode extends CfgNode, LocalSourceNode {
219225
ParameterDefinition def;
220226

221227
ParameterNode() {
@@ -237,6 +243,9 @@ class ParameterNode extends CfgNode {
237243
Parameter getParameter() { result = def.getParameter() }
238244
}
239245

246+
/** Gets a node corresponding to parameter `p`. */
247+
ParameterNode parameterNode(Parameter p) { result.getParameter() = p }
248+
240249
/** A data flow node that represents a call argument. */
241250
class ArgumentNode extends Node {
242251
ArgumentNode() { this = any(DataFlowCall c).getArg(_) }
@@ -467,103 +476,6 @@ class BarrierGuard extends GuardNode {
467476
}
468477
}
469478

470-
/**
471-
* A data flow node that is a source of local flow. This includes things like
472-
* - Expressions
473-
* - Function parameters
474-
*/
475-
class LocalSourceNode extends Node {
476-
LocalSourceNode() {
477-
not simpleLocalFlowStep+(any(CfgNode n), this) and
478-
not this instanceof ModuleVariableNode
479-
or
480-
this = any(ModuleVariableNode mvn).getARead()
481-
}
482-
483-
/** Holds if this `LocalSourceNode` can flow to `nodeTo` in one or more local flow steps. */
484-
pragma[inline]
485-
predicate flowsTo(Node nodeTo) { Cached::hasLocalSource(nodeTo, this) }
486-
487-
/**
488-
* Gets a reference (read or write) of attribute `attrName` on this node.
489-
*/
490-
AttrRef getAnAttributeReference(string attrName) { Cached::namedAttrRef(this, attrName, result) }
491-
492-
/**
493-
* Gets a read of attribute `attrName` on this node.
494-
*/
495-
AttrRead getAnAttributeRead(string attrName) { result = getAnAttributeReference(attrName) }
496-
497-
/**
498-
* Gets a reference (read or write) of any attribute on this node.
499-
*/
500-
AttrRef getAnAttributeReference() {
501-
Cached::namedAttrRef(this, _, result)
502-
or
503-
Cached::dynamicAttrRef(this, result)
504-
}
505-
506-
/**
507-
* Gets a read of any attribute on this node.
508-
*/
509-
AttrRead getAnAttributeRead() { result = getAnAttributeReference() }
510-
511-
/**
512-
* Gets a call to this node.
513-
*/
514-
CallCfgNode getACall() { Cached::call(this, result) }
515-
}
516-
517-
cached
518-
private module Cached {
519-
/**
520-
* Holds if `source` is a `LocalSourceNode` that can reach `sink` via local flow steps.
521-
*
522-
* The slightly backwards parametering ordering is to force correct indexing.
523-
*/
524-
cached
525-
predicate hasLocalSource(Node sink, Node source) {
526-
// Declaring `source` to be a `SourceNode` currently causes a redundant check in the
527-
// recursive case, so instead we check it explicitly here.
528-
source = sink and
529-
source instanceof LocalSourceNode
530-
or
531-
exists(Node mid |
532-
hasLocalSource(mid, source) and
533-
simpleLocalFlowStep(mid, sink)
534-
)
535-
}
536-
537-
/**
538-
* Holds if `base` flows to the base of `ref` and `ref` has attribute name `attr`.
539-
*/
540-
cached
541-
predicate namedAttrRef(LocalSourceNode base, string attr, AttrRef ref) {
542-
base.flowsTo(ref.getObject()) and
543-
ref.getAttributeName() = attr
544-
}
545-
546-
/**
547-
* Holds if `base` flows to the base of `ref` and `ref` has no known attribute name.
548-
*/
549-
cached
550-
predicate dynamicAttrRef(LocalSourceNode base, AttrRef ref) {
551-
base.flowsTo(ref.getObject()) and
552-
not exists(ref.getAttributeName())
553-
}
554-
555-
/**
556-
* Holds if `func` flows to the callee of `call`.
557-
*/
558-
cached
559-
predicate call(LocalSourceNode func, CallCfgNode call) {
560-
exists(CfgNode n |
561-
func.flowsTo(n) and
562-
n = call.getFunction()
563-
)
564-
}
565-
}
566-
567479
/**
568480
* Algebraic datatype for tracking data content associated with values.
569481
* Content can be collection elements or object attributes.
Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
/**
2+
* Provides support for intra-procedural tracking of a customizable
3+
* set of data flow nodes.
4+
*
5+
* Note that unlike `TypeTracker.qll`, this library only performs
6+
* local tracking within a function.
7+
*/
8+
9+
import python
10+
import DataFlowPublic
11+
private import DataFlowPrivate
12+
private import TaintTrackingPublic
13+
14+
/**
15+
* A data flow node that is a source of local flow. This includes things like
16+
* - Expressions
17+
* - Function parameters
18+
*/
19+
class LocalSourceNode extends Node {
20+
LocalSourceNode() {
21+
not simpleLocalFlowStep+(any(CfgNode n), this) and
22+
not this instanceof ModuleVariableNode
23+
or
24+
this = any(ModuleVariableNode mvn).getARead()
25+
}
26+
27+
/** Holds if this `LocalSourceNode` can flow to `nodeTo` in one or more local flow steps. */
28+
pragma[inline]
29+
predicate flowsTo(Node nodeTo) { Cached::hasLocalSource(nodeTo, this) }
30+
31+
/** Holds if this `LocalSourceNode` can flow to `nodeTo` in one or more local taint steps. */
32+
pragma[inline]
33+
predicate taintFlowsTo(Node nodeTo) { Cached::hasLocalTaintSource(nodeTo, this) }
34+
35+
/**
36+
* Gets a reference (read or write) of attribute `attrName` on this node.
37+
*/
38+
AttrRef getAnAttributeReference(string attrName) { Cached::namedAttrRef(this, attrName, result) }
39+
40+
/**
41+
* Gets a read of attribute `attrName` on this node.
42+
*/
43+
AttrRead getAnAttributeRead(string attrName) { result = getAnAttributeReference(attrName) }
44+
45+
/**
46+
* Gets a reference (read or write) of any attribute on this node.
47+
*/
48+
AttrRef getAnAttributeReference() {
49+
Cached::namedAttrRef(this, _, result)
50+
or
51+
Cached::dynamicAttrRef(this, result)
52+
}
53+
54+
/**
55+
* Gets a read of any attribute on this node.
56+
*/
57+
AttrRead getAnAttributeRead() { result = getAnAttributeReference() }
58+
59+
/**
60+
* Gets a call to this node.
61+
*/
62+
CallCfgNode getACall() { Cached::call(this, result) }
63+
}
64+
65+
cached
66+
private module Cached {
67+
/**
68+
* Holds if `source` is a `LocalSourceNode` that can reach `sink` via local flow steps.
69+
*
70+
* The slightly backwards parametering ordering is to force correct indexing.
71+
*/
72+
cached
73+
predicate hasLocalSource(Node sink, Node source) {
74+
// Declaring `source` to be a `SourceNode` currently causes a redundant check in the
75+
// recursive case, so instead we check it explicitly here.
76+
source = sink and
77+
source instanceof LocalSourceNode
78+
or
79+
exists(Node mid |
80+
hasLocalSource(mid, source) and
81+
simpleLocalFlowStep(mid, sink)
82+
)
83+
}
84+
85+
/**
86+
* Holds if `source` is a `LocalSourceNode` that can reach `sink` via local taint steps.
87+
*
88+
* The slightly backwards parametering ordering is to force correct indexing.
89+
*/
90+
cached
91+
predicate hasLocalTaintSource(Node sink, Node source) {
92+
// Declaring `source` to be a `SourceNode` currently causes a redundant check in the
93+
// recursive case, so instead we check it explicitly here.
94+
source = sink and
95+
source instanceof LocalSourceNode
96+
or
97+
exists(Node mid |
98+
hasLocalTaintSource(mid, source) and
99+
localTaintStep(mid, sink)
100+
)
101+
}
102+
103+
/**
104+
* Holds if `base` flows to the base of `ref` and `ref` has attribute name `attr`.
105+
*/
106+
cached
107+
predicate namedAttrRef(LocalSourceNode base, string attr, AttrRef ref) {
108+
base.flowsTo(ref.getObject()) and
109+
ref.getAttributeName() = attr
110+
}
111+
112+
/**
113+
* Holds if `base` flows to the base of `ref` and `ref` has no known attribute name.
114+
*/
115+
cached
116+
predicate dynamicAttrRef(LocalSourceNode base, AttrRef ref) {
117+
base.flowsTo(ref.getObject()) and
118+
not exists(ref.getAttributeName())
119+
}
120+
121+
/**
122+
* Holds if `func` flows to the callee of `call`.
123+
*/
124+
cached
125+
predicate call(LocalSourceNode func, CallCfgNode call) {
126+
exists(CfgNode n |
127+
func.flowsTo(n) and
128+
n = call.getFunction()
129+
)
130+
}
131+
}

0 commit comments

Comments
 (0)