Skip to content

Commit 6497a61

Browse files
committed
Ratpack: Drop support for flatMap like methods
1 parent af90b00 commit 6497a61

File tree

2 files changed

+65
-47
lines changed

2 files changed

+65
-47
lines changed

java/ql/src/semmle/code/java/frameworks/ratpack/RatpackExec.qll

Lines changed: 34 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ abstract private class SimpleFluentLambdaMethod extends FluentLambdaMethod {
5454
private class RatpackPromiseMapMethod extends SimpleFluentLambdaMethod {
5555
RatpackPromiseMapMethod() {
5656
getDeclaringType() instanceof RatpackPromise and
57-
hasName(["map", "flatMap", "blockingMap", "apply"])
57+
hasName(["map", "blockingMap"]) // "flatMap" & "apply" cause false positives. Wait for fluent lambda support.
5858
}
5959

6060
override predicate consumesTaint(int lambdaArg) { lambdaArg = 0 }
@@ -63,15 +63,14 @@ private class RatpackPromiseMapMethod extends SimpleFluentLambdaMethod {
6363
}
6464

6565
/**
66-
* Represents the `mapIf` and `flatMapIf` method.
66+
* Represents the `mapIf` method.
6767
*
6868
* `<O> Promise<O> mapIf(Predicate<T> predicate, Function<T, O> onTrue, Function<T, O> onFalse)`
69-
* `<O> Promise<O> flatMapIf(Predicate<T> predicate, Function<T, Promise<O>> onTrue, Function<T, Promise<O>> onFalse)`
7069
*/
7170
private class RatpackPromiseMapIfMethod extends FluentLambdaMethod {
7271
RatpackPromiseMapIfMethod() {
7372
getDeclaringType() instanceof RatpackPromise and
74-
hasName(["mapIf", "flatMapIf"]) and
73+
hasName(["mapIf"]) and // "flatMapIf" causes false positives. Wait for fluent lambda support.
7574
getNumberOfParameters() = 3
7675
}
7776

@@ -85,7 +84,7 @@ private class RatpackPromiseMapIfMethod extends FluentLambdaMethod {
8584
private class RatpackPromiseMapErrorMethod extends FluentLambdaMethod {
8685
RatpackPromiseMapErrorMethod() {
8786
getDeclaringType() instanceof RatpackPromise and
88-
hasName(["mapError", "flatMapError"])
87+
hasName(["mapError"]) // "flatMapError" causes false positives. Wait for fluent lambda support.
8988
}
9089

9190
override predicate consumesTaint(int methodArg, int lambdaArg) { none() }
@@ -121,41 +120,41 @@ private class RatpackPromiseFluentMethod extends FluentMethod, FluentLambdaMetho
121120
hasName(["route"]) and methodArg = [0, 1] and lambdaArg = 0
122121
}
123122

124-
override predicate doesReturnTaint(int arg) { hasName(["flatMapIf"]) and arg = 1 }
123+
override predicate doesReturnTaint(int arg) { none() } // "flatMapIf" causes false positives. Wait for fluent lambda support.
124+
}
125+
126+
/**
127+
* Holds if the method access qualifier `node1` has dataflow to the functional expression parameter `node2`.
128+
*/
129+
private predicate stepIntoLambda(DataFlow::Node node1, DataFlow::Node node2) {
130+
exists(MethodAccess ma, FluentLambdaMethod flm, int methodArg, int lambdaArg |
131+
flm.consumesTaint(methodArg, lambdaArg)
132+
|
133+
ma.getMethod() = flm and
134+
node1.asExpr() = ma.getQualifier() and
135+
ma.getArgument(methodArg).(FunctionalExpr).asMethod().getParameter(lambdaArg) =
136+
node2.asParameter()
137+
)
138+
}
139+
140+
/**
141+
* Holds if the return statement result of the functional expression `node1` has dataflow to the
142+
* method access result `node2`.
143+
*/
144+
private predicate stepOutOfLambda(DataFlow::Node node1, DataFlow::Node node2) {
145+
exists(FluentLambdaMethod flm, MethodAccess ma, FunctionalExpr fe, int arg |
146+
flm.doesReturnTaint(arg)
147+
|
148+
fe.asMethod().getBody().getAStmt().(ReturnStmt).getResult() = node1.asExpr() and
149+
ma.getMethod() = flm and
150+
node2.asExpr() = ma and
151+
ma.getArgument(arg) = fe
152+
)
125153
}
126154

127155
private class RatpackPromiseTaintPreservingStep extends AdditionalTaintStep {
128156
override predicate step(DataFlow::Node node1, DataFlow::Node node2) {
129157
stepIntoLambda(node1, node2) or
130158
stepOutOfLambda(node1, node2)
131159
}
132-
133-
/**
134-
* Holds if the method access qualifier `node1` has dataflow to the functional expression parameter `node2`.
135-
*/
136-
predicate stepIntoLambda(DataFlow::Node node1, DataFlow::Node node2) {
137-
exists(MethodAccess ma, FluentLambdaMethod flm, int methodArg, int lambdaArg |
138-
flm.consumesTaint(methodArg, lambdaArg)
139-
|
140-
ma.getMethod() = flm and
141-
node1.asExpr() = ma.getQualifier() and
142-
ma.getArgument(methodArg).(FunctionalExpr).asMethod().getParameter(lambdaArg) =
143-
node2.asParameter()
144-
)
145-
}
146-
147-
/**
148-
* Holds if the return statement result of the functional expression `node1` has dataflow to the
149-
* method access result `node2`.
150-
*/
151-
predicate stepOutOfLambda(DataFlow::Node node1, DataFlow::Node node2) {
152-
exists(FluentLambdaMethod flm, MethodAccess ma, FunctionalExpr fe, int arg |
153-
flm.doesReturnTaint(arg)
154-
|
155-
fe.asMethod().getBody().getAStmt().(ReturnStmt).getResult() = node1.asExpr() and
156-
ma.getMethod() = flm and
157-
node2.asExpr() = ma and
158-
ma.getArgument(arg) = fe
159-
)
160-
}
161160
}

java/ql/test/library-tests/frameworks/ratpack/resources/Resource.java

Lines changed: 31 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import ratpack.core.parse.Parse;
66
import ratpack.exec.Promise;
77
import ratpack.func.Action;
8+
import ratpack.func.Function;
89
import java.io.OutputStream;
910

1011
class Resource {
@@ -67,7 +68,7 @@ void test4() {
6768
sink(Promise.value(tainted)); //$hasTaintFlow
6869
Promise
6970
.value(tainted)
70-
.flatMap(a -> Promise.value(a))
71+
.map(a -> a)
7172
.then(this::sink); //$hasTaintFlow
7273
}
7374

@@ -180,33 +181,51 @@ void test8() {
180181
.then(value -> {
181182
sink(value); //$hasTaintFlow
182183
});
184+
// Waiting for support for lambda data flow
185+
// Promise
186+
// .value("potato")
187+
// .flatMapError(RuntimeException.class, exception -> {
188+
// return Promise.value(taint());
189+
// })
190+
// .then(value -> {
191+
// sink(value); //$hasTaintFlow
192+
// });
193+
}
194+
195+
void test9() {
196+
String tainted = taint();
183197
Promise
184-
.value("potato")
185-
.flatMapError(RuntimeException.class, exception -> {
186-
return Promise.value(taint());
187-
})
198+
.value(tainted)
199+
.map(Resource::identity)
188200
.then(value -> {
189201
sink(value); //$hasTaintFlow
190202
});
203+
Promise
204+
.value("potato")
205+
.map(Resource::identity)
206+
.then(value -> {
207+
sink(value); // no taints flow
208+
});
191209
}
192210

193-
void test9() {
211+
public static String identity(String input) {
212+
return input;
213+
}
214+
215+
void test10() {
194216
String tainted = taint();
195217
Promise
196218
.value(tainted)
197-
.apply(Resource::identity)
219+
.map(a -> a)
198220
.then(value -> {
199221
sink(value); //$hasTaintFlow
200222
});
201223
Promise
202224
.value("potato")
203-
.apply(Resource::identity)
225+
.map(a -> a)
204226
.then(value -> {
205227
sink(value); // no taints flow
206228
});
207229
}
208-
209-
public static Promise<String> identity(Promise<String> input) {
210-
return input.map(i -> i);
211-
}
230+
212231
}

0 commit comments

Comments
 (0)