JS: make moduleImport() work for named imports

This commit is contained in:
Asger F
2019-02-12 17:12:05 +00:00
parent c133362660
commit be10f24de7
13 changed files with 122 additions and 8 deletions

View File

@@ -37,6 +37,9 @@ module DataFlow {
TThisNode(StmtContainer f) { f.(Function).getThisBinder() = f or f instanceof TopLevel } or
TUnusedParameterNode(SimpleParameter p) {
not exists(SsaExplicitDefinition ssa | p = ssa.getDef())
} or
TDestructuredModuleImportNode(ImportDeclaration decl) {
decl.getASpecifier() instanceof NamedImportSpecifier
}
/**
@@ -342,6 +345,28 @@ module DataFlow {
override string toString() { result = "reflective call" }
}
/**
* A node referring to the module imported at a named ES2015 import declaration.
*
* Default imports and namespace imports do not fall into this category, as the
* SSA definition of the local variable is used as the source of the module instead.
*/
private class DestructuredModuleImportNode extends Node, TDestructuredModuleImportNode {
ImportDeclaration imprt;
DestructuredModuleImportNode() { this = TDestructuredModuleImportNode(imprt) }
override BasicBlock getBasicBlock() { result = imprt.getBasicBlock() }
override predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
) {
imprt.getLocation().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
}
override string toString() { result = imprt.toString() }
}
/**
* A data flow node that reads or writes an object property or class member.
*
@@ -372,6 +397,12 @@ module DataFlow {
*/
abstract string getPropertyName();
/**
* Holds if this creates an alias for the property, as opposed to
* just being a read or write of the property.
*/
predicate isES2015PropertyBinding() { none() }
/**
* Holds if this data flow node accesses property `p` on base node `base`.
*/
@@ -659,6 +690,31 @@ module DataFlow {
override string getPropertyName() { none() }
}
/**
* A named import specifier seen as a property read on the imported module.
*/
private class NamedImportSpecifierAsPropRead extends PropRead {
ImportDeclaration imprt;
NamedImportSpecifier spec;
NamedImportSpecifierAsPropRead() {
spec = imprt.getASpecifier() and
exists(SsaExplicitDefinition ssa |
ssa.getDef() = spec and
this = TSsaDefNode(ssa)
)
}
override Node getBase() { result = TDestructuredModuleImportNode(imprt) }
override Expr getPropertyNameExpr() { result = spec.getImported() }
override string getPropertyName() { result = spec.getImportedName() }
override predicate isES2015PropertyBinding() { any() }
}
/**
* A data flow node representing an unused parameter.
*
@@ -875,6 +931,16 @@ module DataFlow {
*/
DataFlow::ThisNode thisNode(StmtContainer container) { result = TThisNode(container) }
/**
* INTERNAL. DO NOT USE.
*
* Gets the data flow node holding the reference to the module being destructured at
* the given import declaration.
*/
DataFlow::Node destructuredModuleImportNode(ImportDeclaration imprt) {
result = TDestructuredModuleImportNode(imprt)
}
/**
* A classification of flows that are not modeled, or only modeled incompletely, by
* `DataFlowNode`:

View File

@@ -427,6 +427,12 @@ class ModuleImportNode extends DataFlow::SourceNode {
is.getImportedName() = "default"
)
or
// `import { createServer } from 'http'`
exists(ImportDeclaration id |
this = DataFlow::destructuredModuleImportNode(id) and
id.getImportedPath().getValue() = path
)
or
// declared AMD dependency
exists(AMDModuleDefinition amd |
this = DataFlow::parameterNode(amd.getDependencyParameter(path))
@@ -456,14 +462,6 @@ ModuleImportNode moduleImport(string path) { result.getPath() = path }
*/
DataFlow::SourceNode moduleMember(string path, string m) {
result = moduleImport(path).getAPropertyRead(m)
or
exists(ImportDeclaration id, ImportSpecifier is, SsaExplicitDefinition ssa |
id.getImportedPath().getValue() = path and
is = id.getASpecifier() and
is.getImportedName() = m and
ssa.getDef() = is and
result = DataFlow::ssaDefinitionNode(ssa)
)
}
/**

View File

@@ -207,6 +207,8 @@ module SourceNode {
this instanceof DataFlow::Impl::InvokeNodeDef
or
DataFlow::thisNode(this, _)
or
this = DataFlow::destructuredModuleImportNode(_)
}
}
}

View File

@@ -175,6 +175,25 @@ private class AnalyzedNamespaceImport extends AnalyzedImport {
}
}
/**
* Flow analysis for namespace imports.
*/
private class AnalyzedDestructuredImport extends AnalyzedPropertyRead {
Module imported;
AnalyzedDestructuredImport() {
exists(ImportDeclaration id |
this = DataFlow::destructuredModuleImportNode(id) and
imported = id.getImportedModule()
)
}
override predicate reads(AbstractValue base, string propName) {
base = TAbstractModuleObject(imported) and
propName = "exports"
}
}
/**
* Flow analysis for `require` calls, interpreted as an implicit read of
* the `module.exports` property of the imported module.