Python: Support named imports as attribute reads

Required a small change in `DataFlow::importModule` to get the desired
behaviour (cf. the type trackers defined in `moduleattr.ql`, but this
should be harmless. The node that is added doesn't have any flow
anywhere.
This commit is contained in:
Taus Brock-Nannestad
2020-10-08 18:08:55 +02:00
parent df447c0af9
commit d46453caaa
6 changed files with 98 additions and 2 deletions

View File

@@ -218,3 +218,31 @@ private class GetAttrCallAsAttrRead extends AttrRead, CfgNode {
result = this.getAttributeNameExpr().asExpr().(StrConst).getText()
}
}
/**
* A convenience class for embedding `ImportMemberNode` into `DataFlowCfgNode`, as the former is not
* obviously a subtype of the latter.
*/
private class DataFlowImportMemberNode extends ImportMemberNode, DataFlowCfgNode { }
/**
* Represents a named import as an attribute read. That is,
* ```python
* from module import attr as attr_ref
* ```
* is treated as if it is a read of the attribute `module.attr`, even if `module` is not imported directly.
*/
private class ModuleAttributeImportAsAttrRead extends AttrRead, CfgNode {
override DataFlowImportMemberNode node;
override Node getObject() { result.asCfgNode() = node.getModule(_) }
override ExprNode getAttributeNameExpr() {
// The name of an imported attribute doesn't exist as a `Node` in the control flow graph, as it
// can only ever be an identifier, and is therefore represented directly as a string.
// Use `getAttributeName` to access the name of the attribute.
none()
}
override string getAttributeName() { exists(node.getModule(result)) }
}

View File

@@ -36,7 +36,7 @@ predicate localFlow(Node source, Node sink) { localFlowStep*(source, sink) }
*
* Also see `DataFlow::importMember`
*/
EssaNode importModule(string name) {
Node importModule(string name) {
exists(Variable var, Import imp, Alias alias |
alias = imp.getAName() and
alias.getAsname() = var.getAStore() and
@@ -45,8 +45,14 @@ EssaNode importModule(string name) {
or
name = alias.getValue().(ImportExpr).getImportedModuleName()
) and
result.getVar().(AssignmentDefinition).getSourceVariable() = var
result.(EssaNode).getVar().(AssignmentDefinition).getSourceVariable() = var
)
or
// In `from module import attr`, we want to consider `module` to be an expression that refers to a
// module of that name, as this allows us to refer to attributes of this module, even if it's
// never imported directly. Note that there crucially isn't any _flow_ from `module` to references
// to that same identifier.
result.asCfgNode().getNode() = any(ImportExpr i | i.getAnImportedModuleName() = name)
}
/**