@@ -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