Skip to content

Commit bf401c7

Browse files
authored
Merge pull request github#5103 from tausbn/python-port-flask-to-api-graphs
Python: Port Flask models to use API graphs
2 parents 6eafa9d + 2ca12aa commit bf401c7

File tree

5 files changed

+157
-358
lines changed

5 files changed

+157
-358
lines changed

python/ql/src/semmle/python/ApiGraphs.qll

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,11 @@ module API {
9292
*/
9393
Node getReturn() { result = getASuccessor(Label::return()) }
9494

95+
/**
96+
* Gets a node representing a subclass of the class represented by this node.
97+
*/
98+
Node getASubclass() { result = getASuccessor(Label::subclass()) }
99+
95100
/**
96101
* Gets a string representation of the lexicographically least among all shortest access paths
97102
* from the root to this node.
@@ -312,12 +317,11 @@ module API {
312317
* For instance, `prefix_member("foo.bar", "baz", "foo.bar.baz")` would hold.
313318
*/
314319
private predicate prefix_member(TApiNode base, string member, TApiNode sub) {
315-
exists(string base_str, string sub_str |
316-
base = MkModuleImport(base_str) and
320+
exists(string sub_str, string regexp |
321+
regexp = "(.+)[.]([^.]+)" and
322+
base = MkModuleImport(sub_str.regexpCapture(regexp, 1)) and
323+
member = sub_str.regexpCapture(regexp, 2) and
317324
sub = MkModuleImport(sub_str)
318-
|
319-
base_str + "." + member = sub_str and
320-
not member.matches("%.%")
321325
)
322326
}
323327

@@ -351,13 +355,19 @@ module API {
351355
// the relationship between `pred` and `ref`.
352356
use(base, src) and pred = trackUseNode(src)
353357
|
354-
// Reading an attribute on a node that is a use of `base`:
358+
// Referring to an attribute on a node that is a use of `base`:
355359
lbl = Label::memberFromRef(ref) and
356-
ref = pred.getAnAttributeRead()
360+
ref = pred.getAnAttributeReference()
357361
or
358362
// Calling a node that is a use of `base`
359363
lbl = Label::return() and
360364
ref = pred.getACall()
365+
or
366+
// Subclassing a node
367+
lbl = Label::subclass() and
368+
exists(DataFlow::Node superclass | pred.flowsTo(superclass) |
369+
ref.asExpr().(ClassExpr).getABase() = superclass.asExpr()
370+
)
361371
)
362372
}
363373

@@ -468,4 +478,6 @@ private module Label {
468478

469479
/** Gets the `return` edge label. */
470480
string return() { result = "getReturn()" }
481+
482+
string subclass() { result = "getASubclass()" }
471483
}

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -159,7 +159,9 @@ private class SetAttrCallAsAttrWrite extends AttrWrite, CfgNode {
159159
* Instances of this class correspond to the `NameNode` for `attr`, and also gives access to `value` by
160160
* virtue of being a `DefinitionNode`.
161161
*/
162-
private class ClassAttributeAssignmentNode extends DefinitionNode, NameNode { }
162+
private class ClassAttributeAssignmentNode extends DefinitionNode, NameNode {
163+
ClassAttributeAssignmentNode() { this.getScope() = any(ClassExpr c).getInnerScope() }
164+
}
163165

164166
/**
165167
* An attribute assignment via a class field, e.g.

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

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,23 @@ class CfgNode extends Node, TCfgNode {
165165
override Location getLocation() { result = node.getLocation() }
166166
}
167167

168+
/** A data-flow node corresponding to a `CallNode` in the control-flow graph. */
169+
class CallCfgNode extends CfgNode {
170+
override CallNode node;
171+
172+
/**
173+
* Gets the data-flow node for the function component of the call corresponding to this data-flow
174+
* node.
175+
*/
176+
Node getFunction() { result.asCfgNode() = node.getFunction() }
177+
178+
/** Gets the data-flow node corresponding to the i'th argument of the call corresponding to this data-flow node */
179+
Node getArg(int i) { result.asCfgNode() = node.getArg(i) }
180+
181+
/** Gets the data-flow node corresponding to the named argument of the call corresponding to this data-flow node */
182+
Node getArgByName(string name) { result.asCfgNode() = node.getArgByName(name) }
183+
}
184+
168185
/**
169186
* An expression, viewed as a node in a data flow graph.
170187
*
@@ -481,7 +498,7 @@ class LocalSourceNode extends Node {
481498
/**
482499
* Gets a call to this node.
483500
*/
484-
Node getACall() { Cached::call(this, result) }
501+
CallCfgNode getACall() { Cached::call(this, result) }
485502
}
486503

487504
cached
@@ -526,10 +543,10 @@ private module Cached {
526543
* Holds if `func` flows to the callee of `call`.
527544
*/
528545
cached
529-
predicate call(LocalSourceNode func, Node call) {
546+
predicate call(LocalSourceNode func, CallCfgNode call) {
530547
exists(CfgNode n |
531548
func.flowsTo(n) and
532-
n.asCfgNode() = call.asCfgNode().(CallNode).getFunction()
549+
n = call.getFunction()
533550
)
534551
}
535552
}

0 commit comments

Comments
 (0)