Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit f750238

Browse files
committed
Python: Model fabric package (version 1.x)
1 parent 6b30198 commit f750238

3 files changed

Lines changed: 147 additions & 6 deletions

File tree

python/ql/src/experimental/semmle/python/Frameworks.qll

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
private import experimental.semmle.python.frameworks.Dill
66
private import experimental.semmle.python.frameworks.Django
7+
private import experimental.semmle.python.frameworks.Fabric
78
private import experimental.semmle.python.frameworks.Flask
89
private import experimental.semmle.python.frameworks.Invoke
910
private import experimental.semmle.python.frameworks.Stdlib
Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
/**
2+
* Provides classes modeling security-relevant aspects of the `fabric` PyPI package, for
3+
* both version 1.x and 2.x.
4+
*
5+
* See
6+
* - http://docs.fabfile.org/en/1.14/tutorial.html and
7+
* - http://docs.fabfile.org/en/2.5/getting-started.html
8+
*/
9+
10+
private import python
11+
private import experimental.dataflow.DataFlow
12+
private import experimental.dataflow.RemoteFlowSources
13+
private import experimental.semmle.python.Concepts
14+
15+
/**
16+
* Provides classes modeling security-relevant aspects of the `fabric` PyPI package, for
17+
* version 1.x.
18+
*
19+
* See http://docs.fabfile.org/en/1.14/tutorial.html.
20+
*/
21+
private module FabricV1 {
22+
/** Gets a reference to the `fabric` module. */
23+
private DataFlow::Node fabric(DataFlow::TypeTracker t) {
24+
t.start() and
25+
result = DataFlow::importNode("fabric")
26+
or
27+
exists(DataFlow::TypeTracker t2 | result = fabric(t2).track(t2, t))
28+
}
29+
30+
/** Gets a reference to the `fabric` module. */
31+
DataFlow::Node fabric() { result = fabric(DataFlow::TypeTracker::end()) }
32+
33+
/**
34+
* Gets a reference to the attribute `attr_name` of the `fabric` module.
35+
* WARNING: Only holds for a few predefined attributes.
36+
*/
37+
private DataFlow::Node fabric_attr(DataFlow::TypeTracker t, string attr_name) {
38+
attr_name in ["api"] and
39+
(
40+
t.start() and
41+
result = DataFlow::importNode("fabric" + "." + attr_name)
42+
or
43+
t.startInAttr(attr_name) and
44+
result = fabric()
45+
)
46+
or
47+
// Due to bad performance when using normal setup with `fabric_attr(t2, attr_name).track(t2, t)`
48+
// we have inlined that code and forced a join
49+
exists(DataFlow::TypeTracker t2 |
50+
exists(DataFlow::StepSummary summary |
51+
fabric_attr_first_join(t2, attr_name, result, summary) and
52+
t = t2.append(summary)
53+
)
54+
)
55+
}
56+
57+
pragma[nomagic]
58+
private predicate fabric_attr_first_join(
59+
DataFlow::TypeTracker t2, string attr_name, DataFlow::Node res, DataFlow::StepSummary summary
60+
) {
61+
DataFlow::StepSummary::step(fabric_attr(t2, attr_name), res, summary)
62+
}
63+
64+
/**
65+
* Gets a reference to the attribute `attr_name` of the `fabric` module.
66+
* WARNING: Only holds for a few predefined attributes.
67+
*/
68+
private DataFlow::Node fabric_attr(string attr_name) {
69+
result = fabric_attr(DataFlow::TypeTracker::end(), attr_name)
70+
}
71+
72+
/** Provides models for the `fabric` module. */
73+
module fabric {
74+
// -------------------------------------------------------------------------
75+
// fabric.api
76+
// -------------------------------------------------------------------------
77+
/** Gets a reference to the `fabric.api` module. */
78+
DataFlow::Node api() { result = fabric_attr("api") }
79+
80+
/** Provides models for the `fabric.api` module */
81+
module api {
82+
/**
83+
* Gets a reference to the attribute `attr_name` of the `fabric.api` module.
84+
* WARNING: Only holds for a few predefined attributes.
85+
*/
86+
private DataFlow::Node api_attr(DataFlow::TypeTracker t, string attr_name) {
87+
attr_name in ["run", "local", "sudo"] and
88+
(
89+
t.start() and
90+
result = DataFlow::importNode("fabric.api" + "." + attr_name)
91+
or
92+
t.startInAttr(attr_name) and
93+
result = api()
94+
)
95+
or
96+
// Due to bad performance when using normal setup with `api_attr(t2, attr_name).track(t2, t)`
97+
// we have inlined that code and forced a join
98+
exists(DataFlow::TypeTracker t2 |
99+
exists(DataFlow::StepSummary summary |
100+
api_attr_first_join(t2, attr_name, result, summary) and
101+
t = t2.append(summary)
102+
)
103+
)
104+
}
105+
106+
pragma[nomagic]
107+
private predicate api_attr_first_join(
108+
DataFlow::TypeTracker t2, string attr_name, DataFlow::Node res,
109+
DataFlow::StepSummary summary
110+
) {
111+
DataFlow::StepSummary::step(api_attr(t2, attr_name), res, summary)
112+
}
113+
114+
/**
115+
* Gets a reference to the attribute `attr_name` of the `fabric.api` module.
116+
* WARNING: Only holds for a few predefined attributes.
117+
*/
118+
private DataFlow::Node api_attr(string attr_name) {
119+
result = api_attr(DataFlow::TypeTracker::end(), attr_name)
120+
}
121+
122+
/**
123+
* A call to either
124+
* - `fabric.api.local`
125+
* - `fabric.api.run`
126+
* - `fabric.api.sudo`
127+
* See
128+
* - https://docs.fabfile.org/en/1.14/api/core/operations.html#fabric.operations.local
129+
* - https://docs.fabfile.org/en/1.14/api/core/operations.html#fabric.operations.run
130+
* - https://docs.fabfile.org/en/1.14/api/core/operations.html#fabric.operations.sudo
131+
*/
132+
private class FabricApiLocalRunSudoCall extends SystemCommandExecution::Range,
133+
DataFlow::CfgNode {
134+
override CallNode node;
135+
136+
FabricApiLocalRunSudoCall() {
137+
node.getFunction() = api_attr(["local", "run", "sudo"]).asCfgNode()
138+
}
139+
140+
override DataFlow::Node getCommand() {
141+
result.asCfgNode() = [node.getArg(0), node.getArgByName("command")]
142+
}
143+
}
144+
}
145+
}
146+
}

