Skip to content

Commit b5c8f22

Browse files
authored
Merge pull request github#3805 from esbena/js/seal-freeze-flow
Approved by asgerf
2 parents eecc3ca + 3ca6031 commit b5c8f22

File tree

11 files changed

+220
-2
lines changed

11 files changed

+220
-2
lines changed

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
import javascript
66
private import semmle.javascript.dataflow.InferredTypes
7+
private import semmle.javascript.dataflow.internal.FlowSteps as FlowSteps
78

89
deprecated module GlobalAccessPath {
910
/**
@@ -58,7 +59,8 @@ module AccessPath {
5859
not this instanceof DataFlow::PropRead and
5960
not this instanceof PropertyProjection and
6061
not this instanceof Closure::ClosureNamespaceAccess and
61-
not this = DataFlow::parameterNode(any(ImmediatelyInvokedFunctionExpr iife).getAParameter())
62+
not this = DataFlow::parameterNode(any(ImmediatelyInvokedFunctionExpr iife).getAParameter()) and
63+
not FlowSteps::identityFunctionStep(_, this)
6264
}
6365

6466
/** Holds if this represents the root of the global access path. */

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,8 @@ module DataFlow {
198198
result = unique(Expr ret | ret = fun.getAReturnedExpr()).flow() and
199199
not fun.getExit().isJoin() // can only reach exit by the return statement
200200
)
201+
or
202+
FlowSteps::identityFunctionStep(result, this)
201203
}
202204

203205
/**

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

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -404,6 +404,18 @@ private module CachedSteps {
404404
predicate receiverPropWrite(Function f, string prop, DataFlow::Node rhs) {
405405
DataFlow::thisNode(f).hasPropertyWrite(prop, rhs)
406406
}
407+
408+
/**
409+
* A step from `pred` to `succ` through a call to an identity function.
410+
*/
411+
cached
412+
predicate identityFunctionStep(DataFlow::Node pred, DataFlow::CallNode succ) {
413+
exists(DataFlow::GlobalVarRefNode global |
414+
global.getName() = "Object" and
415+
succ.(DataFlow::MethodCallNode).calls(global, ["freeze", "seal"]) and
416+
pred = succ.getArgument(0)
417+
)
418+
}
407419
}
408420

409421
import CachedSteps

