Skip to content

Commit c378981

Browse files
authored
Python: Support import * in API graphs
1 parent 8b6b4dd commit c378981

File tree

1 file changed

+44
-0
lines changed

1 file changed

+44
-0
lines changed

python/ql/src/semmle/python/ApiGraphs.qll

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -440,6 +440,23 @@ module API {
440440
m = n.getEnclosingModule()
441441
}
442442

443+
/**
444+
* Holds if `n` is an access of a variable called `name` (which is _not_ the name of a
445+
* built-in, and which is _not_ a global defined in the enclosing module) inside the scope `s`.
446+
*/
447+
private predicate name_possibly_defined_in_import_star(NameNode n, string name, Scope s) {
448+
n.isLoad() and
449+
name = n.getId() and
450+
// Not already defined in an enclosing scope.
451+
not exists(LocalVariable v |
452+
v.getId() = name and v.getScope().getEnclosingScope*() = n.getScope()
453+
) and
454+
not name = getBuiltInName() and
455+
s = n.getScope().getEnclosingScope*() and
456+
exists(potential_import_star_base(s)) and
457+
not global_name_defined_in_module(name, n.getEnclosingModule())
458+
}
459+
443460
/** Holds if a global variable called `name` is assigned a value in the module `m`. */
444461
private predicate global_name_defined_in_module(string name, Module m) {
445462
exists(NameNode n |
@@ -450,6 +467,24 @@ module API {
450467
)
451468
}
452469

470+
/**
471+
* Gets the API graph node for all modules imported with `from ... import *` inside the scope `s`.
472+
*
473+
* For example, given
474+
*
475+
* `from foo.bar import *`
476+
*
477+
* this would be the API graph node with the path
478+
*
479+
* `moduleImport("foo").getMember("bar")`
480+
*/
481+
private TApiNode potential_import_star_base(Scope s) {
482+
exists(DataFlow::Node ref |
483+
ref.asCfgNode() = any(ImportStarNode n | n.getScope() = s).getModule() and
484+
use(result, ref)
485+
)
486+
}
487+
453488
/**
454489
* Holds if `ref` is a use of a node that should have an incoming edge from `base` labeled
455490
* `lbl` in the API graph.
@@ -492,6 +527,15 @@ module API {
492527
// Built-ins, treated as members of the module `builtins`
493528
base = MkModuleImport("builtins") and
494529
lbl = Label::member(any(string name | ref = likely_builtin(name)))
530+
or
531+
// Unknown variables that may belong to a module imported with `import *`
532+
exists(Scope s |
533+
base = potential_import_star_base(s) and
534+
lbl =
535+
Label::member(any(string name |
536+
name_possibly_defined_in_import_star(ref.asCfgNode(), name, s)
537+
))
538+
)
495539
}
496540

497541
/**

0 commit comments

Comments
 (0)