Skip to content

Commit 6ed1016

Browse files
authored
Merge pull request github#5669 from tausbn/python-use-api-graphs-for-invoke
Python: Use API graphs for Invoke
2 parents e1028a2 + 5c79ad2 commit 6ed1016

File tree

1 file changed

+20
-82
lines changed

1 file changed

+20
-82
lines changed

python/ql/src/semmle/python/frameworks/Invoke.qll

Lines changed: 20 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
private import python
77
private import semmle.python.dataflow.new.DataFlow
88
private import semmle.python.Concepts
9+
private import semmle.python.ApiGraphs
910

1011
/**
1112
* Provides models for the `invoke` PyPI package.
@@ -16,102 +17,44 @@ private module Invoke {
1617
// invoke
1718
// ---------------------------------------------------------------------------
1819
/** Gets a reference to the `invoke` module. */
19-
private DataFlow::Node invoke(DataFlow::TypeTracker t) {
20-
t.start() and
21-
result = DataFlow::importNode("invoke")
22-
or
23-
exists(DataFlow::TypeTracker t2 | result = invoke(t2).track(t2, t))
24-
}
25-
26-
/** Gets a reference to the `invoke` module. */
27-
DataFlow::Node invoke() { result = invoke(DataFlow::TypeTracker::end()) }
28-
29-
/**
30-
* Gets a reference to the attribute `attr_name` of the `invoke` module.
31-
* WARNING: Only holds for a few predefined attributes.
32-
*/
33-
private DataFlow::Node invoke_attr(DataFlow::TypeTracker t, string attr_name) {
34-
attr_name in ["run", "sudo", "context", "Context", "task"] and
35-
(
36-
t.start() and
37-
result = DataFlow::importNode("invoke." + attr_name)
38-
or
39-
t.startInAttr(attr_name) and
40-
result = DataFlow::importNode("invoke")
41-
)
42-
or
43-
// Due to bad performance when using normal setup with `invoke_attr(t2, attr_name).track(t2, t)`
44-
// we have inlined that code and forced a join
45-
exists(DataFlow::TypeTracker t2 |
46-
exists(DataFlow::StepSummary summary |
47-
invoke_attr_first_join(t2, attr_name, result, summary) and
48-
t = t2.append(summary)
49-
)
50-
)
51-
}
52-
53-
pragma[nomagic]
54-
private predicate invoke_attr_first_join(
55-
DataFlow::TypeTracker t2, string attr_name, DataFlow::Node res, DataFlow::StepSummary summary
56-
) {
57-
DataFlow::StepSummary::step(invoke_attr(t2, attr_name), res, summary)
58-
}
59-
60-
/**
61-
* Gets a reference to the attribute `attr_name` of the `invoke` module.
62-
* WARNING: Only holds for a few predefined attributes.
63-
*/
64-
private DataFlow::Node invoke_attr(string attr_name) {
65-
result = invoke_attr(DataFlow::TypeTracker::end(), attr_name)
66-
}
20+
API::Node invoke() { result = API::moduleImport("invoke") }
6721

