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

Skip to content

Commit 830100d

Browse files
committed
support interprocedural flow with custom load/store steps
1 parent d09bce5 commit 830100d

7 files changed

Lines changed: 115 additions & 34 deletions

File tree

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

Lines changed: 30 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -466,22 +466,22 @@ abstract class AdditionalFlowStep extends DataFlow::Node {
466466
}
467467

468468
/**
469-
* Holds if the `pred` should be stored in the object `succ` under the property `prop`.
469+
* Holds if the `pred` should be stored in the object `succ` under the property `prop`.
470470
*/
471471
cached
472472
predicate store(DataFlow::Node pred, DataFlow::Node succ, string prop) { none() }
473473

474474
/**
475-
* Holds if the property `prop` of the object `pred` should be loaded into `succ`.
475+
* Holds if the property `prop` of the object `pred` should be loaded into `succ`.
476476
*/
477477
cached
478478
predicate load(DataFlow::Node pred, DataFlow::Node succ, string prop) { none() }
479479

480480
/**
481-
* Holds if the property `prop` should be copied from the object `pred` to the object `succ`.
481+
* Holds if the property `prop` should be copied from the object `pred` to the object `succ`.
482482
*/
483483
cached
484-
predicate copyProperty(DataFlow::Node pred, DataFlow::Node succ, string prop) { none() }
484+
predicate copyProperty(DataFlow::Node pred, DataFlow::Node succ, string prop) { none() }
485485
}
486486

