Skip to content

Commit

Permalink
Merge pull request #14430 from RasmusWL/api-graph-import-star
Browse files Browse the repository at this point in the history
Python: Better allow `import *` to work with API graphs
  • Loading branch information
RasmusWL authored Oct 11, 2023
2 parents e99b159 + ee75b10 commit 68d00a8
Show file tree
Hide file tree
Showing 3 changed files with 26 additions and 3 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
---
category: minorAnalysis
---
* 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()`
17 changes: 17 additions & 0 deletions python/ql/lib/semmle/python/dataflow/new/internal/LocalSources.qll
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import DataFlowPublic
private import DataFlowPrivate
private import semmle.python.internal.CachedStages
private import semmle.python.internal.Awaited
private import semmle.python.dataflow.new.internal.ImportStar

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

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

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

print(const_from_unknown) #$ use=moduleImport("unknown").getMember("const_from_unknown")

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

0 comments on commit 68d00a8

Please sign in to comment.