Skip to content

Commit 68d00a8

Browse files
authored
Merge pull request #14430 from RasmusWL/api-graph-import-star
Python: Better allow `import *` to work with API graphs
2 parents e99b159 + ee75b10 commit 68d00a8

File tree

3 files changed

+26
-3
lines changed

3 files changed

+26
-3
lines changed
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
---
2+
category: minorAnalysis
3+
---
4+
* Added better support for API graphs when encountering `from ... import *`. For example in the code `from foo import *; Bar()`, we will now find a result for `API::moduleImport("foo").getMember("Bar").getACall()`

python/ql/lib/semmle/python/dataflow/new/internal/LocalSources.qll

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import DataFlowPublic
1111
private import DataFlowPrivate
1212
private import semmle.python.internal.CachedStages
1313
private import semmle.python.internal.Awaited
14+
private import semmle.python.dataflow.new.internal.ImportStar
1415

1516
/**
1617
* A data flow node that is a source of local flow. This includes things like
@@ -39,6 +40,22 @@ class LocalSourceNode extends Node {
3940
this instanceof ExprNode and
4041
not simpleLocalFlowStepForTypetracking(_, this)
4142
or
43+
// For `from foo import *; foo_function()`, we want to let the variables we think
44+
// could originate in `foo` (such as `foo_function`) to be available in the API
45+
// graph. This requires them to be local sources. They would not be from the code
46+
// just above, since the CFG node has flow going into it from its corresponding
47+
// `GlobalSsaVariable`. (a different work-around is to change API graphs to not rely
48+
// as heavily on LocalSourceNode; I initially tried this, but it relied on a lot of
49+
// copy-pasted code, and it requires some non-trivial deprecation for downgrading
50+
// the result type of `.asSource()` to DataFlow::Node, so we've opted for this
51+
// approach instead).
52+
//
53+
// Note: This is only needed at the module level -- uses inside functions appear as
54+
// LocalSourceNodes as we expect.
55+
//
56+
// TODO: When rewriting SSA, we should be able to remove this workaround
57+
ImportStar::namePossiblyDefinedInImportStar(this.(ExprNode).getNode(), _, any(Module m))
58+
or
4259
// We include all module variable nodes, as these act as stepping stones between writes and
4360
// reads of global variables. Without them, type tracking based on `LocalSourceNode`s would be
4461
// unable to track across global variables.

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

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,11 @@
22

33
from unknown import * #$ use=moduleImport("unknown")
44

5-
# Currently missing, as we do not consider `hello` to be a `LocalSourceNode`, since it has flow
6-
# going into it from its corresponding `GlobalSsaVariable`.
7-
hello() #$ MISSING: use=moduleImport("unknown").getMember("hello").getReturn()
5+
# This used to be missing, as we did not consider `hello` to be a `LocalSourceNode`,
6+
# since it has flow going into it from its corresponding `GlobalSsaVariable`.
7+
hello() #$ use=moduleImport("unknown").getMember("hello").getReturn()
8+
9+
print(const_from_unknown) #$ use=moduleImport("unknown").getMember("const_from_unknown")
810

911
# We don't want our analysis to think that either `non_module_member` or `outer_bar` can
1012
# come from `from unknown import *`

0 commit comments

Comments
 (0)