Skip to content

Commit c365833

Browse files
committed
JS: Refactor the public access path API
1 parent e90516d commit c365833

6 files changed

Lines changed: 136 additions & 70 deletions

File tree

javascript/ql/src/semmle/javascript/Closure.qll

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -230,15 +230,15 @@ module Closure {
230230
* Gets the closure namespace path addressed by the given data flow node, if any.
231231
*/
232232
string getClosureNamespaceFromSourceNode(DataFlow::SourceNode node) {
233-
result = GlobalAccessPath::getAccessPath(node) and
233+
node = AccessPath::getAReferenceOrAssignmentTo(result) and
234234
hasClosureNamespacePrefix(result)
235235
}
236236

237237
/**
238238
* Gets the closure namespace path written to by the given property write, if any.
239239
*/
240240
string getWrittenClosureNamespace(DataFlow::PropWrite node) {
241-
result = GlobalAccessPath::fromRhs(node.getRhs()) and
241+
node.getRhs() = AccessPath::getAnAssignmentTo(result) and
242242
hasClosureNamespacePrefix(result)
243243
}
244244

javascript/ql/src/semmle/javascript/GlobalAccessPaths.qll

Lines changed: 121 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,41 @@
44

55
import javascript
66

7+
deprecated
78
module GlobalAccessPath {
9+
/**
10+
* DEPRECATED. Instead use `AccessPath::getAReferenceTo` with the result and parameter reversed.
11+
*/
12+
pragma[inline]
13+
string fromReference(DataFlow::Node node) {
14+
node = AccessPath::getAReferenceTo(result)
15+
}
16+
17+
/**
18+
* DEPRECATED. Instead use `AccessPath::getAnAssignmentTo` with the result and parameter reversed.
19+
*/
20+
pragma[inline]
21+
string fromRhs(DataFlow::Node node) {
22+
node = AccessPath::getAnAssignmentTo(result)
23+
}
24+
25+
/**
26+
* DEPRECATED. Use `AccessPath::getAReferenceOrAssignmentTo`.
27+
*/
28+
pragma[inline]
29+
string getAccessPath(DataFlow::Node node) {
30+
result = fromReference(node)
31+
or
32+
not exists(fromReference(node)) and
33+
result = fromRhs(node)
34+
}
35+
}
36+
37+
module AccessPath {
838
/**
939
* A source node that can be the root of an access path.
1040
*/
11-
private class Root extends DataFlow::SourceNode {
41+
class Root extends DataFlow::SourceNode {
1242
Root() {
1343
not this.accessesGlobal(_) and
1444
not this instanceof DataFlow::PropRead and
@@ -53,7 +83,7 @@ module GlobalAccessPath {
5383
* ```
5484
*/
5585
cached
56-
string fromReference(DataFlow::Node node, Root root) {
86+
private string fromReference(DataFlow::Node node, Root root) {
5787
root = node and
5888
not root.isGlobal() and
5989
result = ""
@@ -110,29 +140,6 @@ module GlobalAccessPath {
110140
)
111141
}
112142

113-
/**
114-
* Gets the global access path referred to by `node`.
115-
*
116-
* This holds for direct references as well as for aliases
117-
* established through local data flow.
118-
*
119-
* Examples:
120-
* ```
121-
* function f() {
122-
* let v = foo.bar; // reference to 'foo.bar'
123-
* v.baz; // reference to 'foo.bar.baz'
124-
* }
125-
*
126-
* (function(ns) {
127-
* ns.x; // reference to 'NS.x'
128-
* })(NS = NS || {});
129-
* ```
130-
*/
131-
cached
132-
string fromReference(DataFlow::Node node) {
133-
result = fromReference(node, DataFlow::globalAccessPathRootPseudoNode())
134-
}
135-
136143
/**
137144
* Holds if `rhs` is the right-hand side of a self-assignment.
138145
*
@@ -183,7 +190,7 @@ module GlobalAccessPath {
183190
* ```
184191
*/
185192
cached
186-
string fromRhs(DataFlow::Node node, Root root) {
193+
private string fromRhs(DataFlow::Node node, Root root) {
187194
exists(DataFlow::PropWrite write, string baseName |
188195
node = write.getRhs() and
189196
result = baseName + "." + write.getPropertyName()
@@ -225,42 +232,109 @@ module GlobalAccessPath {
225232
}
226233

227234
/**
228-
* Gets the global access path `node` is being assigned to, if any.
235+
* Gets a node that refers to the given access path relative to the given `root` node,
236+
* or `root` itself if the access path is empty.
229237
*
230-
* Only holds for the immediate right-hand side of an assignment or property, not
231-
* for nodes that transitively flow there.
238+
* This works for direct references as well as for aliases established through local data flow.
239+
*
240+
* Note that non-empty access paths contain an initial `.`, such as in `.foo.bar`.
232241
*
233-
* For example, the class nodes below all map to `foo.bar`:
242+
* For example:
234243
* ```
235-
* foo.bar = class {};
244+
* function f(x) {
245+
* let a = x.f.g; // reference to (x, ".f.g")
246+
* let b = a.h; // reference to (x, ".f.g.h")
247+
* }
248+
* ```
249+
*/
250+
DataFlow::Node getAReferenceTo(Root root, string path) {
251+
path = fromReference(result, root) and
252+
not root.isGlobal()
253+
}
254+
255+
/**
256+
* Gets a node that refers to the given global access path.
236257
*
237-
* foo = { bar: class {} };
258+
* This works for direct references as well as for aliases established through local data flow.
259+
*
260+
* Examples:
261+
* ```
262+
* function f() {
263+
* let v = foo.bar; // reference to 'foo.bar'
264+
* v.baz; // reference to 'foo.bar.baz'
265+
* }
266+
*
267+
* (function(ns) {
268+
* ns.x; // reference to 'NS.x'
269+
* })(NS = NS || {});
270+
* ```
271+
*/
272+
DataFlow::Node getAReferenceTo(string path) {
273+
path = fromReference(result, DataFlow::globalAccessPathRootPseudoNode())
274+
}
275+
276+
/**
277+
* Gets a node that is assigned to the given access path relative to the given `root` node.
278+
*
279+
* Only gets the immediate right-hand side of an assignment or property, not
280+
* nodes that transitively flow there.
281+
*
282+
* Note that access paths contain an initial `.`, such as in `.foo.bar`.
238283
*
284+
* For example, the class nodes below are all assignments to `(x, ".foo.bar")`.
285+
* ```
286+
* function f(x) {
287+
* x.foo.bar = class {};
288+
* x.foo = { bar: class() };
289+
* let alias = x;
290+
* alias.foo.bar = class {};
291+
* }
292+
* ```
293+
*/
294+
DataFlow::Node getAnAssignmentTo(Root root, string path) {
295+
path = fromRhs(result, root) and
296+
not root.isGlobal()
297+
}
298+
299+
/**
300+
* Gets a node that is assigned to the given global access path.
301+
*
302+
* Only gets the immediate right-hand side of an assignment or property or a global declaration,
303+
* not nodes that transitively flow there.
304+
*
305+
* For example, the class nodes below are all assignmetns to `foo.bar`:
306+
* ```
307+
* foo.bar = class {};
308+
* foo = { bar: class {} };
239309
* (function(f) {
240310
* f.bar = class {}
241311
* })(foo = foo || {});
242312
* ```
243313
*/
244-
cached
245-
string fromRhs(DataFlow::Node node) {
246-
result = fromRhs(node, DataFlow::globalAccessPathRootPseudoNode())
314+
DataFlow::Node getAnAssignmentTo(string path) {
315+
path = fromRhs(result, DataFlow::globalAccessPathRootPseudoNode())
247316
}
248317

249318
/**
250-
* Gets the access path relative to `root` referenced by or assigned to `node`.
319+
* Gets a node that refers to or is assigned to the given global access path.
320+
*
321+
* See `getAReferenceTo` and `getAnAssignmentTo` for more details.
251322
*/
252-
string getAccessPath(DataFlow::Node node, Root root) {
253-
result = fromReference(node, root)
323+
DataFlow::Node getAReferenceOrAssignmentTo(string path) {
324+
result = getAReferenceTo(path)
254325
or
255-
not exists(fromReference(node, root)) and
256-
result = fromRhs(node, root)
326+
result = getAnAssignmentTo(path)
257327
}
258328

259329
/**
260-
* Gets the global access path referenced by or assigned to `node`.
330+
* Gets a node that refers to or is assigned to the given access path.
331+
*
332+
* See `getAReferenceTo` and `getAnAssignmentTo` for more details.
261333
*/
262-
string getAccessPath(DataFlow::Node node) {
263-
result = getAccessPath(node, DataFlow::globalAccessPathRootPseudoNode())
334+
DataFlow::Node getAReferenceOrAssignmentTo(Root root, string path) {
335+
result = getAReferenceTo(root, path)
336+
or
337+
result = getAnAssignmentTo(root, path)
264338
}
265339

266340
/**
@@ -269,14 +343,13 @@ module GlobalAccessPath {
269343
pragma[inline]
270344
predicate step(DataFlow::Node pred, DataFlow::Node succ) {
271345
exists(string name, Root root |
272-
name = fromRhs(pred, root) and
273-
name = fromReference(succ, root) and
274-
not root.isGlobal()
346+
pred = getAnAssignmentTo(root, name) and
347+
succ = getAReferenceTo(root, name)
275348
)
276349
or
277350
exists(string name |
278-
name = fromRhs(pred) and
279-
name = fromReference(succ) and
351+
pred = getAnAssignmentTo(name) and
352+
succ = getAReferenceTo(name) and
280353
isAssignedInUniqueFile(name)
281354
)
282355
}

javascript/ql/src/semmle/javascript/JSDoc.qll

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -582,7 +582,7 @@ module JSDoc {
582582
* within this container.
583583
*/
584584
string resolveAlias(string alias) {
585-
result = GlobalAccessPath::getAccessPath(getNodeFromAlias(alias))
585+
getNodeFromAlias(alias) = AccessPath::getAReferenceOrAssignmentTo(result)
586586
}
587587

588588
/**

javascript/ql/src/semmle/javascript/dataflow/Nodes.qll

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -771,11 +771,7 @@ class ClassNode extends DataFlow::SourceNode {
771771
*/
772772
pragma[noinline]
773773
predicate hasQualifiedName(string name) {
774-
exists(DataFlow::Node rhs |
775-
getAClassReference().flowsTo(rhs) and
776-
name = GlobalAccessPath::fromRhs(rhs) and
777-
GlobalAccessPath::isAssignedInUniqueFile(name)
778-
)
774+
getAClassReference().flowsTo(AccessPath::getAnAssignmentTo(name))
779775
}
780776
}
781777

@@ -883,7 +879,7 @@ module ClassNode {
883879
}
884880

885881
private DataFlow::PropRef getAPrototypeReferenceInFile(string name, File f) {
886-
GlobalAccessPath::getAccessPath(result.getBase()) = name and
882+
result.getBase() = AccessPath::getAReferenceOrAssignmentTo(name) and
887883
result.getPropertyName() = "prototype" and
888884
result.getFile() = f
889885
}
@@ -904,7 +900,7 @@ module ClassNode {
904900
)
905901
or
906902
exists(string name |
907-
name = GlobalAccessPath::fromRhs(this) and
903+
this = AccessPath::getAnAssignmentTo(name) and
908904
exists(getAPrototypeReferenceInFile(name, getFile()))
909905
)
910906
)

javascript/ql/src/semmle/javascript/dataflow/TypeTracking.qll

Lines changed: 8 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,9 @@ private class PropertyName extends string {
1313
PropertyName() {
1414
this = any(DataFlow::PropRef pr).getPropertyName()
1515
or
16-
GlobalAccessPath::isAssignedInUniqueFile(this)
16+
AccessPath::isAssignedInUniqueFile(this)
1717
or
18-
this = GlobalAccessPath::fromRhs(_, _) and
19-
this != ""
18+
exists(AccessPath::getAnAssignmentTo(_, this))
2019
}
2120
}
2221

@@ -100,31 +99,29 @@ module StepSummary {
10099
or
101100
// Store to global access path
102101
exists(string name |
103-
name = GlobalAccessPath::fromRhs(pred) and
104-
GlobalAccessPath::isAssignedInUniqueFile(name) and
102+
pred = AccessPath::getAnAssignmentTo(name) and
103+
AccessPath::isAssignedInUniqueFile(name) and
105104
succ = DataFlow::globalAccessPathRootPseudoNode() and
106105
summary = StoreStep(name)
107106
)
108107
or
109108
// Load from global access path
110109
exists(string name |
111-
name = GlobalAccessPath::fromReference(succ) and
112-
GlobalAccessPath::isAssignedInUniqueFile(name) and
110+
succ = AccessPath::getAReferenceTo(name) and
111+
AccessPath::isAssignedInUniqueFile(name) and
113112
pred = DataFlow::globalAccessPathRootPseudoNode() and
114113
summary = LoadStep(name)
115114
)
116115
or
117116
// Store to non-global access path
118117
exists(string name |
119-
name = GlobalAccessPath::fromRhs(pred, succ) and
120-
succ != DataFlow::globalAccessPathRootPseudoNode() and
118+
pred = AccessPath::getAnAssignmentTo(succ, name) and
121119
summary = StoreStep(name)
122120
)
123121
or
124122
// Load from non-global access path
125123
exists(string name |
126-
name = GlobalAccessPath::fromReference(succ, pred) and
127-
pred != DataFlow::globalAccessPathRootPseudoNode() and
124+
succ = AccessPath::getAReferenceTo(pred, name) and
128125
summary = LoadStep(name) and
129126
name != ""
130127
)

javascript/ql/src/semmle/javascript/dataflow/internal/CallGraphs.qll

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ module CallGraph {
4343
or
4444
imprecision = 0 and
4545
t.start() and
46-
GlobalAccessPath::step(function, result)
46+
AccessPath::step(function, result)
4747
or
4848
imprecision = 0 and
4949
exists(DataFlow::ClassNode cls |

0 commit comments

Comments
 (0)