Skip to content

Commit 5984014

Browse files
committed
introduce a PseudoProperty type in Collections.qll
1 parent b2b009c commit 5984014

File tree

1 file changed

+45
-33
lines changed

1 file changed

+45
-33
lines changed

javascript/ql/src/semmle/javascript/Collections.qll

Lines changed: 45 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,22 @@ import javascript
88
private import semmle.javascript.dataflow.internal.StepSummary
99
private import DataFlow::PseudoProperties
1010

11+
/**
12+
* A pseudo-property used in a data-flow/type-tracking step for collections.
13+
*
14+
* By extending `TypeTrackingPseudoProperty` the class enables the use of the collection related pseudo-properties in type-tracking predicates.
15+
*/
16+
private class PseudoProperty extends TypeTrackingPseudoProperty {
17+
PseudoProperty() {
18+
this = [arrayLikeElement(), "1"] or // the "1" is required for the `ForOfStep`.
19+
this = any(CollectionDataFlow::MapSet step).getAPseudoProperty()
20+
}
21+
22+
override PseudoProperty getLoadStoreToProp() {
23+
exists(CollectionFlowStep step | step.loadStore(_, _, this, result))
24+
}
25+
}
26+
1127
/**
1228
* An `AdditionalFlowStep` used to model a data-flow step related to standard library collections.
1329
*
@@ -27,7 +43,7 @@ abstract private class CollectionFlowStep extends DataFlow::AdditionalFlowStep {
2743
/**
2844
* Holds if the property `prop` of the object `pred` should be loaded into `succ`.
2945
*/
30-
predicate load(DataFlow::Node pred, DataFlow::Node succ, string prop) { none() }
46+
predicate load(DataFlow::Node pred, DataFlow::Node succ, PseudoProperty prop) { none() }
3147

