Skip to content

Commit d2816b3

Browse files
authored
Merge pull request github#5240 from erik-krogh/vsPerf
Approved by asgerf
2 parents add960b + 539ef49 commit d2816b3

File tree

9 files changed

+117
-96
lines changed

9 files changed

+117
-96
lines changed

javascript/ql/src/semmle/javascript/AST.qll

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,7 @@ class ASTNode extends @ast_node, NodeInStmtContainer {
118118
int getNumChildStmt() { result = count(getAChildStmt()) }
119119

120120
/** Gets the parent node of this node, if any. */
121+
cached
121122
ASTNode getParent() { this = result.getAChild() }
122123

123124
/** Gets the first control flow node belonging to this syntactic entity. */

javascript/ql/src/semmle/javascript/Arrays.qll

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -156,9 +156,8 @@ private module ArrayDataFlow {
156156

157157
ArrayIndexingStep() {
158158
read = this and
159-
forex(InferredType type | type = read.getPropertyNameExpr().flow().analyze().getAType() |
160-
type = TTNumber()
161-
) and
159+
TTNumber() =
160+
unique(InferredType type | type = read.getPropertyNameExpr().flow().analyze().getAType()) and
162161
exists(VarAccess i, ExprOrVarDecl init |
163162
i = read.getPropertyNameExpr() and init = any(ForStmt f).getInit()
164163
|

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

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1305,12 +1305,9 @@ private predicate summarizedHigherOrderCall(
13051305
Function f, DataFlow::InvokeNode outer, DataFlow::InvokeNode inner, int j,
13061306
DataFlow::Node innerArg, DataFlow::SourceNode cbParm, PathSummary oldSummary
13071307
|
1308-
reachableFromInput(f, outer, arg, innerArg, cfg, oldSummary) and
1309-
// Only track actual parameter flow.
13101308
// Captured flow does not need to be summarized - it is handled by the local case in `higherOrderCall`.
13111309
not arg = DataFlow::capturedVariableNode(_) and
1312-
argumentPassing(outer, cb, f, cbParm) and
1313-
innerArg = inner.getArgument(j)
1310+
summarizedHigherOrderCallAux(f, outer, arg, innerArg, cfg, oldSummary, cbParm, inner, j, cb)
13141311
|
13151312
// direct higher-order call
13161313
cbParm.flowsTo(inner.getCalleeNode()) and
@@ -1326,6 +1323,21 @@ private predicate summarizedHigherOrderCall(
13261323
)
13271324
}
13281325

1326+
/**
1327+
* @see `summarizedHigherOrderCall`.
1328+
*/
1329+
pragma[noinline]
1330+
private predicate summarizedHigherOrderCallAux(
1331+
Function f, DataFlow::InvokeNode outer, DataFlow::Node arg, DataFlow::Node innerArg,
1332+
DataFlow::Configuration cfg, PathSummary oldSummary, DataFlow::SourceNode cbParm,
1333+
DataFlow::InvokeNode inner, int j, DataFlow::Node cb
1334+
) {
1335+
reachableFromInput(f, outer, arg, innerArg, cfg, oldSummary) and
1336+
// Only track actual parameter flow.
1337+
argumentPassing(outer, cb, f, cbParm) and
1338+
innerArg = inner.getArgument(j)
1339+
}
1340+
13291341
/**
13301342
* Holds if `arg` is passed as the `i`th argument to `callback` through a callback invocation.
13311343
*

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

Lines changed: 22 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -365,22 +365,38 @@ DataFlow::SourceNode globalObjectRef() {
365365
)
366366
or
367367
// DOM
368-
result = globalVarRef("window")
368+
result = globalVariable("window")
369369
or
370370
// Node.js
371-
result = globalVarRef("global")
371+
result = globalVariable("global")
372372
or
373373
// DOM and service workers
374-
result = globalVarRef("self")
374+
result = globalVariable("self")
375375
or
376376
// ECMAScript 2020
377-
result = globalVarRef("globalThis")
377+
result = globalVariable("globalThis")
378378
or
379379
// `require("global")`
380380
result = moduleImport("global")
381381
or
382382
// Closure library - based on AST to avoid recursion with Closure library model
383-
result = globalVarRef("goog").getAPropertyRead("global")
383+
result = globalVariable("goog").getAPropertyRead("global")
384+
}
385+
386+
/**
387+
* Gets a reference to a global variable `name`.
388+
* For example, if `name` is "foo":
389+
* ```js
390+
* foo
391+
* require('global/foo')
392+
* ```
393+
*/
394+
private DataFlow::SourceNode globalVariable(string name) {
395+
result.(GlobalVarRefNode).getName() = name
396+
or
397+
// `require("global/document")` or `require("global/window")`
398+
(name = "document" or name = "window") and
399+
result = moduleImport("global/" + name)
384400
}
385401

386402
/**
@@ -398,13 +414,9 @@ DataFlow::SourceNode globalObjectRef() {
398414
*/
399415
pragma[nomagic]
400416
DataFlow::SourceNode globalVarRef(string name) {
401-
result.(GlobalVarRefNode).getName() = name
417+
result = globalVariable(name)
402418
or
403419
result = globalObjectRef().getAPropertyReference(name)
404-
or
405-
// `require("global/document")` or `require("global/window")`
406-
(name = "document" or name = "window") and
407-
result = moduleImport("global/" + name)
408420
}
409421

410422
/**

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -324,6 +324,7 @@ class VarRefinementContext extends RefinementContext, TVarRefinementContext {
324324
}
325325

326326
/** Holds if `e` is nested inside a guard node. */
327+
pragma[nomagic]
327328
private predicate inGuard(Expr e) {
328329
e = any(GuardControlFlowNode g).getTest()
329330
or

javascript/ql/src/semmle/javascript/frameworks/PropertyProjection.qll

Lines changed: 48 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,53 @@ module PropertyProjection {
6565

6666
deprecated class CustomPropertyProjection = PropertyProjection::Range;
6767

68+
/**
69+
* Gets a callee of a simple property projection call.
70+
* This predicate is used exclusively in `SimplePropertyProjection`.
71+
*/
72+
pragma[noinline]
73+
private DataFlow::SourceNode getASimplePropertyProjectionCallee(
74+
boolean singleton, int selectorIndex, int objectIndex
75+
) {
76+
singleton = false and
77+
(
78+
result = LodashUnderscore::member("pick") and
79+
objectIndex = 0 and
80+
selectorIndex = [1 .. max(result.getACall().getNumArgument())]
81+
or
82+
result = LodashUnderscore::member("pickBy") and
83+
objectIndex = 0 and
84+
selectorIndex = 1
85+
or
86+
result = DataFlow::moduleMember("ramda", ["pick", "pickAll", "pickBy"]) and
87+
objectIndex = 1 and
88+
selectorIndex = 0
89+
or
90+
result = DataFlow::moduleMember("dotty", "search") and
91+
objectIndex = 0 and
92+
selectorIndex = 1
93+
)
94+
or
95+
singleton = true and
96+
(
97+
result = LodashUnderscore::member("get") and
98+
objectIndex = 0 and
99+
selectorIndex = 1
100+
or
101+
result = DataFlow::moduleMember("ramda", "path") and
102+
objectIndex = 1 and
103+
selectorIndex = 0
104+
or
105+
result = DataFlow::moduleMember("dottie", "get") and
106+
objectIndex = 0 and
107+
selectorIndex = 1
108+
or
109+
result = DataFlow::moduleMember("dotty", "get") and
110+
objectIndex = 0 and
111+
selectorIndex = 1
112+
)
113+
}
114+
68115
/**
69116
* A simple model of common property projection functions.
70117
*/
@@ -74,51 +121,7 @@ private class SimplePropertyProjection extends PropertyProjection::Range {
74121
boolean singleton;
75122

76123
SimplePropertyProjection() {
77-
exists(DataFlow::SourceNode callee | this = callee.getACall() |
78-
singleton = false and
79-
(
80-
callee = LodashUnderscore::member("pick") and
81-
objectIndex = 0 and
82-
selectorIndex = [1 .. getNumArgument()]
83-
or
84-
callee = LodashUnderscore::member("pickBy") and
85-
objectIndex = 0 and
86-
selectorIndex = 1
87-
or
88-
exists(string name |
89-
name = "pick" or
90-
name = "pickAll" or
91-
name = "pickBy"
92-
|
93-
callee = DataFlow::moduleMember("ramda", name) and
94-
objectIndex = 1 and
95-
selectorIndex = 0
96-
)
97-
or
98-
callee = DataFlow::moduleMember("dotty", "search") and
99-
objectIndex = 0 and
100-
selectorIndex = 1
101-
)
102-
or
103-
singleton = true and
104-
(
105-
callee = LodashUnderscore::member("get") and
106-
objectIndex = 0 and
107-
selectorIndex = 1
108-
or
109-
callee = DataFlow::moduleMember("ramda", "path") and
110-
objectIndex = 1 and
111-
selectorIndex = 0
112-
or
113-
callee = DataFlow::moduleMember("dottie", "get") and
114-
objectIndex = 0 and
115-
selectorIndex = 1
116-
or
117-
callee = DataFlow::moduleMember("dotty", "get") and
118-
objectIndex = 0 and
119-
selectorIndex = 1
120-
)
121-
)
124+
this = getASimplePropertyProjectionCallee(singleton, selectorIndex, objectIndex).getACall()
122125
}
123126

124127
override DataFlow::Node getObject() { result = getArgument(objectIndex) }

javascript/ql/src/semmle/javascript/frameworks/SystemCommandExecutors.qll

Lines changed: 27 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -5,33 +5,29 @@
55

66
import javascript
77

8-
private predicate execApi(string mod, string fn, int cmdArg, int optionsArg, boolean shell) {
9-
mod = "cross-spawn" and
10-
fn = "sync" and
11-
cmdArg = 0 and
12-
shell = false and
13-
optionsArg = -1
14-
or
15-
mod = "execa" and
16-
optionsArg = -1 and
8+
pragma[noinline]
9+
private predicate execApi(
10+
string mod, string fn, int cmdArg, int optionsArg, boolean shell, boolean sync
11+
) {
12+
sync = getSync(fn) and
1713
(
14+
mod = "cross-spawn" and
15+
fn = "sync" and
16+
cmdArg = 0 and
1817
shell = false and
19-
(
20-
fn = "node" or
21-
fn = "stdout" or
22-
fn = "stderr" or
23-
fn = "sync"
24-
)
18+
optionsArg = -1
2519
or
26-
shell = true and
20+
mod = "execa" and
21+
optionsArg = -1 and
2722
(
28-
fn = "command" or
29-
fn = "commandSync" or
30-
fn = "shell" or
31-
fn = "shellSync"
32-
)
33-
) and
34-
cmdArg = 0
23+
shell = false and
24+
fn = ["node", "stdout", "stderr", "sync"]
25+
or
26+
shell = true and
27+
fn = ["command", "commandSync", "shell", "shellSync"]
28+
) and
29+
cmdArg = 0
30+
)
3531
}
3632

3733
private predicate execApi(string mod, int cmdArg, int optionsArg, boolean shell) {
@@ -61,17 +57,16 @@ private class SystemCommandExecutors extends SystemCommandExecution, DataFlow::I
6157
SystemCommandExecutors() {
6258
exists(string mod |
6359
exists(string fn |
64-
execApi(mod, fn, cmdArg, optionsArg, shell) and
65-
sync = getSync(fn) and
66-
this = API::moduleImport(mod).getMember(fn).getReturn().getAUse()
60+
execApi(mod, fn, cmdArg, optionsArg, shell, sync) and
61+
this = API::moduleImport(mod).getMember(fn).getAnInvocation()
6762
)
6863
or
6964
execApi(mod, cmdArg, optionsArg, shell) and
7065
sync = false and
71-
this = API::moduleImport(mod).getReturn().getAUse()
66+
this = API::moduleImport(mod).getAnInvocation()
7267
)
7368
or
74-
this = API::moduleImport("foreground-child").getReturn().getAUse() and
69+
this = API::moduleImport("foreground-child").getACall() and
7570
cmdArg = 0 and
7671
optionsArg = 1 and
7772
shell = false and
@@ -115,19 +110,19 @@ private class RemoteCommandExecutor extends SystemCommandExecution, DataFlow::In
115110
int cmdArg;
116111

117112
RemoteCommandExecutor() {
118-
this = API::moduleImport("remote-exec").getReturn().getAUse() and
113+
this = API::moduleImport("remote-exec").getACall() and
119114
cmdArg = 1
120115
or
121116
exists(API::Node ssh2, API::Node client |
122117
ssh2 = API::moduleImport("ssh2") and
123118
client in [ssh2, ssh2.getMember("Client")] and
124-
this = client.getInstance().getMember("exec").getReturn().getAUse() and
119+
this = client.getInstance().getMember("exec").getACall() and
125120
cmdArg = 0
126121
)
127122
or
128123
exists(API::Node ssh2stream |
129124
ssh2stream = API::moduleImport("ssh2-streams").getMember("SSH2Stream") and
130-
this = ssh2stream.getInstance().getMember("exec").getReturn().getAUse() and
125+
this = ssh2stream.getInstance().getMember("exec").getACall() and
131126
cmdArg = 1
132127
)
133128
}
@@ -142,7 +137,7 @@ private class RemoteCommandExecutor extends SystemCommandExecution, DataFlow::In
142137
}
143138

144139
private class Opener extends SystemCommandExecution, DataFlow::InvokeNode {
145-
Opener() { this = API::moduleImport("opener").getReturn().getAUse() }
140+
Opener() { this = API::moduleImport("opener").getACall() }
146141

147142
override DataFlow::Node getACommandArgument() { result = getOptionArgument(1, "command") }
148143

javascript/ql/test/library-tests/Nodes/globalObjectRef.expected

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,5 @@
99
| tst.js:1:1:1:6 | window |
1010
| tst.js:3:1:3:6 | window |
1111
| tst.js:4:1:4:6 | window |
12-
| tst.js:4:1:4:13 | window.window |
1312
| tst.js:5:1:5:4 | self |
1413
| tst.js:6:1:6:10 | globalThis |

javascript/ql/test/library-tests/Nodes/globalVarRef.expected

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
| String | tst2.js:9:1:9:11 | this.String |
33
| document | tst2.js:2:1:2:26 | require ... ument") |
44
| document | tst.js:3:1:3:15 | window.document |
5-
| document | tst.js:4:1:4:22 | window. ... ocument |
65
| document | tst.js:5:1:5:13 | self.document |
76
| document | tst.js:6:1:6:19 | globalThis.document |
87
| foo | tst3.js:4:1:4:5 | w.foo |

0 commit comments

Comments
 (0)