6822
/** Provides models for the `invoke` module. */
6923
module invoke {
7024
/** Gets a reference to the `invoke.context` module. */
71-
DataFlow::Node context() { result = invoke_attr("context") }
25+
API::Node context() { result = invoke().getMember("context") }
7226

7327
/** Provides models for the `invoke.context` module */
7428
module context {
7529
/** Provides models for the `invoke.context.Context` class */
7630
module Context {
7731
/** Gets a reference to the `invoke.context.Context` class. */
78-
private DataFlow::Node classRef(DataFlow::TypeTracker t) {
79-
t.start() and
80-
result = DataFlow::importNode("invoke.context.Context")
81-
or
82-
t.startInAttr("Context") and
83-
result = invoke::context()
84-
or
85-
// handle invoke.Context alias
86-
t.start() and
87-
result = invoke_attr("Context")
32+
API::Node classRef() {
33+
result = API::moduleImport("invoke").getMember("context").getMember("Context")
8834
or
89-
exists(DataFlow::TypeTracker t2 | result = classRef(t2).track(t2, t))
35+
result = API::moduleImport("invoke").getMember("Context")
9036
}
9137

92-
/** Gets a reference to the `invoke.context.Context` class. */
93-
DataFlow::Node classRef() { result = classRef(DataFlow::TypeTracker::end()) }
94-
9538
/** Gets a reference to an instance of `invoke.context.Context`. */
96-
private DataFlow::Node instance(DataFlow::TypeTracker t) {
39+
private DataFlow::LocalSourceNode instance(DataFlow::TypeTracker t) {
9740
t.start() and
98-
result.asCfgNode().(CallNode).getFunction() =
99-
invoke::context::Context::classRef().asCfgNode()
100-
or
101-
t.start() and
102-
exists(Function func |
103-
func.getADecorator() = invoke_attr("task").asExpr() and
104-
result.(DataFlow::ParameterNode).getParameter() = func.getArg(0)
41+
(
42+
result = invoke::context::Context::classRef().getACall()
43+
or
44+
exists(Function func |
45+
func.getADecorator() = invoke().getMember("task").getAUse().asExpr() and
46+
result.(DataFlow::ParameterNode).getParameter() = func.getArg(0)
47+
)
10548
)
10649
or
10750
exists(DataFlow::TypeTracker t2 | result = instance(t2).track(t2, t))
10851
}
10952

11053
/** Gets a reference to an instance of `invoke.context.Context`. */
111-
DataFlow::Node instance() { result = instance(DataFlow::TypeTracker::end()) }
54+
DataFlow::Node instance() { instance(DataFlow::TypeTracker::end()).flowsTo(result) }
11255

11356
/** Gets a reference to the `run` or `sudo` methods on a `invoke.context.Context` instance. */
114-
private DataFlow::Node instanceRunMethods(DataFlow::TypeTracker t) {
57+
private DataFlow::LocalSourceNode instanceRunMethods(DataFlow::TypeTracker t) {
11558
t.startInAttr(["run", "sudo"]) and
11659
result = invoke::context::Context::instance()
11760
or
@@ -120,7 +63,7 @@ private module Invoke {
12063

12164
/** Gets a reference to the `run` or `sudo` methods on a `invoke.context.Context` instance. */
12265
DataFlow::Node instanceRunMethods() {
123-
result = instanceRunMethods(DataFlow::TypeTracker::end())
66+
instanceRunMethods(DataFlow::TypeTracker::end()).flowsTo(result)
12467
}
12568
}
12669
}
@@ -131,15 +74,10 @@ private module Invoke {
13174
* - `invoke.run` or `invoke.sudo` functions (http://docs.pyinvoke.org/en/stable/api/__init__.html)
13275
* - `run` or `sudo` methods on a `invoke.context.Context` instance (http://docs.pyinvoke.org/en/stable/api/context.html#invoke.context.Context.run)
13376
*/
134-
private class InvokeRunCommandCall extends SystemCommandExecution::Range, DataFlow::CfgNode {
135-
override CallNode node;
136-
77+
private class InvokeRunCommandCall extends SystemCommandExecution::Range, DataFlow::CallCfgNode {
13778
InvokeRunCommandCall() {
138-
exists(DataFlow::Node callFunction | node.getFunction() = callFunction.asCfgNode() |
139-
callFunction = invoke_attr(["run", "sudo"])
140-
or
141-
callFunction = invoke::context::Context::instanceRunMethods()
142-
)
79+
this = invoke().getMember(["run", "sudo"]).getACall() or
80+
this.getFunction() = invoke::context::Context::instanceRunMethods()
14381
}
14482

14583
override DataFlow::Node getCommand() {

0 commit comments

Comments
 (0)