Skip to content

Commit 7cd51d6

Browse files
authored
Merge pull request #9126 from RasmusWL/moduleimport-with-dots
Python: Fully disallow `API::moduleImport` of module with dots
2 parents dd900e6 + 795adf0 commit 7cd51d6

File tree

9 files changed

+49
-8
lines changed

9 files changed

+49
-8
lines changed
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
---
2+
category: breaking
3+
---
4+
`API::moduleImport` no longer has any results for dotted names, such as `API::moduleImport("foo.bar")`. Using `API::moduleImport("foo.bar").getMember("baz").getACall()` previously worked if the Python code was `from foo.bar import baz; baz()`, but not if the code was `import foo.bar; foo.bar.baz()` -- we are making this change to ensure the approach that can handle all cases is always used.

python/ql/lib/semmle/python/ApiGraphs.qll

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -280,7 +280,13 @@ module API {
280280
* you should use `.getMember` on the parent module. For example, for nodes corresponding to the module `foo.bar`,
281281
* use `moduleImport("foo").getMember("bar")`.
282282
*/
283-
Node moduleImport(string m) { result = Impl::MkModuleImport(m) }
283+
Node moduleImport(string m) {
284+
result = Impl::MkModuleImport(m) and
285+
// restrict `moduleImport` so it will never give results for a dotted name. Note
286+
// that we cannot move this logic to the `MkModuleImport` construction, since we
287+
// need the intermediate API graph nodes for the prefixes in `import foo.bar.baz`.
288+
not m.matches("%.%")
289+
}
284290

285291
/** Gets a node corresponding to the built-in with the given name, if any. */
286292
Node builtin(string n) { result = moduleImport("builtins").getMember(n) }

python/ql/src/experimental/Security/CWE-285/PamAuthorization.ql

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ import semmle.python.dataflow.new.TaintTracking
1616

1717
API::Node libPam() {
1818
exists(API::CallNode findLibCall, API::CallNode cdllCall |
19-
findLibCall = API::moduleImport("ctypes.util").getMember("find_library").getACall() and
19+
findLibCall = API::moduleImport("ctypes").getMember("util").getMember("find_library").getACall() and
2020
findLibCall.getParameter(0).getAValueReachingRhs().asExpr().(StrConst).getText() = "pam" and
2121
cdllCall = API::moduleImport("ctypes").getMember("CDLL").getACall() and
2222
cdllCall.getParameter(0).getAValueReachingRhs() = findLibCall

python/ql/src/experimental/semmle/python/frameworks/NoSQL.qll

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -210,10 +210,13 @@ private module NoSql {
210210
*/
211211
private class BsonObjectIdCall extends DataFlow::CallCfgNode, NoSqlSanitizer::Range {
212212
BsonObjectIdCall() {
213-
this =
214-
API::moduleImport(["bson", "bson.objectid", "bson.json_util"])
215-
.getMember("ObjectId")
216-
.getACall()
213+
exists(API::Node mod |
214+
mod = API::moduleImport("bson")
215+
or
216+
mod = API::moduleImport("bson").getMember(["objectid", "json_util"])
217+
|
218+
this = mod.getMember("ObjectId").getACall()
219+
)
217220
}
218221

219222
override DataFlow::Node getAnInput() { result = this.getArg(0) }

python/ql/test/experimental/dataflow/typetracking/tracked.ql

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,7 @@ DataFlow::Node foo() { foo(DataFlow::TypeTracker::end()).flowsTo(result) }
131131
/** Gets a reference to `foo.bar` (fictive module). */
132132
private DataFlow::TypeTrackingNode foo_bar(DataFlow::TypeTracker t) {
133133
t.start() and
134-
result = API::moduleImport("foo.bar").getAnImmediateUse()
134+
result = API::moduleImport("foo").getMember("bar").getAnImmediateUse()
135135
or
136136
t.startInAttr("bar") and
137137
result = foo()
@@ -145,7 +145,7 @@ DataFlow::Node foo_bar() { foo_bar(DataFlow::TypeTracker::end()).flowsTo(result)
145145
/** Gets a reference to `foo.bar.baz` (fictive attribute on `foo.bar` module). */
146146
private DataFlow::TypeTrackingNode foo_bar_baz(DataFlow::TypeTracker t) {
147147
t.start() and
148-
result = API::moduleImport("foo.bar.baz").getAnImmediateUse()
148+
result = API::moduleImport("foo").getMember("bar").getMember("baz").getAnImmediateUse()
149149
or
150150
t.startInAttr("baz") and
151151
result = foo_bar()
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
moduleImportWithDots
2+
doesntFullyWork
3+
works
4+
| test.py:25:6:25:18 | ControlFlowNode for Attribute() |
5+
| test.py:28:10:28:17 | ControlFlowNode for method() |
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
private import python
2+
private import semmle.python.ApiGraphs
3+
4+
query API::Node moduleImportWithDots() { result = API::moduleImport("a.b.c.d") }
5+
6+
query API::CallNode doesntFullyWork() {
7+
result = API::moduleImport("a.b.c.d").getMember("method").getACall()
8+
}
9+
10+
query API::CallNode works() {
11+
result =
12+
API::moduleImport("a")
13+
.getMember("b")
14+
.getMember("c")
15+
.getMember("d")
16+
.getMember("method")
17+
.getACall()
18+
}

python/ql/test/library-tests/ApiGraphs/py3/test.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,9 @@
2424

2525
x5 = abcd.method() #$ use=moduleImport("a").getMember("b").getMember("c").getMember("d").getMember("method").getReturn()
2626

27+
from a.b.c.d import method
28+
x5_alt = method() #$ use=moduleImport("a").getMember("b").getMember("c").getMember("d").getMember("method").getReturn()
29+
2730
from a6 import m6 #$ use=moduleImport("a6").getMember("m6")
2831

2932
x6 = m6().foo().bar() #$ use=moduleImport("a6").getMember("m6").getReturn().getMember("foo").getReturn().getMember("bar").getReturn()
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,3 @@
1+
// Note: This is not using standard inline-expectation tests, so will not alert if you
2+
// have not manually added an annotation to a line!
13
import TestUtilities.VerifyApiGraphs

0 commit comments

Comments
 (0)