@@ -23,7 +23,7 @@ private class RxJsSubscribeStep extends TaintTracking::SharedTaintStep {
23
23
* created by the `map` call.
24
24
*/
25
25
private 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
27
27
result = pipe .getCallback ( 0 ) .getParameter ( 0 )
28
28
}
29
29
@@ -48,30 +48,56 @@ private DataFlow::Node pipeOutput(DataFlow::CallNode pipe) {
48
48
* be special-cased.
49
49
*/
50
50
private 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
+ }
52
91
}
53
92
54
93
/**
55
94
* A step in or out of the map callback in a call of form `x.pipe(map(y => ...))`.
56
95
*/
57
96
private class RxJsPipeMapStep extends TaintTracking:: SharedTaintStep {
58
97
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 )
75
101
)
76
102
}
77
103
}
0 commit comments