@@ -15,14 +15,15 @@ module AsyncPackage {
1515 }
1616
1717 /**
18- * Gets a reference to the given member or one of its `Limit` or `Series` variants.
18+ * Gets `Limit` or `Series` name variants for a given member name .
1919 *
20- * For example, `memberVariant ("map")` finds references to `map`, `mapLimit`, and `mapSeries`.
20+ * For example, `memberNameVariant ("map")` returns `map`, `mapLimit`, and `mapSeries`.
2121 */
22- DataFlow:: SourceNode memberVariant ( string name ) {
23- result = member ( name ) or
24- result = member ( name + "Limit" ) or
25- result = member ( name + "Series" )
22+ bindingset [ name]
23+ string memberNameVariant ( string name ) {
24+ result = name or
25+ result = name + "Limit" or
26+ result = name + "Series"
2627 }
2728
2829 /**
@@ -101,22 +102,47 @@ module AsyncPackage {
101102 */
102103 class IterationCall extends DataFlow:: InvokeNode {
103104 string name ;
105+ int iteratorCallbackIndex ;
106+ int finalCallbackIndex ;
104107
105108 IterationCall ( ) {
106- this = memberVariant ( name ) .getACall ( ) and
107- name =
108- [
109- "concat" , "detect" , "each" , "eachOf" , "forEach" , "forEachOf" , "every" , "filter" ,
110- "groupBy" , "map" , "mapValues" , "reduce" , "reduceRight" , "reject" , "some" , "sortBy" ,
111- "transform"
112- ]
109+ (
110+ (
111+ name =
112+ memberNameVariant ( [
113+ "concat" , "detect" , "each" , "eachOf" , "forEach" , "forEachOf" , "every" , "filter" ,
114+ "groupBy" , "map" , "mapValues" , "reject" , "some" , "sortBy" ,
115+ ] ) and
116+ if name .matches ( "%Limit" )
117+ then (
118+ iteratorCallbackIndex = 2 and finalCallbackIndex = 3
119+ ) else (
120+ iteratorCallbackIndex = 1 and finalCallbackIndex = 2
121+ )
122+ )
123+ or
124+ name = [ "reduce" , "reduceRight" , "transform" ] and
125+ iteratorCallbackIndex = 2 and
126+ finalCallbackIndex = 3
127+ ) and
128+ this = member ( name ) .getACall ( )
113129 }
114130
115131 /**
116- * Gets the name of the iteration call, without the `Limit` or `Series` suffix.
132+ * Gets the name of the iteration call
117133 */
118134 string getName ( ) { result = name }
119135
136+ /**
137+ * Gets the iterator callback index
138+ */
139+ int getIteratorCallbackIndex ( ) { result = iteratorCallbackIndex }
140+
141+ /**
142+ * Gets the final callback index
143+ */
144+ int getFinalCallbackIndex ( ) { result = finalCallbackIndex }
145+
120146 /**
121147 * Gets the node holding the collection being iterated over.
122148 */
@@ -125,41 +151,88 @@ module AsyncPackage {
125151 /**
126152 * Gets the node holding the function being called for each element in the collection.
127153 */
128- DataFlow:: Node getIteratorCallback ( ) { result = this .getArgument ( this .getNumArgument ( ) - 2 ) }
154+ DataFlow:: FunctionNode getIteratorCallback ( ) {
155+ result = this .getCallback ( iteratorCallbackIndex )
156+ }
129157
130158 /**
131- * Gets the node holding the function being invoked after iteration is complete.
159+ * Gets the node holding the function being invoked after iteration is complete. (may not exist)
132160 */
133- DataFlow:: Node getFinalCallback ( ) { result = this .getArgument ( this .getNumArgument ( ) - 1 ) }
161+ DataFlow:: FunctionNode getFinalCallback ( ) { result = this .getCallback ( finalCallbackIndex ) }
162+ }
163+
164+ /**
165+ * An IterationCall with its iterator callback at index 1
166+ */
167+ private class IterationCallCallbacksFirstArg extends IterationCall {
168+ IterationCallCallbacksFirstArg ( ) { this .getIteratorCallbackIndex ( ) = 1 }
169+ }
170+
171+ /**
172+ * An IterationCall with its iterator callback at index 2
173+ */
174+ private class IterationCallCallbacksSecondArg extends IterationCall {
175+ IterationCallCallbacksSecondArg ( ) { this .getIteratorCallbackIndex ( ) = 2 }
176+ }
177+
178+ /**
179+ * The model with the iteratorCallbackIndex abstracted
180+ */
181+ bindingset [ iteratorCallbackIndex]
182+ private predicate iterationCallPropagatesFlow (
183+ string input , string output , boolean preservesValue , int iteratorCallbackIndex
184+ ) {
185+ preservesValue = true and
186+ input = "Argument[0]." + [ "ArrayElement" , "SetElement" , "IteratorElement" , "AnyMember" ] and
187+ output = "Argument[" + iteratorCallbackIndex + "].Parameter[0]"
134188 }
135189
136190 /**
137- * A taint step from the collection into the iterator callback of an iteration call.
191+ * A taint step from the collection into the iterator callback (at index 1) of an iteration call.
138192 *
139193 * For example: `data -> item` in `async.each(data, (item, cb) => {})`.
140194 */
141- private class IterationInputTaintStep extends TaintTracking:: SharedTaintStep {
142- override predicate step ( DataFlow:: Node pred , DataFlow:: Node succ ) {
143- exists ( DataFlow:: FunctionNode iteratee , IterationCall call |
144- iteratee = call .getIteratorCallback ( ) and // Require a closure to avoid spurious call/return mismatch.
145- pred = call .getCollection ( ) and // TODO: needs a flow summary to ensure ArrayElement content is unfolded
146- succ = iteratee .getParameter ( 0 )
147- )
195+ class IterationCallCallbacksFirstArgFlowSummary extends DataFlow:: SummarizedCallable {
196+ IterationCallCallbacksFirstArgFlowSummary ( ) { this = "async.[IterationCallCallbacksFirstArg]" }
197+
198+ override DataFlow:: InvokeNode getACallSimple ( ) {
199+ result instanceof IterationCallCallbacksFirstArg
200+ }
201+
202+ override predicate propagatesFlow ( string input , string output , boolean preservesValue ) {
203+ iterationCallPropagatesFlow ( input , output , preservesValue , 1 )
204+ }
205+ }
206+
207+ /**
208+ * A taint step from the collection into the iterator callback (at index 2) of an iteration call.
209+ *
210+ * For example: `data -> item` in `async.eachLimit(data, 1, (item, cb) => {})`.
211+ */
212+ class IterationCallCallbacksSecondArgFlowSummary extends DataFlow:: SummarizedCallable {
213+ IterationCallCallbacksSecondArgFlowSummary ( ) { this = "async.[IterationCallCallbackSecondArg]" }
214+
215+ override DataFlow:: InvokeNode getACallSimple ( ) {
216+ result instanceof IterationCallCallbacksSecondArg
217+ }
218+
219+ override predicate propagatesFlow ( string input , string output , boolean preservesValue ) {
220+ iterationCallPropagatesFlow ( input , output , preservesValue , 2 )
148221 }
149222 }
150223
151224 /**
152225 * A taint step from the return value of an iterator callback to the result of the iteration
153226 * call.
154227 *
155- * For example: `item + taint()` -> result` in `async.map(data, (item, cb) => cb(null, item + taint()), (err, result) => {})`.
228+ * For example: `item + taint() -> result` in `async.map(data, (item, cb) => cb(null, item + taint()), (err, result) => {})`.
156229 */
157230 private class IterationOutputTaintStep extends TaintTracking:: SharedTaintStep {
158231 override predicate step ( DataFlow:: Node pred , DataFlow:: Node succ ) {
159232 exists (
160233 DataFlow:: FunctionNode iteratee , DataFlow:: FunctionNode final , int i , IterationCall call
161234 |
162- iteratee = call .getIteratorCallback ( ) . getALocalSource ( ) and
235+ iteratee = call .getIteratorCallback ( ) and
163236 final = call .getFinalCallback ( ) and // Require a closure to avoid spurious call/return mismatch.
164237 pred = getLastParameter ( iteratee ) .getACall ( ) .getArgument ( i ) and
165238 succ = final .getParameter ( i ) and
@@ -175,14 +248,18 @@ module AsyncPackage {
175248 *
176249 * For example: `data -> result` in `async.sortBy(data, orderingFn, (err, result) => {})`.
177250 */
178- private class IterationPreserveTaintStep extends TaintTracking:: SharedTaintStep {
179- override predicate step ( DataFlow:: Node pred , DataFlow:: Node succ ) {
180- exists ( DataFlow:: FunctionNode final , IterationCall call |
181- final = call .getFinalCallback ( ) and // Require a closure to avoid spurious call/return mismatch.
182- pred = call .getCollection ( ) and
183- succ = final .getParameter ( 1 ) and
184- call .getName ( ) = "sortBy"
185- )
251+ class IterationPreserveTaintStepFlowSummary extends DataFlow:: SummarizedCallable {
252+ IterationPreserveTaintStepFlowSummary ( ) { this = "async.sortBy" }
253+
254+ override DataFlow:: InvokeNode getACallSimple ( ) {
255+ result instanceof IterationCall and
256+ result .( IterationCall ) .getName ( ) = "sortBy"
257+ }
258+
259+ override predicate propagatesFlow ( string input , string output , boolean preservesValue ) {
260+ preservesValue = false and
261+ input = "Argument[0]." + [ "ArrayElement" , "SetElement" , "IteratorElement" , "AnyMember" ] and
262+ output = "Argument[2].Parameter[1]"
186263 }
187264 }
188265}
0 commit comments