Merge pull request #5794 from erik-krogh/rxPipe

Approved by asgerf
This commit is contained in:
CodeQL CI
2021-05-10 02:06:34 -07:00
committed by GitHub
3 changed files with 76 additions and 20 deletions

View File

@@ -23,7 +23,7 @@ private class RxJsSubscribeStep extends TaintTracking::SharedTaintStep {
* created by the `map` call.
*/
private DataFlow::Node pipeInput(DataFlow::CallNode pipe) {
pipe = DataFlow::moduleMember("rxjs/operators", ["map", "filter"]).getACall() and
pipe = DataFlow::moduleMember("rxjs/operators", any(string s | not s = "catchError")).getACall() and
result = pipe.getCallback(0).getParameter(0)
}
@@ -34,7 +34,8 @@ private DataFlow::Node pipeInput(DataFlow::CallNode pipe) {
* the pipe.
*/
private DataFlow::Node pipeOutput(DataFlow::CallNode pipe) {
pipe = DataFlow::moduleMember("rxjs/operators", "map").getACall() and
// we assume if there is a return, it is an output.
pipe = DataFlow::moduleMember("rxjs/operators", _).getACall() and
result = pipe.getCallback(0).getReturnNode()
or
pipe = DataFlow::moduleMember("rxjs/operators", "filter").getACall() and
@@ -48,7 +49,46 @@ private DataFlow::Node pipeOutput(DataFlow::CallNode pipe) {
* be special-cased.
*/
private predicate isIdentityPipe(DataFlow::CallNode pipe) {
pipe = DataFlow::moduleMember("rxjs/operators", "catchError").getACall()
pipe = DataFlow::moduleMember("rxjs/operators", ["catchError", "tap"]).getACall()
}
/**
* A call to `pipe`, which is assumed to be an `rxjs/operators` pipe.
*
* Has utility methods `getInput`/`getOutput` to get the input/output of each
* element of the pipe.
* These utility methods automatically handle itentity pipes, and the
* first/last elements of the pipe.
*/
private class RxJSPipe extends DataFlow::MethodCallNode {
RxJSPipe() { this.getMethodName() = "pipe" }
/**
* Gets an input to pipe element `i`.
* Or if `i` is equal to the number of elements, gets the output of the pipe (the call itself)
*/
DataFlow::Node getInput(int i) {
result = pipeInput(this.getArgument(i).getALocalSource())
or
i = this.getNumArgument() and
result = this
}
/**
* Gets an output from pipe element `i`.
* Handles identity pipes by getting the output from the previous element.
* If `i` is -1, gets the receiver to the call, which started the pipe.
*/
DataFlow::Node getOutput(int i) {
isIdentityPipe(this.getArgument(i).getALocalSource()) and
result = getOutput(i - 1)
or
not isIdentityPipe(this.getArgument(i).getALocalSource()) and
result = pipeOutput(this.getArgument(i).getALocalSource())
or
i = -1 and
result = this.getReceiver()
}
}
/**
@@ -56,22 +96,9 @@ private predicate isIdentityPipe(DataFlow::CallNode pipe) {
*/
private class RxJsPipeMapStep extends TaintTracking::SharedTaintStep {
override predicate heapStep(DataFlow::Node pred, DataFlow::Node succ) {
exists(DataFlow::MethodCallNode call | call.getMethodName() = "pipe" |
pred = call.getReceiver() and
succ = pipeInput(call.getArgument(0).getALocalSource())
or
exists(int i |
pred = pipeOutput(call.getArgument(i).getALocalSource()) and
succ = pipeInput(call.getArgument(i + 1).getALocalSource())
)
or
pred = pipeOutput(call.getLastArgument().getALocalSource()) and
succ = call
or
// Handle a common case where the last step is `catchError`.
isIdentityPipe(call.getLastArgument().getALocalSource()) and
pred = pipeOutput(call.getArgument(call.getNumArgument() - 2)) and
succ = call
exists(RxJSPipe pipe, int i |
pred = pipe.getOutput(i) and
succ = pipe.getInput(i + 1)
)
}
}