487487
/**
@@ -584,7 +584,7 @@ private predicate exploratoryFlowStep(
584584
basicFlowStep(pred, succ, _, cfg) or
585585
basicStoreStep(pred, succ, _) or
586586
basicLoadStep(pred, succ, _) or
587-
587+
588588
any(AdditionalFlowStep s).store(pred, succ, _) or
589589
cfg.isAdditionalStoreStep(pred, succ, _) or
590590
any(AdditionalFlowStep s).load(pred, succ, _) or
@@ -765,6 +765,16 @@ private predicate storeStep(
765765
(
766766
returnedPropWrite(f, _, prop, mid)
767767
or
768+
exists(DataFlow::SourceNode base |
769+
(
770+
any(AdditionalFlowStep step).store(mid, _, prop)
771+
or
772+
cfg.isAdditionalStoreStep(mid, _, prop)
773+
)
774+
and
775+
base.flowsToExpr(f.getAReturnedExpr())
776+
)
777+
or
768778
succ instanceof DataFlow::NewNode and
769779
receiverPropWrite(f, prop, mid)
770780
)
@@ -775,12 +785,18 @@ private predicate storeStep(
775785
* Holds if `f` may `read` property `prop` of parameter `parm`.
776786
*/
777787
private predicate parameterPropRead(
778-
Function f, DataFlow::Node invk, DataFlow::Node arg, string prop, DataFlow::PropRead read,
788+
Function f, DataFlow::Node invk, DataFlow::Node arg, string prop, DataFlow::Node read,
779789
DataFlow::Configuration cfg
780790
) {
781-
exists(DataFlow::SourceNode parm |
791+
exists(DataFlow::Node parm |
782792
callInputStep(f, invk, arg, parm, cfg) and
783-
read = parm.getAPropertyRead(prop)
793+
(
794+
read = parm.(DataFlow::SourceNode).getAPropertyRead(prop)
795+
or
796+
any(AdditionalFlowStep step).load(parm, read, prop)
797+
or
798+
cfg.isAdditionalLoadStep(parm, read, prop)
799+
)
784800
)
785801
}
786802

@@ -819,7 +835,7 @@ private predicate loadStep(
819835
cfg.isAdditionalLoadStep(pred, succ, prop) and
820836
summary = PathSummary::level()
821837
or
822-
exists(Function f, DataFlow::PropRead read |
838+
exists(Function f, DataFlow::Node read |
823839
parameterPropRead(f, succ, pred, prop, read, cfg) and
824840
reachesReturn(f, read, cfg, summary)
825841
)
@@ -866,17 +882,17 @@ private predicate flowThroughProperty(
866882

867883
/**
868884
* Holds if the property `prop` is copied from `fromNode` to `toNode` using at least 1 step.
869-
*
870-
* The recursion of this predicate has been unfolded once compared to a naive implementation in order to avoid having no constraint on `prop`.
871-
* Therefore a caller of this predicate should also test whether the `toNode` and `fromNode` are equal.
885+
*
886+
* The recursion of this predicate has been unfolded once compared to a naive implementation in order to avoid having no constraint on `prop`.
887+
* Therefore a caller of this predicate should also test whether the `toNode` and `fromNode` are equal.
872888
*/
873889
private predicate existsCopyProperty(DataFlow::Node fromNode, DataFlow::Node toNode, string prop) {
874-
exists(DataFlow::AdditionalFlowStep step, DataFlow::Node mid |
890+
exists(DataFlow::AdditionalFlowStep step, DataFlow::Node mid |
875891
step.copyProperty(fromNode, mid, prop) and
876892
(
877893
existsCopyProperty(mid, toNode, prop)
878894
or
879-
mid = toNode
895+
mid = toNode
880896
)
881897
)
882898
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
| tst.js:4:15:4:22 | "source" | tst.js:9:7:9:24 | readTaint(tainted) |
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import javascript
2+
3+
class Configuration extends TaintTracking::Configuration {
4+
Configuration() { this = "PromiseFlowTestingConfig" }
5+
6+
override predicate isSource(DataFlow::Node source) {
7+
source.getEnclosingExpr().getStringValue() = "source"
8+
}
9+
10+
override predicate isSink(DataFlow::Node sink) {
11+
any(DataFlow::InvokeNode call | call.getCalleeName() = "sink").getAnArgument() = sink
12+
}
13+
14+
// When the source code states that "foo" is being read, "bar" is additionally being read.
15+
override predicate isAdditionalLoadStep(DataFlow::Node pred, DataFlow::Node succ, string prop) {
16+
pred.(DataFlow::SourceNode).getAPropertyRead("foo") = succ and prop = "bar"
17+
}
18+
}
19+
20+
from DataFlow::Node pred, DataFlow::Node succ, Configuration cfg
21+
where cfg.hasFlow(pred, succ)
22+
select pred, succ
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
// When the source code states that "foo" is being read, "bar" is additionally being read.
2+
3+
(function () {
4+
var source = "source";
5+
var tainted = { bar: source };
6+
function readTaint(x) {
7+
return x.foo;
8+
}
9+
sink(readTaint(tainted));
10+
})();

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

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
var p4 = new Promise((resolve, reject) => reject(source));
1414
try {
1515
var foo = await p4;
16-
} catch(e) {
16+
} catch (e) {
1717
sink(e); // NOT OK!
1818
}
1919

@@ -31,19 +31,24 @@
3131

3232
new Promise((resolve, reject) => reject(source)).catch(x => sink(x)); // NOT OK!
3333

34-
Promise.resolve(source).catch(() => {}).then(a => sink(a)); // NOT OK!
34+
Promise.resolve(source).catch(() => { }).then(a => sink(a)); // NOT OK!
3535

3636
var p5 = Promise.resolve(source);
37-
var p6 = p5.catch(() => {});
37+
var p6 = p5.catch(() => { });
3838
var p7 = p6.then(a => sink(a)); // NOT OK!
3939

40-
new Promise((resolve, reject) => reject(source)).then(() => {}).catch(x => sink(x)); // NOT OK!
40+
new Promise((resolve, reject) => reject(source)).then(() => { }).catch(x => sink(x)); // NOT OK!
4141

42-
new Promise((resolve, reject) => reject(source)).then(() => {}, () => {}).catch(x => sink(x)); // OK!
42+
new Promise((resolve, reject) => reject(source)).then(() => { }, () => { }).catch(x => sink(x)); // OK!
4343

44-
Promise.resolve(source).catch(() => {}).catch(() => {}).catch(() => {}).then(a => sink(a)); // NOT OK!
44+
Promise.resolve(source).catch(() => { }).catch(() => { }).catch(() => { }).then(a => sink(a)); // NOT OK!
4545

46-
Promise.resolve(source).finally(() => {}).then(a => sink(a)); // NOT OK!
46+
Promise.resolve(source).finally(() => { }).then(a => sink(a)); // NOT OK!
4747

48-
new Promise(() => {throw source}).catch(x => sink(x)); // NOT OK!
48+
new Promise(() => { throw source }).catch(x => sink(x)); // NOT OK!
49+
50+
function createPromise(src) {
51+
return Promise.resolve(src);
52+
}
53+
createPromise(source).then(v => sink(v)); // NOT OK!
4954
})();
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
(function () {
2+
function getSource() {
3+
var source = "source"; // step 1
4+
return source; // step 2
5+
}
6+
loadScript(getSource()) // step 3
7+
.then(function () { })
8+
.then(function () { })
9+
.catch(handleError);
10+
function loadScript(src) { // step 4 (is summarized)
11+
return new Promise(function (resolve, reject) {
12+
setTimeout(function (error) {
13+
reject(new Error('Blah: ' + src)); // step 5
14+
}, 1000);
15+
});
16+
}
17+
function handleError(error) { // step 6
18+
sink(error); // step 7
19+
}
20+
})();

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

Lines changed: 19 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,15 @@ test_ResolvedPromiseDefinition
88
| flow.js:36:11:36:33 | Promise ... source) | flow.js:36:27:36:32 | source |
99
| flow.js:44:2:44:24 | Promise ... source) | flow.js:44:18:44:23 | source |
1010
| flow.js:46:2:46:24 | Promise ... source) | flow.js:46:18:46:23 | source |
11+
| flow.js:51:10:51:29 | Promise.resolve(src) | flow.js:51:26:51:28 | src |
1112
| promises.js:53:19:53:41 | Promise ... source) | promises.js:53:35:53:40 | source |
1213
| promises.js:62:19:62:41 | Promise ... source) | promises.js:62:35:62:40 | source |
1314
| promises.js:71:5:71:27 | Promise ... source) | promises.js:71:21:71:26 | source |
1415
test_PromiseDefinition_getARejectHandler
1516
| flow.js:26:2:26:49 | new Pro ... ource)) | flow.js:26:69:26:80 | y => sink(y) |
1617
| flow.js:32:2:32:49 | new Pro ... ource)) | flow.js:32:57:32:68 | x => sink(x) |
17-
| flow.js:42:2:42:49 | new Pro ... ource)) | flow.js:42:66:42:73 | () => {} |
18-
| flow.js:48:2:48:34 | new Pro ... ource}) | flow.js:48:42:48:53 | x => sink(x) |
18+
| flow.js:42:2:42:49 | new Pro ... ource)) | flow.js:42:67:42:75 | () => { } |
19+
| flow.js:48:2:48:36 | new Pro ... urce }) | flow.js:48:44:48:55 | x => sink(x) |
1920
| promises.js:10:18:17:4 | new Pro ... );\\n }) | promises.js:20:6:22:3 | (v) => ... v;\\n } |
2021
| promises.js:10:18:17:4 | new Pro ... );\\n }) | promises.js:23:18:25:3 | (v) => ... v;\\n } |
2122
| promises.js:10:18:17:4 | new Pro ... );\\n }) | promises.js:26:20:28:3 | (v) => ... v;\\n } |
@@ -28,7 +29,8 @@ test_PromiseDefinition_getExecutor
2829
| flow.js:32:2:32:49 | new Pro ... ource)) | flow.js:32:14:32:48 | (resolv ... source) |
2930
| flow.js:40:2:40:49 | new Pro ... ource)) | flow.js:40:14:40:48 | (resolv ... source) |
3031
| flow.js:42:2:42:49 | new Pro ... ource)) | flow.js:42:14:42:48 | (resolv ... source) |
31-
| flow.js:48:2:48:34 | new Pro ... ource}) | flow.js:48:14:48:33 | () => {throw source} |
32+
| flow.js:48:2:48:36 | new Pro ... urce }) | flow.js:48:14:48:35 | () => { ... ource } |
33+
| interflow.js:11:12:15:6 | new Pro ... \\n }) | interflow.js:11:24:15:5 | functio ... ;\\n } |
3234
| promises.js:3:17:5:4 | new Pro ... );\\n }) | promises.js:3:29:5:3 | functio ... e);\\n } |
3335
| promises.js:10:18:17:4 | new Pro ... );\\n }) | promises.js:10:30:17:3 | (res, r ... e);\\n } |
3436
| promises.js:33:19:35:6 | new Pro ... \\n }) | promises.js:33:31:35:5 | functio ... ;\\n } |
@@ -44,16 +46,17 @@ test_PromiseDefinition
4446
| flow.js:32:2:32:49 | new Pro ... ource)) |
4547
| flow.js:40:2:40:49 | new Pro ... ource)) |
4648
| flow.js:42:2:42:49 | new Pro ... ource)) |
47-
| flow.js:48:2:48:34 | new Pro ... ource}) |
49+
| flow.js:48:2:48:36 | new Pro ... urce }) |
50+
| interflow.js:11:12:15:6 | new Pro ... \\n }) |
4851
| promises.js:3:17:5:4 | new Pro ... );\\n }) |
4952
| promises.js:10:18:17:4 | new Pro ... );\\n }) |
5053
| promises.js:33:19:35:6 | new Pro ... \\n }) |
5154
| promises.js:43:19:45:6 | Q.Promi ... \\n }) |
5255
test_PromiseDefinition_getAResolveHandler
5356
| flow.js:24:2:24:49 | new Pro ... ource)) | flow.js:24:56:24:67 | x => sink(x) |
5457
| flow.js:26:2:26:49 | new Pro ... ource)) | flow.js:26:56:26:66 | x => foo(x) |
55-
| flow.js:40:2:40:49 | new Pro ... ource)) | flow.js:40:56:40:63 | () => {} |
56-
| flow.js:42:2:42:49 | new Pro ... ource)) | flow.js:42:56:42:63 | () => {} |
58+
| flow.js:40:2:40:49 | new Pro ... ource)) | flow.js:40:56:40:64 | () => { } |
59+
| flow.js:42:2:42:49 | new Pro ... ource)) | flow.js:42:56:42:64 | () => { } |
5760
| promises.js:3:17:5:4 | new Pro ... );\\n }) | promises.js:6:16:8:3 | functio ... al;\\n } |
5861
| promises.js:10:18:17:4 | new Pro ... );\\n }) | promises.js:18:17:20:3 | (v) => ... v;\\n } |
5962
| promises.js:10:18:17:4 | new Pro ... );\\n }) | promises.js:26:20:28:3 | (v) => ... v;\\n } |
@@ -68,6 +71,7 @@ test_PromiseDefinition_getRejectParameter
6871
| flow.js:32:2:32:49 | new Pro ... ource)) | flow.js:32:24:32:29 | reject |
6972
| flow.js:40:2:40:49 | new Pro ... ource)) | flow.js:40:24:40:29 | reject |
7073
| flow.js:42:2:42:49 | new Pro ... ource)) | flow.js:42:24:42:29 | reject |
74+
| interflow.js:11:12:15:6 | new Pro ... \\n }) | interflow.js:11:43:11:48 | reject |
7175
| promises.js:3:17:5:4 | new Pro ... );\\n }) | promises.js:3:48:3:53 | reject |
7276
| promises.js:10:18:17:4 | new Pro ... );\\n }) | promises.js:10:36:10:38 | rej |
7377
| promises.js:33:19:35:6 | new Pro ... \\n }) | promises.js:33:50:33:55 | reject |
@@ -81,13 +85,14 @@ test_PromiseDefinition_getResolveParameter
8185
| flow.js:32:2:32:49 | new Pro ... ource)) | flow.js:32:15:32:21 | resolve |
8286
| flow.js:40:2:40:49 | new Pro ... ource)) | flow.js:40:15:40:21 | resolve |
8387
| flow.js:42:2:42:49 | new Pro ... ource)) | flow.js:42:15:42:21 | resolve |
88+
| interflow.js:11:12:15:6 | new Pro ... \\n }) | interflow.js:11:34:11:40 | resolve |
8489
| promises.js:3:17:5:4 | new Pro ... );\\n }) | promises.js:3:39:3:45 | resolve |
8590
| promises.js:10:18:17:4 | new Pro ... );\\n }) | promises.js:10:31:10:33 | res |
8691
| promises.js:33:19:35:6 | new Pro ... \\n }) | promises.js:33:41:33:47 | resolve |
8792
| promises.js:43:19:45:6 | Q.Promi ... \\n }) | promises.js:43:39:43:45 | resolve |
8893
test_PromiseDefinition_getACatchHandler
8994
| flow.js:32:2:32:49 | new Pro ... ource)) | flow.js:32:57:32:68 | x => sink(x) |
90-
| flow.js:48:2:48:34 | new Pro ... ource}) | flow.js:48:42:48:53 | x => sink(x) |
95+
| flow.js:48:2:48:36 | new Pro ... urce }) | flow.js:48:44:48:55 | x => sink(x) |
9196
| promises.js:10:18:17:4 | new Pro ... );\\n }) | promises.js:23:18:25:3 | (v) => ... v;\\n } |
9297
flow
9398
| flow.js:2:15:2:22 | "source" | flow.js:5:7:5:14 | await p1 |
@@ -97,9 +102,11 @@ flow
97102
| flow.js:2:15:2:22 | "source" | flow.js:26:79:26:79 | y |
98103
| flow.js:2:15:2:22 | "source" | flow.js:28:58:28:58 | z |
99104
| flow.js:2:15:2:22 | "source" | flow.js:32:67:32:67 | x |
100-
| flow.js:2:15:2:22 | "source" | flow.js:34:57:34:57 | a |
105+
| flow.js:2:15:2:22 | "source" | flow.js:34:58:34:58 | a |
101106
| flow.js:2:15:2:22 | "source" | flow.js:38:29:38:29 | a |
102-
| flow.js:2:15:2:22 | "source" | flow.js:40:82:40:82 | x |
103-
| flow.js:2:15:2:22 | "source" | flow.js:44:89:44:89 | a |
104-
| flow.js:2:15:2:22 | "source" | flow.js:46:59:46:59 | a |
105-
| flow.js:2:15:2:22 | "source" | flow.js:48:52:48:52 | x |
107+
| flow.js:2:15:2:22 | "source" | flow.js:40:83:40:83 | x |
108+
| flow.js:2:15:2:22 | "source" | flow.js:44:92:44:92 | a |
109+
| flow.js:2:15:2:22 | "source" | flow.js:46:60:46:60 | a |
110+
| flow.js:2:15:2:22 | "source" | flow.js:48:54:48:54 | x |
111+
| flow.js:2:15:2:22 | "source" | flow.js:53:39:53:39 | v |
112+
| interflow.js:3:18:3:25 | "source" | interflow.js:18:10:18:14 | error |

0 commit comments

Comments
 (0)