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

Skip to content

Commit 2e7bf9b

Browse files
committed
implement Immutable lists
1 parent a5c9492 commit 2e7bf9b

3 files changed

Lines changed: 51 additions & 8 deletions

File tree

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

Lines changed: 34 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ import javascript
66

77
/**
88
* Provides classes implementing data-flow for Immutable.
9+
*
10+
* The implemention rely on the flowsteps implemented in `Collections.qll`.
911
*/
1012
private module Immutable {
1113
/**
@@ -32,27 +34,49 @@ private module Immutable {
3234
* An instance of any immutable collection.
3335
*/
3436
API::Node immutableCollection() {
35-
result = immutableImport().getMember(["Map", "fromJS"]).getReturn()
37+
// keep this list in sync with the constructors defined in `storeStep`.
38+
result = immutableImport().getMember(["Map", "List", "fromJS"]).getReturn()
3639
or
37-
result.getAnImmediateUse() = step(immutableCollection().getAUse())
40+
result = immutableCollection().getMember(["set", "map", "filter", "push"]).getReturn()
3841
}
3942

4043
/**
4144
* Gets the immutable collection where `pred` has been stored using the name `prop`.
4245
*/
4346
DataFlow::SourceNode storeStep(DataFlow::Node pred, string prop) {
47+
// Immutable.Map() and Immutable.fromJS().
4448
exists(DataFlow::CallNode call |
4549
call = immutableImport().getMember(["Map", "fromJS"]).getACall()
4650
|
4751
pred = call.getOptionArgument(0, prop) and
4852
result = call
4953
)
5054
or
55+
// Immutable.List()
56+
exists(DataFlow::CallNode call, DataFlow::ArrayCreationNode arr |
57+
call = immutableImport().getMember("List").getACall()
58+
|
59+
arr = call.getArgument(0).getALocalSource() and
60+
exists(int i |
61+
prop = DataFlow::PseudoProperties::arrayElement(i) and
62+
pred = arr.getElement(i) and
63+
result = call
64+
)
65+
)
66+
or
67+
// collection.set(key, value)
5168
exists(DataFlow::CallNode call | call = immutableCollection().getMember("set").getACall() |
5269
call.getArgument(0).mayHaveStringValue(prop) and
5370
pred = call.getArgument(1) and
5471
result = call
5572
)
73+
or
74+
// list.push(x)
75+
exists(DataFlow::CallNode call | call = immutableCollection().getMember("push").getACall() |
76+
pred = call.getArgument(0) and
77+
result = call and
78+
prop = DataFlow::PseudoProperties::arrayElement()
79+
)
5680
}
5781

5882
/**
@@ -73,14 +97,18 @@ private module Immutable {
7397
* Gets an immutable collection that contains all the elements from `pred`.
7498
*/
7599
DataFlow::SourceNode step(DataFlow::Node pred) {
76-
// map.set() copies all existing values
77-
exists(DataFlow::CallNode call | call = immutableCollection().getMember("set").getACall() |
100+
// map.set() / list.push() copies all existing values
101+
exists(DataFlow::CallNode call |
102+
call = immutableCollection().getMember(["set", "push"]).getACall()
103+
|
78104
pred = call.getReceiver() and
79105
result = call
80106
)
81107
or
82-
// toJS() or any immutable collection converts it to a plain JavaScript object/array (and vice versa for `fromJS`).
83-
exists(DataFlow::CallNode call | call = immutableCollection().getMember("toJS").getACall() |
108+
// toJS()/toList() on any immutable collection converts it to a plain JavaScript object/array (and vice versa for `fromJS`).
109+
exists(DataFlow::CallNode call |
110+
call = immutableCollection().getMember(["toJS", "toList"]).getACall()
111+
|
84112
pred = call.getReceiver() and
85113
result = call
86114
)

javascript/ql/test/library-tests/frameworks/Immutable/immutable.js

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
var obj = { a: source("a"), b: source("b1") };
22
sink(obj["a"]); // NOT OK
33

4-
const { Map, fromJS } = require('immutable');
4+
const { Map, fromJS, List } = require('immutable');
55

66
const map1 = Map(obj);
77

@@ -19,4 +19,15 @@ sink(map3.get("d")); // NOT OK
1919

2020
sink(map3.toJS()["a"]); // NOT OK
2121

22-
sink(fromJS({"e": source("e")}).get("e")); // NOT OK
22+
sink(fromJS({"e": source("e")}).get("e")); // NOT OK
23+
24+
const l1 = List([source(), "foobar"]);
25+
l1.forEach(x => sink(x)); // NOT OK
26+
27+
l1.map(x => "safe").forEach(x => sink(x)); // OK
28+
29+
List(["safe"]).map(x => source()).forEach(x => sink(x)); // NOT OK
30+
31+
List([source()]).map(x => x).filter(x => true).toList().forEach(x => sink(x)); // NOT OK
32+
33+
List(["safe"]).push(source()).forEach(x => sink(x)); // NOT OK

javascript/ql/test/library-tests/frameworks/Immutable/tests.expected

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,7 @@
66
| immutable.js:1:32:1:43 | source("b1") | immutable.js:13:6:13:18 | map2.get("b") |
77
| immutable.js:15:28:15:38 | source("d") | immutable.js:17:6:17:18 | map3.get("d") |
88
| immutable.js:22:19:22:29 | source("e") | immutable.js:22:6:22:40 | fromJS( ... et("e") |
9+
| immutable.js:24:18:24:25 | source() | immutable.js:25:22:25:22 | x |
10+
| immutable.js:29:25:29:32 | source() | immutable.js:29:53:29:53 | x |
11+
| immutable.js:31:7:31:14 | source() | immutable.js:31:75:31:75 | x |
12+
| immutable.js:33:21:33:28 | source() | immutable.js:33:49:33:49 | x |

0 commit comments

Comments
 (0)