@@ -8,6 +8,22 @@ import javascript
88private import semmle.javascript.dataflow.internal.StepSummary
99private 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