@@ -18,7 +18,7 @@ predicate localFlowStep(Node nodeFrom, Node nodeTo) { simpleLocalFlowStep(nodeFr
1818predicate localFlow ( Node source , Node sink ) { localFlowStep * ( source , sink ) }
1919
2020/**
21- * Gets an EssaNode that holds the module imported by `name`.
21+ * Gets a `Node` that refers to the module referenced by `name`.
2222 * Note that for the statement `import pkg.mod`, the new variable introduced is `pkg` that is a
2323 * reference to the module `pkg`.
2424 *
@@ -27,6 +27,9 @@ predicate localFlow(Node source, Node sink) { localFlowStep*(source, sink) }
2727 * 2. `from <package> import <module>` when `<name> = <package> + "." + <module>`
2828 * 3. `from <module> import <member>` when `<name> = <module> + "." + <member>`
2929 *
30+ * Finally, in `from <module> import <member>` we consider the `ImportExpr` corresponding to
31+ * `<module>` to be a reference to that module.
32+ *
3033 * Note:
3134 * While it is technically possible that `import mypkg.foo` and `from mypkg import foo` can give different values,
3235 * it's highly unlikely that this will be a problem in production level code.
@@ -48,10 +51,25 @@ Node importModule(string name) {
4851 result .( EssaNode ) .getVar ( ) .( AssignmentDefinition ) .getSourceVariable ( ) = var
4952 )
5053 or
51- // In `from module import attr`, we want to consider `module` to be an expression that refers to a
52- // module of that name, as this allows us to refer to attributes of this module, even if it's
53- // never imported directly. Note that there crucially isn't any _flow_ from `module` to references
54- // to that same identifier.
54+ // Although it may seem superfluous to consider the `foo` part of `from foo import bar as baz` to
55+ // be a reference to a module (since that reference only makes sense locally within the `import`
56+ // statement), it's important for our use of type trackers to consider this local reference to
57+ // also refer to the `foo` module. That way, if one wants to track references to the `bar`
58+ // attribute using a type tracker, one can simply write
59+ //
60+ // ```ql
61+ // DataFlow::Node bar_attr_tracker(TypeTracker t) {
62+ // t.startInAttr("bar") and
63+ // result = foo_module_tracker()
64+ // or
65+ // exists(TypeTracker t2 | result = bar_attr_tracker(t2).track(t2, t))
66+ // }
67+ // ```
68+ //
69+ // Where `foo_module_tracker` is a type tracker that tracks references to the `foo` module.
70+ // Because named imports are modelled as `AttrRead`s, the statement `from foo import bar as baz`
71+ // is interpreted as if it was an assignment `baz = foo.bar`, which means `baz` gets tracked as a
72+ // reference to `foo.bar`, as desired.
5573 result .asCfgNode ( ) .getNode ( ) = any ( ImportExpr i | i .getAnImportedModuleName ( ) = name )
5674}
5775
0 commit comments