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

Skip to content

Commit b77dd54

Browse files
committed
implement basic map get/set for immutable.js
1 parent 26288ad commit b77dd54

6 files changed

Lines changed: 146 additions & 1 deletion

File tree

javascript/ql/src/javascript.qll

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@ import semmle.javascript.frameworks.Electron
8585
import semmle.javascript.frameworks.EventEmitter
8686
import semmle.javascript.frameworks.Files
8787
import semmle.javascript.frameworks.Firebase
88+
import semmle.javascript.frameworks.Immutable
8889
import semmle.javascript.frameworks.jQuery
8990
import semmle.javascript.frameworks.JWT
9091
import semmle.javascript.frameworks.Handlebars

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

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -659,9 +659,15 @@ module PseudoProperties {
659659
*/
660660
pragma[inline]
661661
string mapValueKnownKey(DataFlow::Node key) {
662-
result = pseudoProperty("mapValue", any(string s | key.mayHaveStringValue(s)))
662+
result = mapValueKey(any(string s | key.mayHaveStringValue(s)))
663663
}
664664

665+
/**
666+
* Gets a pseudo-property for the location of a map value where the key is `key`.
667+
*/
668+
bindingset[key]
669+
string mapValueKey(string key) { result = pseudoProperty("mapValue", key) }
670+
665671
/**
666672
* Gets a pseudo-property for the location of a map value where the key is `key`.
667673
*/
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
/**
2+
* Provides classes and predicates for reasoning about [immutable](https://www.npmjs.com/package/immutable).
3+
*/
4+
5+
import javascript
6+
7+
/**
8+
* Provides classes implementing data-flow for Immutable.
9+
*/
10+
private module Immutable {
11+
private import DataFlow::PseudoProperties
12+
13+
/**
14+
* An API entrypoint for the global `Immutable` variable.
15+
*/
16+
private class ImmutableGlobalEntry extends API::EntryPoint {
17+
ImmutableGlobalEntry() { this = "ImmutableGlobalEntry" }
18+
19+
override DataFlow::SourceNode getAUse() { result = DataFlow::globalVarRef("Immutable") }
20+
21+
override DataFlow::Node getARhs() { none() }
22+
}
23+
24+
/**
25+
* An import of the `Immutable` library.
26+
*/
27+
API::Node immutableImport() {
28+
result = API::moduleImport("immutable")
29+
or
30+
result = API::root().getASuccessor(any(ImmutableGlobalEntry i))
31+
}
32+
33+
/**
34+
* An instance of an immutable map.
35+
*/
36+
API::Node immutableMap() {
37+
result = immutableImport().getMember("Map").getReturn()
38+
or
39+
result = immutableMap().getMember("set").getReturn()
40+
}
41+
42+
/**
43+
* Gets the immutable collection where `pred` has been stored using the pseudoproperty `prop`.
44+
*/
45+
DataFlow::SourceNode storeStep(DataFlow::Node pred, string prop) {
46+
exists(DataFlow::CallNode call, string key |
47+
call = immutableImport().getMember("Map").getACall()
48+
|
49+
prop = mapValueKey(key) and
50+
pred = call.getOptionArgument(0, key) and
51+
result = call
52+
)
53+
// TODO: map set.
54+
}
55+
56+
/**
57+
* Gets the value that was stored in the immutable collection `pred` under the pseudoproperty `prop`.
58+
*/
59+
DataFlow::Node loadStep(DataFlow::Node pred, string prop) {
60+
// map.get()
61+
exists(DataFlow::MethodCallNode call | call = immutableMap().getMember("get").getACall() |
62+
pred = call.getReceiver() and
63+
result = call and
64+
prop = mapValue(call.getArgument(0))
65+
)
66+
}
67+
68+
/**
69+
* Gets an immutable collection that contains all the elements from `pred`.
70+
*/
71+
DataFlow::Node step(DataFlow::Node pred) {
72+
// map.set() copies all existing values
73+
exists(DataFlow::CallNode call | call = immutableMap().getMember("set").getACall() |
74+
pred = call.getReceiver() and
75+
result = call
76+
)
77+
}
78+
79+
/**
80+
* A dataflow step for an immutable collection.
81+
*/
82+
class ImmutableConstructionStep extends DataFlow::AdditionalFlowStep {
83+
ImmutableConstructionStep() { this = [loadStep(_, _), storeStep(_, _), step(_)] }
84+
85+
override predicate loadStep(DataFlow::Node pred, DataFlow::Node succ, string prop) {
86+
this = loadStep(pred, prop) and
87+
succ = this
88+
}
89+
90+
override predicate storeStep(DataFlow::Node pred, DataFlow::SourceNode succ, string prop) {
91+
this = storeStep(pred, prop) and
92+
succ = this
93+
}
94+
95+
override predicate step(DataFlow::Node pred, DataFlow::Node succ) {
96+
this = step(pred) and
97+
succ = this
98+
}
99+
}
100+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
var obj = { a: source("a"), b: source("b1") };
2+
sink(obj["a"]); // NOT OK
3+
4+
const { Map } = require('immutable');
5+
6+
const map1 = Map(obj);
7+
8+
sink(map1.get("b")); // NOT OK
9+
10+
const map2 = map1.set('c', "safe");
11+
sink(map1.get("a")); // NOT OK
12+
sink(map2.get("a")); // NOT OK
13+
sink(map2.get("b")); // OK - but still flagged [INCONSISTENCY]
14+
15+
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
| immutable.js:1:16:1:26 | source("a") | immutable.js:2:6:2:13 | obj["a"] |
2+
| immutable.js:1:16:1:26 | source("a") | immutable.js:11:6:11:18 | map1.get("a") |
3+
| immutable.js:1:16:1:26 | source("a") | immutable.js:12:6:12:18 | map2.get("a") |
4+
| immutable.js:1:32:1:43 | source("b1") | immutable.js:8:6:8:18 | map1.get("b") |
5+
| immutable.js:1:32:1:43 | source("b1") | immutable.js:13:6:13:18 | map2.get("b") |
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import javascript
2+
private import semmle.javascript.dataflow.internal.StepSummary
3+
4+
class Config extends DataFlow::Configuration {
5+
Config() { this = "Config" }
6+
7+
override predicate isSource(DataFlow::Node source) {
8+
source.(DataFlow::CallNode).getCalleeName() = "source"
9+
}
10+
11+
override predicate isSink(DataFlow::Node sink) {
12+
exists(DataFlow::CallNode call | call.getCalleeName() = "sink" | call.getAnArgument() = sink)
13+
}
14+
}
15+
16+
query predicate dataFlow(DataFlow::Node pred, DataFlow::Node succ) {
17+
any(Config c).hasFlow(pred, succ)
18+
}

0 commit comments

Comments
 (0)