@@ -424,13 +424,8 @@ module API {
424424 * a value in the module `m`.
425425 */
426426 private predicate possible_builtin_defined_in_module ( string name , Module m ) {
427- exists ( NameNode n |
428- not exists ( LocalVariable v | n .defines ( v ) ) and
429- n .isStore ( ) and
430- name = n .getId ( ) and
431- name = getBuiltInName ( ) and
432- m = n .getEnclosingModule ( )
433- )
427+ global_name_defined_in_module ( name , m ) and
428+ name = getBuiltInName ( )
434429 }
435430
436431 /**
@@ -445,6 +440,51 @@ module API {
445440 m = n .getEnclosingModule ( )
446441 }
447442
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 ( ) = n .getScope ( ) .getEnclosingScope * ( )
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+
460+ /** Holds if a global variable called `name` is assigned a value in the module `m`. */
461+ private predicate global_name_defined_in_module ( string name , Module m ) {
462+ exists ( NameNode n |
463+ not exists ( LocalVariable v | n .defines ( v ) ) and
464+ n .isStore ( ) and
465+ name = n .getId ( ) and
466+ m = n .getEnclosingModule ( )
467+ )
468+ }
469+
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+
448488 /**
449489 * Holds if `ref` is a use of a node that should have an incoming edge from `base` labeled
450490 * `lbl` in the API graph.
@@ -487,6 +527,15 @@ module API {
487527 // Built-ins, treated as members of the module `builtins`
488528 base = MkModuleImport ( "builtins" ) and
489529 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+ )
490539 }
491540
492541 /**
0 commit comments