Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 0edb46c

Browse files
committed
improve precision for load/store steps with async functions
1 parent 26ef2f3 commit 0edb46c

7 files changed

Lines changed: 108 additions & 1 deletion

File tree

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

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1034,6 +1034,25 @@ private predicate storeStep(
10341034
receiverPropWrite(f, prop, mid)
10351035
)
10361036
)
1037+
or
1038+
// store in an immediately awaited function call
1039+
exists(Function f, DataFlow::Node mid | f.isAsync() |
1040+
// `f` stores its parameter `pred` in property `prop` of a value that flows back to the caller,
1041+
// and `succ` is an invocation of `f`
1042+
exists(AwaitExpr await, DataFlow::Node operand |
1043+
operand = await.getOperand().getUnderlyingValue().flow() and
1044+
succ.asExpr() = await
1045+
|
1046+
reachableFromInput(f, operand, pred, mid, cfg, summary) and
1047+
(
1048+
returnedPropWrite(f, _, prop, mid)
1049+
or
1050+
exists(DataFlow::SourceNode base | base.flowsToExpr(f.getAReturnedExpr()) |
1051+
isAdditionalStoreStep(mid, base, prop, cfg)
1052+
)
1053+
)
1054+
)
1055+
)
10371056
}
10381057

10391058
/**
@@ -1132,6 +1151,17 @@ private predicate loadStep(
11321151
parameterPropRead(f, succ, pred, prop, read, cfg) and
11331152
reachesReturn(f, read, cfg, summary)
11341153
)
1154+
or
1155+
// load from an immediately awaited function call
1156+
exists(Function f, DataFlow::Node read | f.isAsync() |
1157+
exists(AwaitExpr await, DataFlow::Node operand |
1158+
operand = await.getOperand().getUnderlyingValue().flow() and
1159+
succ.asExpr() = await
1160+
|
1161+
parameterPropRead(f, operand, pred, prop, read, cfg) and
1162+
reachesReturn(f, read, cfg, summary)
1163+
)
1164+
)
11351165
}
11361166

11371167
/**

javascript/ql/test/library-tests/InterProceduralFlow/GermanFlow.expected

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@
77
| async.js:2:16:2:23 | "source" | async.js:36:17:36:17 | e |
88
| async.js:2:16:2:23 | "source" | async.js:41:17:41:17 | e |
99
| async.js:2:16:2:23 | "source" | async.js:54:17:54:36 | unpack(pack(source)) |
10+
| async.js:79:16:79:23 | "source" | async.js:80:14:80:36 | (await ... ce))).p |
11+
| async.js:79:16:79:23 | "source" | async.js:92:15:92:30 | await (getP(o3)) |
12+
| async.js:96:18:96:25 | "source" | async.js:101:15:101:27 | await readP() |
1013
| callback.js:16:14:16:21 | "source" | callback.js:13:14:13:14 | x |
1114
| callback.js:17:15:17:23 | "source2" | callback.js:13:14:13:14 | x |
1215
| callback.js:27:15:27:23 | "source3" | callback.js:13:14:13:14 | x |

javascript/ql/test/library-tests/InterProceduralFlow/TaintTracking.expected

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@
99
| async.js:2:16:2:23 | "source" | async.js:36:17:36:17 | e |
1010
| async.js:2:16:2:23 | "source" | async.js:41:17:41:17 | e |
1111
| async.js:2:16:2:23 | "source" | async.js:54:17:54:36 | unpack(pack(source)) |
12+
| async.js:79:16:79:23 | "source" | async.js:80:14:80:36 | (await ... ce))).p |
13+
| async.js:79:16:79:23 | "source" | async.js:92:15:92:30 | await (getP(o3)) |
14+
| async.js:96:18:96:25 | "source" | async.js:101:15:101:27 | await readP() |
1215
| callback.js:16:14:16:21 | "source" | callback.js:13:14:13:14 | x |
1316
| callback.js:17:15:17:23 | "source2" | callback.js:13:14:13:14 | x |
1417
| callback.js:27:15:27:23 | "source3" | callback.js:13:14:13:14 | x |

javascript/ql/test/library-tests/InterProceduralFlow/TrackedNodes.expected

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,15 @@
66
| missing | async.js:2:16:2:23 | "source" | async.js:26:12:26:12 | e |
77
| missing | async.js:2:16:2:23 | "source" | async.js:26:12:26:12 | e |
88
| missing | async.js:2:16:2:23 | "source" | async.js:27:17:27:17 | e |
9+
| missing | async.js:73:22:73:22 | x | async.js:80:14:80:36 | (await ... ce))).p |
10+
| missing | async.js:74:12:76:5 | {\\n p: x\\n } | async.js:80:14:80:34 | (await ... urce))) |
11+
| missing | async.js:74:12:76:5 | {\\n p: x\\n } | async.js:80:15:80:33 | await (foo(source)) |
12+
| missing | async.js:79:16:79:23 | "source" | async.js:80:14:80:36 | (await ... ce))).p |
13+
| missing | async.js:79:16:79:23 | "source" | async.js:92:15:92:30 | await (getP(o3)) |
14+
| missing | async.js:84:12:84:17 | base.p | async.js:92:15:92:30 | await (getP(o3)) |
15+
| missing | async.js:88:12:88:17 | base.q | async.js:93:15:93:30 | await (getQ(o3)) |
16+
| missing | async.js:96:18:96:25 | "source" | async.js:101:15:101:27 | await readP() |
17+
| missing | async.js:98:12:98:16 | obj.x | async.js:101:15:101:27 | await readP() |
918
| missing | callback.js:17:15:17:23 | "source2" | callback.js:8:16:8:20 | xs[i] |
1019
| missing | callback.js:17:15:17:23 | "source2" | callback.js:12:16:12:16 | x |
1120
| missing | callback.js:17:15:17:23 | "source2" | callback.js:12:16:12:16 | x |

javascript/ql/test/library-tests/InterProceduralFlow/async.js

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,3 +69,34 @@
6969
}
7070
})();
7171

72+
async function props() {
73+
async function foo(x) {
74+
return {
75+
p: x
76+
};
77+
}
78+
79+
let source = "source";
80+
let sink = (await (foo(source))).p; // NOT OK - this requires the immidiatly awaited storeStep.
81+
let sink2 = foo("not a source").p;
82+
83+
async function getP(base) {
84+
return base.p;
85+
}
86+
87+
async function getQ(base) {
88+
return base.q;
89+
}
90+
91+
let o3 = { p: source };
92+
let sink6 = await (getP(o3)); // NOT OK - this requires the immidiatly awaited loadStep
93+
let sink7 = await (getQ(o3));
94+
95+
async function readP() {
96+
let source = "source";
97+
var obj = {x: source};
98+
return obj.x;
99+
}
100+
101+
let sink8 = await readP(); // NOT OK
102+
}

javascript/ql/test/library-tests/Promises/flow.js

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -129,4 +129,29 @@
129129
new Promise((resolve, reject) => resolve(resolved)).then(x => sink(x)); // NOT OK
130130

131131
Promise.resolve(resolved).then(x => sink(x)); // NOT OK
132-
})();
132+
})();
133+
134+
135+
(async function () {
136+
var source = "source";
137+
138+
async function async() {
139+
return source;
140+
}
141+
sink(async()); // OK - wrapped in a promise. (NOT OK for taint-tracking configs)
142+
sink(await async()); // NOT OK
143+
144+
async function throwsAsync() {
145+
throw source;
146+
}
147+
try {
148+
throwsAsync();
149+
} catch (e) {
150+
sink(e); // OK - throwsAsync just returns a promise.
151+
}
152+
try {
153+
await throwsAsync();
154+
} catch (e) {
155+
sink(e); // NOT OK
156+
}
157+
})();

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -222,6 +222,7 @@ flow
222222
| flow.js:2:15:2:22 | "source" | flow.js:79:20:79:20 | x |
223223
| flow.js:2:15:2:22 | "source" | flow.js:84:21:84:21 | e |
224224
| flow.js:2:15:2:22 | "source" | flow.js:89:45:89:45 | e |
225+
| flow.js:2:15:2:22 | "source" | flow.js:101:7:101:9 | foo |
225226
| flow.js:2:15:2:22 | "source" | flow.js:103:93:103:93 | x |
226227
| flow.js:2:15:2:22 | "source" | flow.js:105:95:105:95 | x |
227228
| flow.js:2:15:2:22 | "source" | flow.js:109:89:109:89 | x |
@@ -231,8 +232,11 @@ flow
231232
| flow.js:2:15:2:22 | "source" | flow.js:125:59:125:59 | x |
232233
| flow.js:2:15:2:22 | "source" | flow.js:129:69:129:69 | x |
233234
| flow.js:2:15:2:22 | "source" | flow.js:131:43:131:43 | x |
235+
| flow.js:136:15:136:22 | "source" | flow.js:142:7:142:19 | await async() |
236+
| flow.js:136:15:136:22 | "source" | flow.js:155:9:155:9 | e |
234237
exclusiveTaintFlow
235238
| flow2.js:2:15:2:22 | "source" | flow2.js:20:7:20:14 | tainted3 |
239+
| flow.js:136:15:136:22 | "source" | flow.js:141:7:141:13 | async() |
236240
| interflow.js:3:18:3:25 | "source" | interflow.js:18:10:18:14 | error |
237241
typetrack
238242
| flow2.js:4:2:4:31 | Promise ... lean"]) | flow2.js:4:14:4:30 | [source, "clean"] | copy $PromiseResolveField$ |
@@ -370,6 +374,8 @@ typetrack
370374
| flow.js:131:2:131:45 | Promise ... ink(x)) | flow.js:131:38:131:44 | sink(x) | copy $PromiseResolveField$ |
371375
| flow.js:131:2:131:45 | Promise ... ink(x)) | flow.js:131:38:131:44 | sink(x) | store $PromiseResolveField$ |
372376
| flow.js:131:33:131:33 | x | flow.js:131:2:131:26 | Promise ... solved) | load $PromiseResolveField$ |
377+
| flow.js:142:7:142:19 | await async() | flow.js:142:13:142:19 | async() | load $PromiseResolveField$ |
378+
| flow.js:153:4:153:22 | await throwsAsync() | flow.js:153:10:153:22 | throwsAsync() | load $PromiseResolveField$ |
373379
| interflow.js:6:3:9:23 | loadScr ... eError) | interflow.js:6:3:8:26 | loadScr ... () { }) | copy $PromiseResolveField$ |
374380
| promises.js:33:19:35:6 | new Pro ... \\n }) | promises.js:34:17:34:22 | source | copy $PromiseResolveField$ |
375381
| promises.js:33:19:35:6 | new Pro ... \\n }) | promises.js:34:17:34:22 | source | store $PromiseResolveField$ |

0 commit comments

Comments
 (0)