3248
final override predicate loadStep(DataFlow::Node pred, DataFlow::Node succ, string prop) {
3349
this.load(pred, succ, prop)
@@ -36,7 +52,7 @@ abstract private class CollectionFlowStep extends DataFlow::AdditionalFlowStep {
3652
/**
3753
* Holds if `pred` should be stored in the object `succ` under the property `prop`.
3854
*/
39-
predicate store(DataFlow::Node pred, DataFlow::Node succ, string prop) { none() }
55+
predicate store(DataFlow::Node pred, DataFlow::Node succ, PseudoProperty prop) { none() }
4056

4157
final override predicate storeStep(DataFlow::Node pred, DataFlow::Node succ, string prop) {
4258
this.store(pred, succ, prop)
@@ -45,7 +61,7 @@ abstract private class CollectionFlowStep extends DataFlow::AdditionalFlowStep {
4561
/**
4662
* Holds if the property `prop` should be copied from the object `pred` to the object `succ`.
4763
*/
48-
predicate loadStore(DataFlow::Node pred, DataFlow::Node succ, string prop) { none() }
64+
predicate loadStore(DataFlow::Node pred, DataFlow::Node succ, PseudoProperty prop) { none() }
4965

5066
final override predicate loadStoreStep(DataFlow::Node pred, DataFlow::Node succ, string prop) {
5167
this.loadStore(pred, succ, prop, prop)
@@ -54,7 +70,9 @@ abstract private class CollectionFlowStep extends DataFlow::AdditionalFlowStep {
5470
/**
5571
* Holds if the property `loadProp` should be copied from the object `pred` to the property `storeProp` of object `succ`.
5672
*/
57-
predicate loadStore(DataFlow::Node pred, DataFlow::Node succ, string loadProp, string storeProp) {
73+
predicate loadStore(
74+
DataFlow::Node pred, DataFlow::Node succ, PseudoProperty loadProp, PseudoProperty storeProp
75+
) {
5876
none()
5977
}
6078

@@ -79,7 +97,7 @@ module CollectionsTypeTracking {
7997
*/
8098
pragma[inline]
8199
DataFlow::SourceNode collectionStep(DataFlow::Node pred, StepSummary summary) {
82-
exists(CollectionFlowStep step, string field |
100+
exists(CollectionFlowStep step, PseudoProperty field |
83101
summary = LoadStep(field) and
84102
step.load(pred, result, field) and
85103
not (
@@ -93,7 +111,7 @@ module CollectionsTypeTracking {
93111
summary = CopyStep(field) and
94112
step.loadStore(pred, result, field)
95113
or
96-
exists(string toField | summary = LoadStoreStep(field, toField) |
114+
exists(PseudoProperty toField | summary = LoadStoreStep(field, toField) |
97115
step.loadStore(pred, result, field, toField)
98116
)
99117
)
@@ -110,20 +128,6 @@ module CollectionsTypeTracking {
110128
result = collectionStep(mid, summary)
111129
)
112130
}
113-
114-
/**
115-
* A class enabling the use of the collection related pseudo-properties in type-tracking predicates.
116-
*/
117-
private class MapRelatedPseudoFieldAsTypeTrackingProperty extends TypeTrackingPseudoProperty {
118-
MapRelatedPseudoFieldAsTypeTrackingProperty() {
119-
this = [setElement(), iteratorElement()] or
120-
any(CollectionFlowStep step).store(_, _, this)
121-
}
122-
123-
override string getLoadStoreToProp() {
124-
exists(CollectionFlowStep step | step.loadStore(_, _, this, result))
125-
}
126-
}
127131
}
128132

129133
/**
@@ -136,7 +140,7 @@ private module CollectionDataFlow {
136140
private class SetAdd extends CollectionFlowStep, DataFlow::MethodCallNode {
137141
SetAdd() { this.getMethodName() = "add" }
138142

139-
override predicate store(DataFlow::Node element, DataFlow::Node obj, string prop) {
143+
override predicate store(DataFlow::Node element, DataFlow::Node obj, PseudoProperty prop) {
140144
obj = this.getReceiver().getALocalSource() and
141145
element = this.getArgument(0) and
142146
prop = setElement()
@@ -150,7 +154,7 @@ private module CollectionDataFlow {
150154
SetConstructor() { this = DataFlow::globalVarRef("Set").getAnInstantiation() }
151155

152156
override predicate loadStore(
153-
DataFlow::Node pred, DataFlow::Node succ, string fromProp, string toProp
157+
DataFlow::Node pred, DataFlow::Node succ, PseudoProperty fromProp, PseudoProperty toProp
154158
) {
155159
pred = this.getArgument(0) and
156160
succ = this and
@@ -176,14 +180,14 @@ private module CollectionDataFlow {
176180
element = DataFlow::lvalueNode(forOf.getLValue())
177181
}
178182

179-
override predicate load(DataFlow::Node obj, DataFlow::Node e, string prop) {
183+
override predicate load(DataFlow::Node obj, DataFlow::Node e, PseudoProperty prop) {
180184
obj = this and
181185
e = element and
182186
prop = arrayLikeElement()
183187
}
184188

185189
override predicate loadStore(
186-
DataFlow::Node pred, DataFlow::Node succ, string fromProp, string toProp
190+
DataFlow::Node pred, DataFlow::Node succ, PseudoProperty fromProp, PseudoProperty toProp
187191
) {
188192
pred = this and
189193
succ = element and
@@ -198,7 +202,7 @@ private module CollectionDataFlow {
198202
private class SetMapForEach extends CollectionFlowStep, DataFlow::MethodCallNode {
199203
SetMapForEach() { this.getMethodName() = "forEach" }
200204

201-
override predicate load(DataFlow::Node obj, DataFlow::Node element, string prop) {
205+
override predicate load(DataFlow::Node obj, DataFlow::Node element, PseudoProperty prop) {
202206
obj = this.getReceiver() and
203207
element = this.getCallback(0).getParameter(0) and
204208
prop = [setElement(), mapValueUnknownKey()]
@@ -207,12 +211,12 @@ private module CollectionDataFlow {
207211

208212
/**
209213
* A call to the `get` method on a Map.
210-
* If the key of the call to `get` has a known string value, then only the value corresponding to that key will be retrieved.
214+
* If the key of the call to `get` has a known string value, then only the value corresponding to that key will be retrieved. (The known string value is encoded as part of the pseudo-property)
211215
*/
212216
private class MapGet extends CollectionFlowStep, DataFlow::MethodCallNode {
213217
MapGet() { this.getMethodName() = "get" }
214218

215-
override predicate load(DataFlow::Node obj, DataFlow::Node element, string prop) {
219+
override predicate load(DataFlow::Node obj, DataFlow::Node element, PseudoProperty prop) {
216220
obj = this.getReceiver() and
217221
element = this and
218222
prop = mapValue(this.getArgument(0))
@@ -228,15 +232,23 @@ private module CollectionDataFlow {
228232
* then the value will be saved into a pseudo-property corresponding to the known string value.
229233
* The value will additionally be saved into a pseudo-property corresponding to values with unknown keys.
230234
*/
231-
private class MapSet extends CollectionFlowStep, DataFlow::MethodCallNode {
235+
class MapSet extends CollectionFlowStep, DataFlow::MethodCallNode {
232236
MapSet() { this.getMethodName() = "set" }
233237

234-
override predicate store(DataFlow::Node element, DataFlow::Node obj, string prop) {
238+
override predicate store(DataFlow::Node element, DataFlow::Node obj, PseudoProperty prop) {
235239
obj = this.getReceiver().getALocalSource() and
236240
element = this.getArgument(1) and
237-
// Makes sure that both known and unknown gets will work.
238-
prop = [mapValue(this.getArgument(0)), mapValueUnknownKey()]
241+
prop = getAPseudoProperty()
239242
}
243+
244+
/**
245+
* Gets a pseudo-property used to store an element in a map.
246+
* The pseudo-property represents both values where the key is a known string value (which is encoded in the pseudo-property),
247+
* and values where the key is unknown.
248+
*
249+
* The return-type is `string` as this predicate is used to define which pseudo-properties exist.
250+
*/
251+
string getAPseudoProperty() { result = [mapValue(this.getArgument(0)), mapValueUnknownKey()] }
240252
}
241253

242254
/**
@@ -246,7 +258,7 @@ private module CollectionDataFlow {
246258
MapAndSetValues() { this.getMethodName() = "values" }
247259

248260
override predicate loadStore(
249-
DataFlow::Node pred, DataFlow::Node succ, string fromProp, string toProp
261+
DataFlow::Node pred, DataFlow::Node succ, PseudoProperty fromProp, PseudoProperty toProp
250262
) {
251263
pred = this.getReceiver() and
252264
succ = this and
@@ -262,7 +274,7 @@ private module CollectionDataFlow {
262274
SetKeys() { this.getMethodName() = "keys" }
263275

264276
override predicate loadStore(
265-
DataFlow::Node pred, DataFlow::Node succ, string fromProp, string toProp
277+
DataFlow::Node pred, DataFlow::Node succ, PseudoProperty fromProp, PseudoProperty toProp
266278
) {
267279
pred = this.getReceiver() and
268280
succ = this and

0 commit comments

Comments
 (0)