Skip to content

Commit 23e60e2

Browse files
committed
Add full integration test for Ratpack example
Signed-off-by: Jonathan Leitschuh <[email protected]>
1 parent ebbbda7 commit 23e60e2

File tree

13 files changed

+369
-5
lines changed

13 files changed

+369
-5
lines changed
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
//semmle-extractor-options: --javac-args -cp ${testdir}/../../../stubs/ratpack-1.9.x:${testdir}/../../../stubs/jackson-databind-2.12:${testdir}/../../../stubs/guava-30.0:${testdir}/../../../stubs/guava-30.0:${testdir}/../../../stubs/netty-4.1.x
1+
//semmle-extractor-options: --javac-args -cp ${testdir}/../../../stubs/ratpack-1.9.x:${testdir}/../../../stubs/jackson-core-2.12:${testdir}/../../../stubs/jackson-databind-2.12:${testdir}/../../../stubs/guava-30.0:${testdir}/../../../stubs/guava-30.0:${testdir}/../../../stubs/netty-4.1.x
Lines changed: 175 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,175 @@
1+
import com.fasterxml.jackson.databind.ObjectMapper;
2+
import com.fasterxml.jackson.databind.node.ObjectNode;
3+
import com.fasterxml.jackson.databind.node.POJONode;
4+
import com.fasterxml.jackson.databind.JsonSerializable;
5+
import com.fasterxml.jackson.databind.node.ArrayNode;
6+
import com.fasterxml.jackson.databind.JsonNode;
7+
import com.google.common.collect.ImmutableList;
8+
import com.google.common.collect.ImmutableMap;
9+
import ratpack.core.handling.Context;
10+
import ratpack.core.http.TypedData;
11+
import ratpack.core.form.Form;
12+
import ratpack.core.form.UploadedFile;
13+
import ratpack.core.parse.Parse;
14+
import ratpack.exec.Promise;
15+
import ratpack.func.Action;
16+
import ratpack.func.Function;
17+
import ratpack.func.MultiValueMap;
18+
19+
import java.io.OutputStream;
20+
import java.io.IOException;
21+
import java.util.Collection;
22+
import java.util.HashMap;
23+
import java.util.Map;
24+
import java.util.List;
25+
import java.util.function.Predicate;
26+
27+
import static ratpack.jackson.Jackson.jsonNode;
28+
29+
class IntegrationTest {
30+
31+
static class Pojo {
32+
String value;
33+
34+
String getValue() {
35+
return value;
36+
}
37+
}
38+
39+
private final ObjectMapper objectMapper = new ObjectMapper();
40+
41+
void sink(Object o) {}
42+
43+
String taint() {
44+
return null;
45+
}
46+
47+
void test1(Context ctx) {
48+
bindJson(ctx, Pojo.class)
49+
.then(pojo ->{
50+
sink(pojo); //$hasTaintFlow
51+
sink(pojo.value); //$hasTaintFlow
52+
sink(pojo.getValue()); //$hasTaintFlow
53+
});
54+
}
55+
56+
void test2(Context ctx) {
57+
bindForm(ctx, Pojo.class, defaults -> defaults.put("another", "potato"))
58+
.then(pojo ->{
59+
sink(pojo); //$hasTaintFlow
60+
sink(pojo.value); //$hasTaintFlow
61+
sink(pojo.getValue()); //$hasTaintFlow
62+
});
63+
}
64+
65+
void test3() {
66+
Object value = extractSingleValueIfPossible(ImmutableList.of("a", taint()));
67+
sink(value); //$hasTaintFlow
68+
}
69+
70+
void test4(Context ctx) {
71+
parseToForm(ctx, Pojo.class)
72+
.map(pojoForm -> {
73+
Map<String, Object> mergedParams = new HashMap<>();
74+
filterAndMerge(pojoForm, mergedParams, name -> false);
75+
return mergedParams;
76+
}).then(pojoMap -> {
77+
sink(pojoMap); //$hasTaintFlow
78+
sink(pojoMap.get("value")); //$hasTaintFlow
79+
});
80+
}
81+
82+
private <T> Promise<T> bindJson(Context ctx, Class<T> type) {
83+
return ctx.getRequest().getBody()
84+
.map(data -> {
85+
String dataText = data.getText();
86+
87+
try {
88+
return ctx.parse(data, jsonNode(objectMapper));
89+
} catch (Exception e) {
90+
String msg = "Unable to parse json data while binding type " + type.getCanonicalName() + " [jsonData: " + dataText + "]";
91+
throw new RuntimeException(msg, e);
92+
}
93+
})
94+
.map(json ->
95+
bind(ctx, json, type)
96+
);
97+
}
98+
99+
private <T> T bind(Context ctx, JsonNode input, Class<T> type) {
100+
T value;
101+
try {
102+
value = objectMapper.convertValue(input, type);
103+
} catch (Exception e) {
104+
throw new RuntimeException("Failed to convert input to " + type.getName(), e);
105+
}
106+
return value;
107+
}
108+
109+
private static Promise<Form> parseToForm(Context ctx, Class<?> type) {
110+
return ctx.getRequest().getBody()
111+
.map(data -> {
112+
try {
113+
return ctx.parse(data, Form.form());
114+
} catch (Exception e) {
115+
String msg = "Unable to parse form data while binding type " + type.getCanonicalName() + " [formData: " + data.getText() + "]";
116+
throw new RuntimeException(msg, e);
117+
}
118+
});
119+
}
120+
121+
private <T> Promise<T> bindForm(Context ctx, Class<T> type, Action<? super ImmutableMap.Builder<String, Object>> defaults) {
122+
return parseToForm(ctx, type)
123+
.map(form -> {
124+
ObjectNode input = toObjectNode(form, defaults, s -> false);
125+
Map<String, List<UploadedFile>> filesMap = form.files().getAll();
126+
filesMap.forEach((name, files) -> {
127+
ArrayNode array = input.putArray(name);
128+
files.forEach(f -> array.add(new POJONode(new UploadedFileWrapper(f))));
129+
});
130+
return bind(ctx, input, type);
131+
});
132+
}
133+
134+
private ObjectNode toObjectNode(MultiValueMap<String, String> params, Action<? super ImmutableMap.Builder<String, Object>> defaults, Predicate<String> paramFilter) throws Exception {
135+
Map<String, Object> mergedParams = new HashMap<>(defaults.with(ImmutableMap.builder()).build());
136+
filterAndMerge(params, mergedParams, paramFilter);
137+
return objectMapper.valueToTree(mergedParams);
138+
}
139+
140+
private static void filterAndMerge(MultiValueMap<String, String> params, Map<String, Object> defaults, Predicate<String> filter) {
141+
params.asMultimap().asMap().forEach((name, values) -> {
142+
if (!isEmptyAndHasDefault(name, values, defaults) && !filter.test(name)) {
143+
defaults.put(name, extractSingleValueIfPossible(values));
144+
}
145+
});
146+
}
147+
148+
private static boolean isEmptyAndHasDefault(String name, Collection<String> values, Map<String, Object> defaults) {
149+
// STUB - This is to make the compiler happy
150+
return false;
151+
}
152+
153+
private static Object extractSingleValueIfPossible(Collection<String> values) {
154+
return values.size() == 1 ? values.iterator().next() : ImmutableList.copyOf(values);
155+
}
156+
157+
private static class UploadedFileWrapper implements JsonSerializable {
158+
159+
private final UploadedFile file;
160+
161+
private UploadedFileWrapper(UploadedFile file) {
162+
this.file = file;
163+
}
164+
165+
@Override
166+
public void serialize(Object gen, Object serializers) throws IOException {
167+
// empty
168+
}
169+
170+
@Override
171+
public void serializeWithType(Object gen, Object serializers, Object typeSer) throws IOException {
172+
// empty
173+
}
174+
}
175+
}

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

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -101,8 +101,12 @@ void test5(Context ctx) {
101101
sink(form.get("questionable_parameter")); //$hasTaintFlow
102102
sink(form.getAll().get("questionable_parameter").get(0)); //$hasTaintFlow
103103
sink(form.getAll("questionable_parameter").get(0)); //$hasTaintFlow
104-
sink(form.asMultimap().get("questionable_parameter")); //$hasTaintFlow // fails!
105-
sink(form.asMultimap().asMap()); //$hasTaintFlow // fails!
104+
sink(form.asMultimap().get("questionable_parameter")); //$hasTaintFlow
105+
sink(form.asMultimap().asMap()); //$hasTaintFlow
106+
form.asMultimap().asMap().forEach((name, values) -> {
107+
sink(name); //$hasTaintFlow
108+
sink(values); //$hasTaintFlow
109+
});
106110
});
107111
}
108112

java/ql/test/stubs/jackson-core-2.12/com/fasterxml/jackson/core/TreeNode.java

Lines changed: 6 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

java/ql/test/stubs/jackson-databind-2.12/com/fasterxml/jackson/databind/JsonSerializable.java

Lines changed: 10 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

java/ql/test/stubs/jackson-databind-2.12/com/fasterxml/jackson/databind/node/ArrayNode.java

Lines changed: 10 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

java/ql/test/stubs/jackson-databind-2.12/com/fasterxml/jackson/databind/node/BaseJsonNode.java

Lines changed: 7 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

java/ql/test/stubs/jackson-databind-2.12/com/fasterxml/jackson/databind/node/ContainerNode.java

Lines changed: 5 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

java/ql/test/stubs/jackson-databind-2.12/com/fasterxml/jackson/databind/node/ObjectNode.java

Lines changed: 9 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

java/ql/test/stubs/jackson-databind-2.12/com/fasterxml/jackson/databind/node/POJONode.java

Lines changed: 76 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)