javascript/ql/test/library-tests/DataFlow/enclosingExpr.expected

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -350,3 +350,10 @@
350350
| tst.js:115:1:115:12 | Array.call() | tst.js:115:1:115:12 | Array.call() |
351351
| tst.js:115:1:115:12 | reflective call | tst.js:115:1:115:12 | Array.call() |
352352
| tst.js:115:7:115:10 | call | tst.js:115:7:115:10 | call |
353+
| tst.js:117:5:117:6 | x2 | tst.js:117:5:117:6 | x2 |
354+
| tst.js:117:5:117:24 | x2 = Object.seal(x1) | tst.js:117:5:117:24 | x2 = Object.seal(x1) |
355+
| tst.js:117:10:117:15 | Object | tst.js:117:10:117:15 | Object |
356+
| tst.js:117:10:117:20 | Object.seal | tst.js:117:10:117:20 | Object.seal |
357+
| tst.js:117:10:117:24 | Object.seal(x1) | tst.js:117:10:117:24 | Object.seal(x1) |
358+
| tst.js:117:17:117:20 | seal | tst.js:117:17:117:20 | seal |
359+
| tst.js:117:22:117:23 | x1 | tst.js:117:22:117:23 | x1 |
Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
| arguments.js:1:2:12:1 | functio ... , 3);\\n} | arguments.js:1:1:12:2 | (functi ... 3);\\n}) |
2+
| arguments.js:2:5:2:5 | arguments | arguments.js:4:28:4:36 | arguments |
3+
| arguments.js:2:5:2:5 | arguments | arguments.js:5:25:5:33 | arguments |
4+
| arguments.js:2:5:2:5 | arguments | arguments.js:6:20:6:28 | arguments |
5+
| arguments.js:2:5:10:5 | functio ... ;\\n } | arguments.js:2:14:2:14 | f |
6+
| arguments.js:2:14:2:14 | f | arguments.js:11:5:11:5 | f |
7+
| arguments.js:2:16:2:16 | x | arguments.js:2:16:2:16 | x |
8+
| arguments.js:2:16:2:16 | x | arguments.js:3:24:3:24 | x |
9+
| arguments.js:6:13:6:28 | args | arguments.js:7:24:7:27 | args |
10+
| arguments.js:6:20:6:28 | arguments | arguments.js:6:13:6:28 | args |
11+
| arguments.js:8:9:8:22 | arguments | arguments.js:9:27:9:35 | arguments |
12+
| arguments.js:8:21:8:22 | {} | arguments.js:8:9:8:22 | arguments |
13+
| arguments.js:8:21:8:22 | {} | arguments.js:8:9:8:22 | arguments = {} |
14+
| eval.js:2:7:2:12 | x | eval.js:4:3:4:3 | x |
15+
| eval.js:2:11:2:12 | 42 | eval.js:2:7:2:12 | x |
16+
| sources.js:1:6:1:6 | x | sources.js:1:6:1:6 | x |
17+
| sources.js:1:6:1:6 | x | sources.js:1:11:1:11 | x |
18+
| sources.js:1:6:1:11 | x => x | sources.js:1:5:1:12 | (x => x) |
19+
| sources.js:1:11:1:11 | x | sources.js:1:1:1:12 | new (x => x) |
20+
| sources.js:3:2:5:1 | functio ... x+19;\\n} | sources.js:3:1:5:2 | (functi ... +19;\\n}) |
21+
| sources.js:3:11:3:11 | x | sources.js:3:11:3:11 | x |
22+
| sources.js:3:11:3:11 | x | sources.js:4:10:4:10 | x |
23+
| sources.js:4:10:4:13 | x+19 | sources.js:3:1:5:6 | (functi ... \\n})(23) |
24+
| sources.js:5:4:5:5 | 23 | sources.js:3:11:3:11 | x |
25+
| sources.js:9:14:9:18 | array | sources.js:9:14:9:18 | array |
26+
| sources.js:9:14:9:18 | array | sources.js:10:19:10:23 | array |
27+
| sources.js:9:14:9:18 | array | sources.js:11:23:11:27 | array |
28+
| sources.js:10:12:10:14 | key | sources.js:10:28:10:30 | key |
29+
| sources.js:11:12:11:18 | key | sources.js:11:32:11:34 | key |
30+
| sources.js:11:14:11:16 | key | sources.js:11:12:11:18 | key |
31+
| tst2.ts:1:1:1:1 | A | tst2.ts:1:18:1:18 | A |
32+
| tst2.ts:1:8:5:1 | A | tst2.ts:11:11:11:11 | A |
33+
| tst2.ts:1:8:5:1 | namespa ... lysed\\n} | tst2.ts:1:8:5:1 | A |
34+
| tst2.ts:2:14:2:19 | x | tst2.ts:4:3:4:3 | x |
35+
| tst2.ts:2:18:2:19 | 42 | tst2.ts:2:14:2:19 | x |
36+
| tst2.ts:7:1:7:0 | A | tst2.ts:8:3:8:3 | A |
37+
| tst2.ts:7:1:9:1 | functio ... = 23;\\n} | tst2.ts:7:10:7:13 | setX |
38+
| tst2.ts:7:10:7:13 | setX | tst2.ts:3:3:3:6 | setX |
39+
| tst2.ts:8:9:8:10 | 23 | tst2.ts:8:3:8:10 | A.x = 23 |
40+
| tst2.ts:11:11:11:13 | A.x | tst2.ts:11:11:11:23 | A.x as number |
41+
| tst2.ts:13:26:13:29 | List | tst2.ts:13:26:13:37 | List<string> |
42+
| tst2.ts:13:39:13:38 | args | tst2.ts:13:39:13:38 | args |
43+
| tst2.ts:13:39:13:38 | args | tst2.ts:13:39:13:38 | args |
44+
| tst.js:1:10:1:11 | fs | tst.js:1:10:1:11 | fs |
45+
| tst.js:1:10:1:11 | fs | tst.js:7:1:7:2 | fs |
46+
| tst.js:1:10:1:11 | fs | tst.js:22:24:22:25 | fs |
47+
| tst.js:3:5:3:10 | x | tst.js:8:1:8:1 | x |
48+
| tst.js:3:5:3:10 | x | tst.js:9:2:9:2 | x |
49+
| tst.js:3:5:3:10 | x | tst.js:10:1:10:1 | x |
50+
| tst.js:3:5:3:10 | x | tst.js:11:1:11:1 | x |
51+
| tst.js:3:5:3:10 | x | tst.js:11:1:11:1 | x |
52+
| tst.js:3:5:3:10 | x | tst.js:11:1:11:1 | x |
53+
| tst.js:3:5:3:10 | x | tst.js:12:1:12:1 | x |
54+
| tst.js:3:5:3:10 | x | tst.js:13:1:13:1 | x |
55+
| tst.js:3:9:3:10 | 42 | tst.js:3:5:3:10 | x |
56+
| tst.js:4:5:4:12 | y | tst.js:10:4:10:4 | y |
57+
| tst.js:4:5:4:12 | y | tst.js:11:6:11:6 | y |
58+
| tst.js:4:5:4:12 | y | tst.js:12:6:12:6 | y |
59+
| tst.js:4:5:4:12 | y | tst.js:13:5:13:5 | y |
60+
| tst.js:4:5:4:12 | y | tst.js:14:9:14:9 | y |
61+
| tst.js:4:5:4:12 | y | tst.js:105:6:105:6 | y |
62+
| tst.js:4:9:4:12 | "hi" | tst.js:4:5:4:12 | y |
63+
| tst.js:9:2:9:2 | x | tst.js:9:1:9:3 | (x) |
64+
| tst.js:10:4:10:4 | y | tst.js:10:1:10:4 | x, y |
65+
| tst.js:12:1:12:1 | x | tst.js:12:1:12:1 | x |
66+
| tst.js:12:1:12:1 | x | tst.js:12:1:12:1 | x |
67+
| tst.js:12:1:12:1 | x | tst.js:12:1:12:1 | x |
68+
| tst.js:13:1:13:1 | x | tst.js:14:5:14:5 | x |
69+
| tst.js:13:1:13:1 | x | tst.js:25:3:25:3 | x |
70+
| tst.js:13:1:13:5 | z | tst.js:14:1:14:1 | z |
71+
| tst.js:13:5:13:5 | y | tst.js:13:1:13:5 | z |
72+
| tst.js:13:5:13:5 | y | tst.js:13:1:13:5 | z = y |
73+
| tst.js:16:2:20:1 | functio ... n "";\\n} | tst.js:16:1:20:2 | (functi ... "";\\n}) |
74+
| tst.js:16:13:16:13 | a | tst.js:16:13:16:13 | a |
75+
| tst.js:16:13:16:13 | a | tst.js:18:12:18:12 | a |
76+
| tst.js:20:4:20:8 | "arg" | tst.js:16:13:16:13 | a |
77+
| tst.js:22:5:22:25 | readFileSync | tst.js:23:1:23:12 | readFileSync |
78+
| tst.js:22:7:22:18 | readFileSync | tst.js:22:5:22:25 | readFileSync |
79+
| tst.js:22:24:22:25 | fs | tst.js:22:5:22:20 | { readFileSync } |
80+
| tst.js:25:1:25:3 | x | tst.js:26:1:26:1 | x |
81+
| tst.js:25:1:25:3 | x | tst.js:57:7:57:7 | x |
82+
| tst.js:25:1:25:3 | x | tst.js:58:11:58:11 | x |
83+
| tst.js:25:1:25:3 | x | tst.js:105:1:105:1 | x |
84+
| tst.js:28:2:28:1 | x | tst.js:29:3:29:3 | x |
85+
| tst.js:28:2:29:3 | () =>\\n x | tst.js:28:1:30:1 | (() =>\\n ... ables\\n) |
86+
| tst.js:29:3:29:3 | x | tst.js:28:1:30:3 | (() =>\\n ... les\\n)() |
87+
| tst.js:32:1:32:0 | x | tst.js:33:10:33:10 | x |
88+
| tst.js:32:1:34:1 | functio ... ables\\n} | tst.js:32:10:32:10 | g |
89+
| tst.js:32:10:32:10 | g | tst.js:35:1:35:1 | g |
90+
| tst.js:32:10:32:10 | g | tst.js:60:1:60:1 | g |
91+
| tst.js:32:10:32:10 | g | tst.js:62:4:62:4 | g |
92+
| tst.js:37:5:42:1 | o | tst.js:43:1:43:1 | o |
93+
| tst.js:37:5:42:1 | o | tst.js:44:1:44:1 | o |
94+
| tst.js:37:5:42:1 | o | tst.js:61:3:61:3 | o |
95+
| tst.js:37:5:42:1 | o | tst.js:62:1:62:1 | o |
96+
| tst.js:37:5:42:1 | o | tst.js:77:15:77:15 | o |
97+
| tst.js:37:5:42:1 | o | tst.js:80:15:80:15 | o |
98+
| tst.js:37:5:42:1 | o | tst.js:83:23:83:23 | o |
99+
| tst.js:37:5:42:1 | o | tst.js:85:23:85:23 | o |
100+
| tst.js:37:9:42:1 | {\\n x: ... ;\\n }\\n} | tst.js:37:5:42:1 | o |
101+
| tst.js:39:4:39:3 | this | tst.js:40:5:40:8 | this |
102+
| tst.js:46:10:46:11 | "" | tst.js:46:1:46:11 | global = "" |
103+
| tst.js:49:1:54:1 | A | tst.js:55:1:55:1 | A |
104+
| tst.js:49:1:54:1 | class A ... `\\n }\\n} | tst.js:49:1:54:1 | A |
105+
| tst.js:64:1:67:1 | functio ... lysed\\n} | tst.js:64:11:64:11 | h |
106+
| tst.js:64:11:64:11 | h | tst.js:68:12:68:12 | h |
107+
| tst.js:68:5:68:14 | iter | tst.js:69:1:69:4 | iter |
108+
| tst.js:68:12:68:14 | h() | tst.js:68:5:68:14 | iter |
109+
| tst.js:77:10:77:10 | i | tst.js:78:3:78:3 | i |
110+
| tst.js:80:10:80:10 | v | tst.js:81:3:81:3 | v |
111+
| tst.js:83:18:83:18 | v | tst.js:83:26:83:26 | v |
112+
| tst.js:85:18:85:18 | v | tst.js:85:26:85:26 | v |
113+
| tst.js:87:2:92:1 | functio ... + z;\\n} | tst.js:87:1:92:2 | (functi ... + z;\\n}) |
114+
| tst.js:87:11:87:24 | o | tst.js:88:18:88:18 | o |
115+
| tst.js:87:11:87:24 | o | tst.js:90:15:90:15 | o |
116+
| tst.js:87:11:87:24 | x | tst.js:91:10:91:10 | x |
117+
| tst.js:87:13:87:16 | p: x | tst.js:87:11:87:24 | x |
118+
| tst.js:88:7:88:18 | y | tst.js:91:14:91:14 | y |
119+
| tst.js:88:9:88:12 | q: y | tst.js:88:7:88:18 | y |
120+
| tst.js:88:18:88:18 | o | tst.js:88:7:88:14 | { q: y } |
121+
| tst.js:90:4:90:15 | z | tst.js:91:18:91:18 | z |
122+
| tst.js:90:4:90:15 | { r: z } = o | tst.js:90:3:90:16 | ({ r: z } = o) |
123+
| tst.js:90:6:90:9 | r: z | tst.js:90:4:90:15 | z |
124+
| tst.js:90:15:90:15 | o | tst.js:90:4:90:11 | { r: z } |
125+
| tst.js:90:15:90:15 | o | tst.js:90:4:90:15 | { r: z } = o |
126+
| tst.js:91:10:91:18 | x + y + z | tst.js:87:1:96:2 | (functi ... r: 0\\n}) |
127+
| tst.js:92:4:96:1 | {\\n p: ... r: 0\\n} | tst.js:87:11:87:24 | { p: x, ...o } |
128+
| tst.js:98:2:103:1 | functio ... + z;\\n} | tst.js:98:1:103:2 | (functi ... + z;\\n}) |
129+
| tst.js:98:11:98:24 | rest | tst.js:99:15:99:18 | rest |
130+
| tst.js:98:11:98:24 | rest | tst.js:101:13:101:16 | rest |
131+
| tst.js:98:11:98:24 | x | tst.js:102:10:102:10 | x |
132+
| tst.js:98:13:98:13 | x | tst.js:98:11:98:24 | x |
133+
| tst.js:99:7:99:18 | y | tst.js:102:14:102:14 | y |
134+
| tst.js:99:9:99:9 | y | tst.js:99:7:99:18 | y |
135+
| tst.js:99:15:99:18 | rest | tst.js:99:7:99:11 | [ y ] |
136+
| tst.js:101:3:101:16 | z | tst.js:102:18:102:18 | z |
137+
| tst.js:101:7:101:7 | z | tst.js:101:3:101:16 | z |
138+
| tst.js:101:13:101:16 | rest | tst.js:101:3:101:9 | [ , z ] |
139+
| tst.js:101:13:101:16 | rest | tst.js:101:3:101:16 | [ , z ] = rest |
140+
| tst.js:102:10:102:18 | x + y + z | tst.js:98:1:103:17 | (functi ... 3, 0 ]) |
141+
| tst.js:103:4:103:16 | [ 19, 23, 0 ] | tst.js:98:11:98:24 | [ x, ...rest ] |
142+
| tst.js:107:2:113:1 | functio ... v2c;\\n} | tst.js:107:1:113:2 | (functi ... v2c;\\n}) |
143+
| tst.js:108:6:108:38 | v1a | tst.js:109:2:109:4 | v1a |
144+
| tst.js:108:6:108:38 | v1b | tst.js:109:8:109:10 | v1b |
145+
| tst.js:108:6:108:38 | v1c | tst.js:109:14:109:16 | v1c |
146+
| tst.js:108:7:108:9 | v1a | tst.js:108:6:108:38 | v1a |
147+
| tst.js:108:36:108:38 | o1d | tst.js:108:6:108:32 | {v1a, v ... = o1c} |
148+
| tst.js:111:6:111:38 | v2a | tst.js:112:2:112:4 | v2a |
149+
| tst.js:111:6:111:38 | v2b | tst.js:112:8:112:10 | v2b |
150+
| tst.js:111:6:111:38 | v2c | tst.js:112:14:112:16 | v2c |
151+
| tst.js:111:7:111:9 | v2a | tst.js:111:6:111:38 | v2a |
152+
| tst.js:111:36:111:38 | o2d | tst.js:111:6:111:32 | [v2a, v ... = o2c] |
153+
| tst.js:115:1:115:12 | reflective call | tst.js:115:1:115:12 | Array.call() |
154+
| tst.js:117:22:117:23 | x1 | tst.js:117:10:117:24 | Object.seal(x1) |
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
import javascript
2+
3+
from DataFlow::Node nd
4+
select nd.getImmediatePredecessor(), nd

javascript/ql/test/library-tests/DataFlow/incomplete.expected

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,3 +112,9 @@
112112
| tst.js:115:1:115:10 | Array.call | heap |
113113
| tst.js:115:1:115:12 | Array.call() | call |
114114
| tst.js:115:1:115:12 | exceptional return of Array.call() | call |
115+
| tst.js:117:10:117:15 | Object | global |
116+
| tst.js:117:10:117:20 | Object.seal | global |
117+
| tst.js:117:10:117:20 | Object.seal | heap |
118+
| tst.js:117:10:117:24 | Object.seal(x1) | call |
119+
| tst.js:117:10:117:24 | exceptional return of Object.seal(x1) | call |
120+
| tst.js:117:22:117:23 | x1 | global |

javascript/ql/test/library-tests/DataFlow/sources.expected

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,3 +130,7 @@
130130
| tst.js:115:1:115:10 | Array.call |
131131
| tst.js:115:1:115:12 | Array.call() |
132132
| tst.js:115:1:115:12 | reflective call |
133+
| tst.js:117:10:117:15 | Object |
134+
| tst.js:117:10:117:20 | Object.seal |
135+
| tst.js:117:10:117:24 | Object.seal(x1) |
136+
| tst.js:117:22:117:23 | x1 |

javascript/ql/test/library-tests/DataFlow/tst.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,3 +113,5 @@ x ?? y; // flow through short-circuiting operator
113113
});
114114

115115
Array.call() // flow from implicit call to `Array` to `Array.call`
116+
117+
var x2 = Object.seal(x1) // flow through identity function

javascript/ql/test/library-tests/GlobalAccessPaths/GlobalAccessPaths.expected

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,24 @@ test_getAReferenceTo
6565
| test.js:44:13:44:25 | Object.create | Object.create |
6666
| test.js:50:7:50:12 | random | random |
6767
| test.js:56:7:56:12 | random | random |
68+
| test.js:67:11:67:16 | Object | Object |
69+
| test.js:67:11:67:23 | Object.freeze | Object.freeze |
70+
| test.js:67:11:67:32 | Object. ... oo.bar) | foo.bar |
71+
| test.js:67:11:67:36 | Object. ... ar).baz | foo.bar.baz |
72+
| test.js:67:25:67:27 | foo | foo |
73+
| test.js:67:25:67:31 | foo.bar | foo.bar |
74+
| test.js:68:11:68:16 | Object | Object |
75+
| test.js:68:11:68:21 | Object.seal | Object.seal |
76+
| test.js:68:11:68:30 | Object.seal(foo.bar) | foo.bar |
77+
| test.js:68:11:68:34 | Object. ... ar).baz | foo.bar.baz |
78+
| test.js:68:23:68:25 | foo | foo |
79+
| test.js:68:23:68:29 | foo.bar | foo.bar |
80+
| test.js:69:6:69:15 | O | Object |
81+
| test.js:69:10:69:15 | Object | Object |
82+
| test.js:70:11:70:11 | O | Object |
83+
| test.js:70:11:70:16 | O.seal | Object.seal |
84+
| test.js:70:18:70:20 | foo | foo |
85+
| test.js:70:18:70:24 | foo.bar | foo.bar |
6886
test_getAnAssignmentTo
6987
| other_ns.js:4:9:4:16 | NS \|\| {} | NS |
7088
| other_ns.js:6:12:6:13 | {} | Conflict |

0 commit comments

Comments
 (0)