python/ql/test/experimental/library-tests/frameworks/fabric/ConceptsTest.expected

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,3 @@
1-
| fabric_v1_test.py:8:22:8:47 | Comment # $getCommand="cmd1; cmd2" | Missing result:getCommand="cmd1; cmd2" |
2-
| fabric_v1_test.py:9:20:9:45 | Comment # $getCommand="cmd1; cmd2" | Missing result:getCommand="cmd1; cmd2" |
3-
| fabric_v1_test.py:10:21:10:46 | Comment # $getCommand="cmd1; cmd2" | Missing result:getCommand="cmd1; cmd2" |
4-
| fabric_v1_test.py:12:30:12:55 | Comment # $getCommand="cmd1; cmd2" | Missing result:getCommand="cmd1; cmd2" |
5-
| fabric_v1_test.py:13:28:13:53 | Comment # $getCommand="cmd1; cmd2" | Missing result:getCommand="cmd1; cmd2" |
6-
| fabric_v1_test.py:14:29:14:54 | Comment # $getCommand="cmd1; cmd2" | Missing result:getCommand="cmd1; cmd2" |
71
| fabric_v2_test.py:13:22:13:47 | Comment # $getCommand="cmd1; cmd2" | Missing result:getCommand="cmd1; cmd2" |
82
| fabric_v2_test.py:14:24:14:49 | Comment # $getCommand="cmd1; cmd2" | Missing result:getCommand="cmd1; cmd2" |
93
| fabric_v2_test.py:15:23:15:48 | Comment # $getCommand="cmd1; cmd2" | Missing result:getCommand="cmd1; cmd2" |

0 commit comments

Comments
 (0)