Skip to content

Commit b74df66

Browse files
committed
implement Immutable merge
1 parent c0de6a3 commit b74df66

File tree

3 files changed

+26
-5
lines changed

3 files changed

+26
-5
lines changed

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

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,12 +36,13 @@ private module Immutable {
3636
* This predicate keeps track of which values in the program are Immutable collections.
3737
*/
3838
API::Node immutableCollection() {
39-
// keep this predicate in sync with the constructors defined in `storeStep`.
40-
result = immutableImport().getMember(["Map", "OrderedMap", "List", "fromJS"]).getReturn()
39+
// keep this predicate in sync with the constructors defined in `storeStep`/`step`.
40+
result =
41+
immutableImport().getMember(["Map", "OrderedMap", "List", "fromJS", "merge"]).getReturn()
4142
or
4243
result = immutableImport().getMember("Record").getReturn().getReturn()
4344
or
44-
result = immutableCollection().getMember(["set", "map", "filter", "push"]).getReturn()
45+
result = immutableCollection().getMember(["set", "map", "filter", "push", "merge"]).getReturn()
4546
}
4647

4748
/**
@@ -125,6 +126,18 @@ private module Immutable {
125126
pred = call.getReceiver() and
126127
result = call
127128
)
129+
or
130+
// Immutable.merge(x, y)
131+
exists(DataFlow::CallNode call | call = immutableImport().getMember("merge").getACall() |
132+
pred = call.getAnArgument() and
133+
result = call
134+
)
135+
or
136+
// collection.merge(other)
137+
exists(DataFlow::CallNode call | call = immutableCollection().getMember("merge").getACall() |
138+
pred = [call.getAnArgument(), call.getReceiver()] and
139+
result = call
140+
)
128141
}
129142

130143
/**

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

Lines changed: 8 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, List, OrderedMap, Record } = require('immutable');
4+
const { Map, fromJS, List, OrderedMap, Record, merge } = require('immutable');
55

66
const map1 = Map(obj);
77

@@ -39,4 +39,10 @@ sink(map4.get("f")); // NOT OK
3939
const map5 = Record({a: source(), b: null, c: null})({b: source()});
4040
sink(map5.get("a")); // NOT OK
4141
sink(map5.get("b")); // NOT OK
42-
sink(map5.get("c")); // OK
42+
sink(map5.get("c")); // OK
43+
44+
const map6 = merge(Map({}), Record({a: source()})());
45+
sink(map6.get("a")); // NOT OK
46+
47+
const map7 = map6.merge(Map({b: source()}));
48+
sink(map7.get("b")); // NOT OK

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,3 +13,5 @@
1313
| immutable.js:36:38:36:45 | source() | immutable.js:37:6:37:18 | map4.get("f") |
1414
| immutable.js:39:25:39:32 | source() | immutable.js:40:6:40:18 | map5.get("a") |
1515
| immutable.js:39:58:39:65 | source() | immutable.js:41:6:41:18 | map5.get("b") |
16+
| immutable.js:44:40:44:47 | source() | immutable.js:45:6:45:18 | map6.get("a") |
17+
| immutable.js:47:33:47:40 | source() | immutable.js:48:6:48:18 | map7.get("b") |

0 commit comments

Comments
 (0)