@@ -23,7 +23,7 @@ private class RxJsSubscribeStep extends TaintTracking::SharedTaintStep {
2323 * created by the `map` call.
2424 */
2525private DataFlow:: Node pipeInput ( DataFlow:: CallNode pipe ) {
26- pipe = DataFlow:: moduleMember ( "rxjs/operators" , [ "map" , "filter" ] ) .getACall ( ) and
26+ pipe = DataFlow:: moduleMember ( "rxjs/operators" , any ( string s | not s = "catchError" ) ) .getACall ( ) and
2727 result = pipe .getCallback ( 0 ) .getParameter ( 0 )
2828}
2929
@@ -48,30 +48,56 @@ private DataFlow::Node pipeOutput(DataFlow::CallNode pipe) {
4848 * be special-cased.
4949 */
5050private predicate isIdentityPipe ( DataFlow:: CallNode pipe ) {
51- pipe = DataFlow:: moduleMember ( "rxjs/operators" , "catchError" ) .getACall ( )
51+ pipe = DataFlow:: moduleMember ( "rxjs/operators" , [ "catchError" , "tap" ] ) .getACall ( )
52+ }
53+
54+ /**
55+ * A call to `pipe`, which is assumed to be an `rxjs/operators` pipe.
56+ *
57+ * Has utility methods `getInput`/`getOutput` to get the input/output of each
58+ * element of the pipe.
59+ * These utility methods automatically handle itentity pipes, and the
60+ * first/last elements of the pipe.
61+ */
62+ private class RxJSPipe extends DataFlow:: MethodCallNode {
63+ RxJSPipe ( ) { this .getMethodName ( ) = "pipe" }
64+
65+ /**
66+ * Gets an input to pipe element `i`.
67+ * Or if `i` is equal to the number of elements, gets the output of the pipe (the call itself)
68+ */
69+ DataFlow:: Node getInput ( int i ) {
70+ result = pipeInput ( this .getArgument ( i ) .getALocalSource ( ) )
71+ or
72+ i = this .getNumArgument ( ) and
73+ result = this
74+ }
75+
76+ /**
77+ * Gets an output from pipe element `i`.
78+ * Handles identity pipes by getting the output from the previous element.
79+ * If `i` is -1, gets the receiver to the call, which started the pipe.
80+ */
81+ DataFlow:: Node getOutput ( int i ) {
82+ isIdentityPipe ( this .getArgument ( i ) .getALocalSource ( ) ) and
83+ result = getOutput ( i - 1 )
84+ or
85+ not isIdentityPipe ( this .getArgument ( i ) .getALocalSource ( ) ) and
86+ result = pipeOutput ( this .getArgument ( i ) .getALocalSource ( ) )
87+ or
88+ i = - 1 and
89+ result = this .getReceiver ( )
90+ }
5291}
5392
5493/**
5594 * A step in or out of the map callback in a call of form `x.pipe(map(y => ...))`.
5695 */
5796private class RxJsPipeMapStep extends TaintTracking:: SharedTaintStep {
5897 override predicate heapStep ( DataFlow:: Node pred , DataFlow:: Node succ ) {
59- exists ( DataFlow:: MethodCallNode call | call .getMethodName ( ) = "pipe" |
60- pred = call .getReceiver ( ) and
61- succ = pipeInput ( call .getArgument ( 0 ) .getALocalSource ( ) )
62- or
63- exists ( int i |
64- pred = pipeOutput ( call .getArgument ( i ) .getALocalSource ( ) ) and
65- succ = pipeInput ( call .getArgument ( i + 1 ) .getALocalSource ( ) )
66- )
67- or
68- pred = pipeOutput ( call .getLastArgument ( ) .getALocalSource ( ) ) and
69- succ = call
70- or
71- // Handle a common case where the last step is `catchError`.
72- isIdentityPipe ( call .getLastArgument ( ) .getALocalSource ( ) ) and
73- pred = pipeOutput ( call .getArgument ( call .getNumArgument ( ) - 2 ) ) and
74- succ = call
98+ exists ( RxJSPipe pipe , int i |
99+ pred = pipe .getOutput ( i ) and
100+ succ = pipe .getInput ( i + 1 )
75101 )
76102 }
77103}
0 commit comments