@@ -68,7 +68,7 @@ module ArrayTaintTracking {
6868 succ = call
6969 or
7070 // `e = Array.from(x)`: if `x` is tainted, then so is `e`.
71- call = DataFlow :: globalVarRef ( "Array" ) . getAPropertyRead ( "from" ) . getACall ( ) and
71+ call = arrayFromCall ( ) and
7272 pred = call .getAnArgument ( ) and
7373 succ = call
7474 or
@@ -79,6 +79,11 @@ module ArrayTaintTracking {
7979 call .( DataFlow:: MethodCallNode ) .getMethodName ( ) = "concat" and
8080 succ = call and
8181 pred = call .getAnArgument ( )
82+ or
83+ // find
84+ // `e = arr.find(callback)`
85+ call = arrayFindCall ( pred ) and
86+ succ = call
8287 }
8388}
8489
@@ -97,7 +102,7 @@ private module ArrayDataFlow {
97102 DataFlow:: Node pred , DataFlow:: Node succ , string fromProp , string toProp
98103 ) {
99104 exists ( DataFlow:: CallNode call |
100- call = DataFlow :: globalVarRef ( "Array" ) . getAMemberCall ( "from" ) and
105+ call = arrayFromCall ( ) and
101106 pred = call .getArgument ( 0 ) and
102107 succ = call and
103108 fromProp = arrayLikeElement ( ) and
@@ -297,4 +302,108 @@ private module ArrayDataFlow {
297302 )
298303 }
299304 }
305+
306+ /**
307+ * A step modelling that elements from an array `arr` are received by calling `find`.
308+ */
309+ private class ArrayFindStep extends DataFlow:: SharedFlowStep {
310+ override predicate loadStep ( DataFlow:: Node pred , DataFlow:: Node succ , string prop ) {
311+ exists ( DataFlow:: CallNode call |
312+ call = arrayFindCall ( pred ) and
313+ succ = call and
314+ prop = arrayElement ( )
315+ )
316+ }
317+ }
318+ }
319+
320+ private import ArrayLibraries
321+
322+ /**
323+ * Classes and predicates modelling various libraries that work on arrays or array-like structures.
324+ */
325+ private module ArrayLibraries {
326+ private import DataFlow:: PseudoProperties
327+
328+ /**
329+ * Gets a call to `Array.from` or a polyfill implementing the same functionality.
330+ */
331+ DataFlow:: CallNode arrayFromCall ( ) {
332+ result = DataFlow:: globalVarRef ( "Array" ) .getAMemberCall ( "from" )
333+ or
334+ result = DataFlow:: moduleImport ( "array-from" ) .getACall ( )
335+ }
336+
337+ /**
338+ * Gets a call to `Array.prototype.find` or a polyfill implementing the same functionality.
339+ */
340+ DataFlow:: CallNode arrayFindCall ( DataFlow:: Node array ) {
341+ result .( DataFlow:: MethodCallNode ) .getMethodName ( ) = "find" and
342+ array = result .getReceiver ( )
343+ or
344+ result = DataFlow:: moduleImport ( [ "array.prototype.find" , "array-find" ] ) .getACall ( ) and
345+ array = result .getArgument ( 0 )
346+ }
347+
348+ /**
349+ * A taint step through the `arrify` library, or other libraries that (maybe) convert values into arrays.
350+ */
351+ private class ArrayifyStep extends TaintTracking:: SharedTaintStep {
352+ override predicate step ( DataFlow:: Node pred , DataFlow:: Node succ ) {
353+ exists ( API:: CallNode call | call = API:: moduleImport ( [ "arrify" , "array-ify" ] ) .getACall ( ) |
354+ pred = call .getArgument ( 0 ) and succ = call
355+ )
356+ }
357+ }
358+
359+ /**
360+ * A call to a library that copies the elements of an array into another array.
361+ * E.g. `array-union` that creates a union of multiple arrays, or `array-uniq` that creates an array with unique elements.
362+ */
363+ DataFlow:: CallNode arrayCopyCall ( DataFlow:: Node array ) {
364+ result = API:: moduleImport ( [ "array-union" , "array-uniq" , "uniq" ] ) .getACall ( ) and
365+ array = result .getAnArgument ( )
366+ }
367+
368+ /**
369+ * A taint step for a library that copies the elements of an array into another array.
370+ */
371+ private class ArrayCopyTaint extends TaintTracking:: SharedTaintStep {
372+ override predicate step ( DataFlow:: Node pred , DataFlow:: Node succ ) {
373+ exists ( DataFlow:: CallNode call |
374+ call = arrayCopyCall ( pred ) and
375+ succ = call
376+ )
377+ }
378+ }
379+
380+ /**
381+ * A loadStoreStep for a library that copies the elements of an array into another array.
382+ */
383+ private class ArrayCopyLoadStore extends DataFlow:: SharedFlowStep {
384+ override predicate loadStoreStep ( DataFlow:: Node pred , DataFlow:: Node succ , string prop ) {
385+ exists ( DataFlow:: CallNode call |
386+ call = arrayCopyCall ( pred ) and
387+ succ = call and
388+ prop = arrayElement ( )
389+ )
390+ }
391+ }
392+
393+ /**
394+ * A taint step through a call to `Array.prototype.flat` or a polyfill implementing array flattening.
395+ */
396+ private class ArrayFlatStep extends TaintTracking:: SharedTaintStep {
397+ override predicate step ( DataFlow:: Node pred , DataFlow:: Node succ ) {
398+ exists ( DataFlow:: CallNode call | succ = call |
399+ call .( DataFlow:: MethodCallNode ) .getMethodName ( ) = "flat" and
400+ pred = call .getReceiver ( )
401+ or
402+ call =
403+ API:: moduleImport ( [ "array-flatten" , "arr-flatten" , "flatten" , "array.prototype.flat" ] )
404+ .getACall ( ) and
405+ pred = call .getAnArgument ( )
406+ )
407+ }
408+ }
300409}
0 commit comments