JS: Refactor the public access path API

This commit is contained in:
Asger F
2019-10-25 11:47:50 +01:00
parent e90516d4d8
commit c365833731
6 changed files with 136 additions and 70 deletions

View File

@@ -230,7 +230,7 @@ module Closure {
* Gets the closure namespace path addressed by the given data flow node, if any.
*/
string getClosureNamespaceFromSourceNode(DataFlow::SourceNode node) {
result = GlobalAccessPath::getAccessPath(node) and
node = AccessPath::getAReferenceOrAssignmentTo(result) and
hasClosureNamespacePrefix(result)
}
@@ -238,7 +238,7 @@ module Closure {
* Gets the closure namespace path written to by the given property write, if any.
*/
string getWrittenClosureNamespace(DataFlow::PropWrite node) {
result = GlobalAccessPath::fromRhs(node.getRhs()) and
node.getRhs() = AccessPath::getAnAssignmentTo(result) and
hasClosureNamespacePrefix(result)
}

View File

@@ -4,11 +4,41 @@
import javascript
deprecated
module GlobalAccessPath {
/**
* DEPRECATED. Instead use `AccessPath::getAReferenceTo` with the result and parameter reversed.
*/
pragma[inline]
string fromReference(DataFlow::Node node) {
node = AccessPath::getAReferenceTo(result)
}
/**
* DEPRECATED. Instead use `AccessPath::getAnAssignmentTo` with the result and parameter reversed.
*/
pragma[inline]
string fromRhs(DataFlow::Node node) {
node = AccessPath::getAnAssignmentTo(result)
}
/**
* DEPRECATED. Use `AccessPath::getAReferenceOrAssignmentTo`.
*/
pragma[inline]
string getAccessPath(DataFlow::Node node) {
result = fromReference(node)
or
not exists(fromReference(node)) and
result = fromRhs(node)
}
}
module AccessPath {
/**
* A source node that can be the root of an access path.
*/
private class Root extends DataFlow::SourceNode {
class Root extends DataFlow::SourceNode {
Root() {
not this.accessesGlobal(_) and
not this instanceof DataFlow::PropRead and
@@ -53,7 +83,7 @@ module GlobalAccessPath {
* ```
*/
cached
string fromReference(DataFlow::Node node, Root root) {
private string fromReference(DataFlow::Node node, Root root) {
root = node and
not root.isGlobal() and
result = ""
@@ -110,29 +140,6 @@ module GlobalAccessPath {
)
}
/**
* Gets the global access path referred to by `node`.
*
* This holds for direct references as well as for aliases
* established through local data flow.
*
* Examples:
* ```
* function f() {
* let v = foo.bar; // reference to 'foo.bar'
* v.baz; // reference to 'foo.bar.baz'
* }
*
* (function(ns) {
* ns.x; // reference to 'NS.x'
* })(NS = NS || {});
* ```
*/
cached
string fromReference(DataFlow::Node node) {
result = fromReference(node, DataFlow::globalAccessPathRootPseudoNode())
}
/**
* Holds if `rhs` is the right-hand side of a self-assignment.
*
@@ -183,7 +190,7 @@ module GlobalAccessPath {
* ```
*/
cached
string fromRhs(DataFlow::Node node, Root root) {
private string fromRhs(DataFlow::Node node, Root root) {
exists(DataFlow::PropWrite write, string baseName |
node = write.getRhs() and
result = baseName + "." + write.getPropertyName()
@@ -225,42 +232,109 @@ module GlobalAccessPath {
}
/**
* Gets the global access path `node` is being assigned to, if any.
* Gets a node that refers to the given access path relative to the given `root` node,
* or `root` itself if the access path is empty.
*
* Only holds for the immediate right-hand side of an assignment or property, not
* for nodes that transitively flow there.
* This works for direct references as well as for aliases established through local data flow.
*
* For example, the class nodes below all map to `foo.bar`:
* Note that non-empty access paths contain an initial `.`, such as in `.foo.bar`.
*
* For example:
* ```
* function f(x) {
* let a = x.f.g; // reference to (x, ".f.g")
* let b = a.h; // reference to (x, ".f.g.h")
* }
* ```
*/
DataFlow::Node getAReferenceTo(Root root, string path) {
path = fromReference(result, root) and
not root.isGlobal()
}
/**
* Gets a node that refers to the given global access path.
*
* This works for direct references as well as for aliases established through local data flow.
*
* Examples:
* ```
* function f() {
* let v = foo.bar; // reference to 'foo.bar'
* v.baz; // reference to 'foo.bar.baz'
* }
*
* (function(ns) {
* ns.x; // reference to 'NS.x'
* })(NS = NS || {});
* ```
*/
DataFlow::Node getAReferenceTo(string path) {
path = fromReference(result, DataFlow::globalAccessPathRootPseudoNode())
}
/**
* Gets a node that is assigned to the given access path relative to the given `root` node.
*
* Only gets the immediate right-hand side of an assignment or property, not
* nodes that transitively flow there.
*
* Note that access paths contain an initial `.`, such as in `.foo.bar`.
*
* For example, the class nodes below are all assignments to `(x, ".foo.bar")`.
* ```
* function f(x) {
* x.foo.bar = class {};
* x.foo = { bar: class() };
* let alias = x;
* alias.foo.bar = class {};
* }
* ```
*/
DataFlow::Node getAnAssignmentTo(Root root, string path) {
path = fromRhs(result, root) and
not root.isGlobal()
}
/**
* Gets a node that is assigned to the given global access path.
*
* Only gets the immediate right-hand side of an assignment or property or a global declaration,
* not nodes that transitively flow there.
*
* For example, the class nodes below are all assignmetns to `foo.bar`:
* ```
* foo.bar = class {};
*
* foo = { bar: class {} };
*
* (function(f) {
* f.bar = class {}
* })(foo = foo || {});
* ```
*/
cached
string fromRhs(DataFlow::Node node) {
result = fromRhs(node, DataFlow::globalAccessPathRootPseudoNode())
DataFlow::Node getAnAssignmentTo(string path) {
path = fromRhs(result, DataFlow::globalAccessPathRootPseudoNode())
}
/**
* Gets the access path relative to `root` referenced by or assigned to `node`.
* Gets a node that refers to or is assigned to the given global access path.
*
* See `getAReferenceTo` and `getAnAssignmentTo` for more details.
*/
string getAccessPath(DataFlow::Node node, Root root) {
result = fromReference(node, root)
DataFlow::Node getAReferenceOrAssignmentTo(string path) {
result = getAReferenceTo(path)
or
not exists(fromReference(node, root)) and
result = fromRhs(node, root)
result = getAnAssignmentTo(path)
}
/**
* Gets the global access path referenced by or assigned to `node`.
* Gets a node that refers to or is assigned to the given access path.
*
* See `getAReferenceTo` and `getAnAssignmentTo` for more details.
*/
string getAccessPath(DataFlow::Node node) {
result = getAccessPath(node, DataFlow::globalAccessPathRootPseudoNode())
DataFlow::Node getAReferenceOrAssignmentTo(Root root, string path) {
result = getAReferenceTo(root, path)
or
result = getAnAssignmentTo(root, path)
}
/**
@@ -269,14 +343,13 @@ module GlobalAccessPath {
pragma[inline]
predicate step(DataFlow::Node pred, DataFlow::Node succ) {
exists(string name, Root root |
name = fromRhs(pred, root) and
name = fromReference(succ, root) and
not root.isGlobal()
pred = getAnAssignmentTo(root, name) and
succ = getAReferenceTo(root, name)
)
or
exists(string name |
name = fromRhs(pred) and
name = fromReference(succ) and
pred = getAnAssignmentTo(name) and
succ = getAReferenceTo(name) and
isAssignedInUniqueFile(name)
)
}

View File

@@ -582,7 +582,7 @@ module JSDoc {
* within this container.
*/
string resolveAlias(string alias) {
result = GlobalAccessPath::getAccessPath(getNodeFromAlias(alias))
getNodeFromAlias(alias) = AccessPath::getAReferenceOrAssignmentTo(result)
}
/**

View File

@@ -771,11 +771,7 @@ class ClassNode extends DataFlow::SourceNode {
*/
pragma[noinline]
predicate hasQualifiedName(string name) {
exists(DataFlow::Node rhs |
getAClassReference().flowsTo(rhs) and
name = GlobalAccessPath::fromRhs(rhs) and
GlobalAccessPath::isAssignedInUniqueFile(name)
)
getAClassReference().flowsTo(AccessPath::getAnAssignmentTo(name))
}
}
@@ -883,7 +879,7 @@ module ClassNode {
}
private DataFlow::PropRef getAPrototypeReferenceInFile(string name, File f) {
GlobalAccessPath::getAccessPath(result.getBase()) = name and
result.getBase() = AccessPath::getAReferenceOrAssignmentTo(name) and
result.getPropertyName() = "prototype" and
result.getFile() = f
}
@@ -904,7 +900,7 @@ module ClassNode {
)
or
exists(string name |
name = GlobalAccessPath::fromRhs(this) and
this = AccessPath::getAnAssignmentTo(name) and
exists(getAPrototypeReferenceInFile(name, getFile()))
)
)

View File

@@ -13,10 +13,9 @@ private class PropertyName extends string {
PropertyName() {
this = any(DataFlow::PropRef pr).getPropertyName()
or
GlobalAccessPath::isAssignedInUniqueFile(this)
AccessPath::isAssignedInUniqueFile(this)
or
this = GlobalAccessPath::fromRhs(_, _) and
this != ""
exists(AccessPath::getAnAssignmentTo(_, this))
}
}
@@ -100,31 +99,29 @@ module StepSummary {
or
// Store to global access path
exists(string name |
name = GlobalAccessPath::fromRhs(pred) and
GlobalAccessPath::isAssignedInUniqueFile(name) and
pred = AccessPath::getAnAssignmentTo(name) and
AccessPath::isAssignedInUniqueFile(name) and
succ = DataFlow::globalAccessPathRootPseudoNode() and
summary = StoreStep(name)
)
or
// Load from global access path
exists(string name |
name = GlobalAccessPath::fromReference(succ) and
GlobalAccessPath::isAssignedInUniqueFile(name) and
succ = AccessPath::getAReferenceTo(name) and
AccessPath::isAssignedInUniqueFile(name) and
pred = DataFlow::globalAccessPathRootPseudoNode() and
summary = LoadStep(name)
)
or
// Store to non-global access path
exists(string name |
name = GlobalAccessPath::fromRhs(pred, succ) and
succ != DataFlow::globalAccessPathRootPseudoNode() and
pred = AccessPath::getAnAssignmentTo(succ, name) and
summary = StoreStep(name)
)
or
// Load from non-global access path
exists(string name |
name = GlobalAccessPath::fromReference(succ, pred) and
pred != DataFlow::globalAccessPathRootPseudoNode() and
succ = AccessPath::getAReferenceTo(pred, name) and
summary = LoadStep(name) and
name != ""
)

View File

@@ -43,7 +43,7 @@ module CallGraph {
or
imprecision = 0 and
t.start() and
GlobalAccessPath::step(function, result)
AccessPath::step(function, result)
or
imprecision = 0 and
exists(DataFlow::ClassNode cls |