@@ -271,67 +271,82 @@ module TaintTracking {
271271 ArrayFunctionTaintStep ( ) { this = call }
272272
273273 override predicate step ( DataFlow:: Node pred , DataFlow:: Node succ ) {
274- // `array.map(function (elt, i, ary) { ... })`: if `array` is tainted, then so are
275- // `elt` and `ary`; similar for `forEach`
276- exists ( string name , Function f , int i |
277- ( name = "map" or name = "forEach" ) and
278- ( i = 0 or i = 2 ) and
279- call .getArgument ( 0 ) .analyze ( ) .getAValue ( ) .( AbstractFunction ) .getFunction ( ) = f and
280- call .( DataFlow:: MethodCallNode ) .getMethodName ( ) = name and
281- pred = call .getReceiver ( ) and
282- succ = DataFlow:: parameterNode ( f .getParameter ( i ) )
283- )
284- or
285- // `array.map` with tainted return value in callback
286- exists ( DataFlow:: FunctionNode f |
287- call .( DataFlow:: MethodCallNode ) .getMethodName ( ) = "map" and
288- call .getArgument ( 0 ) = f and // Require the argument to be a closure to avoid spurious call/return flow
289- pred = f .getAReturn ( ) and
290- succ = call
291- )
292- or
293- // `array.push(e)`, `array.unshift(e)`: if `e` is tainted, then so is `array`.
294- exists ( string name |
295- name = "push" or
296- name = "unshift"
297- |
298- pred = call .getAnArgument ( ) and
299- succ .( DataFlow:: SourceNode ) .getAMethodCall ( name ) = call
300- )
301- or
302- // `array.push(...e)`, `array.unshift(...e)`: if `e` is tainted, then so is `array`.
303- exists ( string name |
304- name = "push" or
305- name = "unshift"
306- |
307- pred = call .getASpreadArgument ( ) and
308- // Make sure we handle reflective calls
309- succ = call .getReceiver ( ) .getALocalSource ( ) and
310- call .getCalleeName ( ) = name
311- )
312- or
313- // `array.splice(i, del, e)`: if `e` is tainted, then so is `array`.
314- exists ( string name | name = "splice" |
315- pred = call .getArgument ( 2 ) and
316- succ .( DataFlow:: SourceNode ) .getAMethodCall ( name ) = call
317- )
318- or
319- // `e = array.pop()`, `e = array.shift()`, or similar: if `array` is tainted, then so is `e`.
320- exists ( string name |
321- name = "pop" or
322- name = "shift" or
323- name = "slice" or
324- name = "splice"
325- |
326- call .( DataFlow:: MethodCallNode ) .calls ( pred , name ) and
327- succ = call
328- )
329- or
330- // `e = Array.from(x)`: if `x` is tainted, then so is `e`.
331- call = DataFlow:: globalVarRef ( "Array" ) .getAPropertyRead ( "from" ) .getACall ( ) and
274+ arrayFunctionTaintStep ( pred , succ , call )
275+ }
276+ }
277+
278+ /**
279+ * A taint propagating data flow edge from `pred` to `succ` caused by a call `call` to a builtin array functions.
280+ */
281+ predicate arrayFunctionTaintStep ( DataFlow:: Node pred , DataFlow:: Node succ , DataFlow:: CallNode call ) {
282+ // `array.map(function (elt, i, ary) { ... })`: if `array` is tainted, then so are
283+ // `elt` and `ary`; similar for `forEach`
284+ exists ( string name , Function f , int i |
285+ ( name = "map" or name = "forEach" ) and
286+ ( i = 0 or i = 2 ) and
287+ call .getArgument ( 0 ) .analyze ( ) .getAValue ( ) .( AbstractFunction ) .getFunction ( ) = f and
288+ call .( DataFlow:: MethodCallNode ) .getMethodName ( ) = name and
289+ pred = call .getReceiver ( ) and
290+ succ = DataFlow:: parameterNode ( f .getParameter ( i ) )
291+ )
292+ or
293+ // `array.map` with tainted return value in callback
294+ exists ( DataFlow:: FunctionNode f |
295+ call .( DataFlow:: MethodCallNode ) .getMethodName ( ) = "map" and
296+ call .getArgument ( 0 ) = f and // Require the argument to be a closure to avoid spurious call/return flow
297+ pred = f .getAReturn ( ) and
298+ succ = call
299+ )
300+ or
301+ // `array.push(e)`, `array.unshift(e)`: if `e` is tainted, then so is `array`.
302+ exists ( string name |
303+ name = "push" or
304+ name = "unshift"
305+ |
332306 pred = call .getAnArgument ( ) and
307+ succ .( DataFlow:: SourceNode ) .getAMethodCall ( name ) = call
308+ )
309+ or
310+ // `array.push(...e)`, `array.unshift(...e)`: if `e` is tainted, then so is `array`.
311+ exists ( string name |
312+ name = "push" or
313+ name = "unshift"
314+ |
315+ pred = call .getASpreadArgument ( ) and
316+ // Make sure we handle reflective calls
317+ succ = call .getReceiver ( ) .getALocalSource ( ) and
318+ call .getCalleeName ( ) = name
319+ )
320+ or
321+ // `array.splice(i, del, e)`: if `e` is tainted, then so is `array`.
322+ exists ( string name | name = "splice" |
323+ pred = call .getArgument ( 2 ) and
324+ succ .( DataFlow:: SourceNode ) .getAMethodCall ( name ) = call
325+ )
326+ or
327+ // `e = array.pop()`, `e = array.shift()`, or similar: if `array` is tainted, then so is `e`.
328+ exists ( string name |
329+ name = "pop" or
330+ name = "shift" or
331+ name = "slice" or
332+ name = "splice"
333+ |
334+ call .( DataFlow:: MethodCallNode ) .calls ( pred , name ) and
333335 succ = call
334- }
336+ )
337+ or
338+ // `e = Array.from(x)`: if `x` is tainted, then so is `e`.
339+ call = DataFlow:: globalVarRef ( "Array" ) .getAPropertyRead ( "from" ) .getACall ( ) and
340+ pred = call .getAnArgument ( ) and
341+ succ = call
342+ or
343+ // `e = arr1.concat(arr2, arr3)`: if any of the `arr` is tainted, then so is `e`.
344+ call .( DataFlow:: MethodCallNode ) .calls ( pred , "concat" ) and
345+ succ = call
346+ or
347+ call .( DataFlow:: MethodCallNode ) .getMethodName ( ) = "concat" and
348+ succ = call and
349+ pred = call .getAnArgument ( )
335350 }
336351
337352 /**
0 commit comments