Add full integration test for Ratpack example

Signed-off-by: Jonathan Leitschuh <Jonathan.Leitschuh@gmail.com>
This commit is contained in:
Jonathan Leitschuh
2021-10-11 17:24:35 -04:00
parent ebbbda70c0
commit 23e60e2c52
13 changed files with 369 additions and 5 deletions

View File

@@ -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
//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

View File

@@ -0,0 +1,175 @@
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.fasterxml.jackson.databind.node.POJONode;
import com.fasterxml.jackson.databind.JsonSerializable;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.JsonNode;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import ratpack.core.handling.Context;
import ratpack.core.http.TypedData;
import ratpack.core.form.Form;
import ratpack.core.form.UploadedFile;
import ratpack.core.parse.Parse;
import ratpack.exec.Promise;
import ratpack.func.Action;
import ratpack.func.Function;
import ratpack.func.MultiValueMap;
import java.io.OutputStream;
import java.io.IOException;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.List;
import java.util.function.Predicate;
import static ratpack.jackson.Jackson.jsonNode;
class IntegrationTest {
static class Pojo {
String value;
String getValue() {
return value;
}
}
private final ObjectMapper objectMapper = new ObjectMapper();
void sink(Object o) {}
String taint() {
return null;
}
void test1(Context ctx) {
bindJson(ctx, Pojo.class)
.then(pojo ->{
sink(pojo); //$hasTaintFlow
sink(pojo.value); //$hasTaintFlow
sink(pojo.getValue()); //$hasTaintFlow
});
}
void test2(Context ctx) {
bindForm(ctx, Pojo.class, defaults -> defaults.put("another", "potato"))
.then(pojo ->{
sink(pojo); //$hasTaintFlow
sink(pojo.value); //$hasTaintFlow
sink(pojo.getValue()); //$hasTaintFlow
});
}
void test3() {
Object value = extractSingleValueIfPossible(ImmutableList.of("a", taint()));
sink(value); //$hasTaintFlow
}
void test4(Context ctx) {
parseToForm(ctx, Pojo.class)
.map(pojoForm -> {
Map<String, Object> mergedParams = new HashMap<>();
filterAndMerge(pojoForm, mergedParams, name -> false);
return mergedParams;
}).then(pojoMap -> {
sink(pojoMap); //$hasTaintFlow
sink(pojoMap.get("value")); //$hasTaintFlow
});
}
private <T> Promise<T> bindJson(Context ctx, Class<T> type) {
return ctx.getRequest().getBody()
.map(data -> {
String dataText = data.getText();
try {
return ctx.parse(data, jsonNode(objectMapper));
} catch (Exception e) {
String msg = "Unable to parse json data while binding type " + type.getCanonicalName() + " [jsonData: " + dataText + "]";
throw new RuntimeException(msg, e);
}
})
.map(json ->
bind(ctx, json, type)
);
}
private <T> T bind(Context ctx, JsonNode input, Class<T> type) {
T value;
try {
value = objectMapper.convertValue(input, type);
} catch (Exception e) {
throw new RuntimeException("Failed to convert input to " + type.getName(), e);
}
return value;
}
private static Promise<Form> parseToForm(Context ctx, Class<?> type) {
return ctx.getRequest().getBody()
.map(data -> {
try {
return ctx.parse(data, Form.form());
} catch (Exception e) {
String msg = "Unable to parse form data while binding type " + type.getCanonicalName() + " [formData: " + data.getText() + "]";
throw new RuntimeException(msg, e);
}
});
}
private <T> Promise<T> bindForm(Context ctx, Class<T> type, Action<? super ImmutableMap.Builder<String, Object>> defaults) {
return parseToForm(ctx, type)
.map(form -> {
ObjectNode input = toObjectNode(form, defaults, s -> false);
Map<String, List<UploadedFile>> filesMap = form.files().getAll();
filesMap.forEach((name, files) -> {
ArrayNode array = input.putArray(name);
files.forEach(f -> array.add(new POJONode(new UploadedFileWrapper(f))));
});
return bind(ctx, input, type);
});
}
private ObjectNode toObjectNode(MultiValueMap<String, String> params, Action<? super ImmutableMap.Builder<String, Object>> defaults, Predicate<String> paramFilter) throws Exception {
Map<String, Object> mergedParams = new HashMap<>(defaults.with(ImmutableMap.builder()).build());
filterAndMerge(params, mergedParams, paramFilter);
return objectMapper.valueToTree(mergedParams);
}
private static void filterAndMerge(MultiValueMap<String, String> params, Map<String, Object> defaults, Predicate<String> filter) {
params.asMultimap().asMap().forEach((name, values) -> {
if (!isEmptyAndHasDefault(name, values, defaults) && !filter.test(name)) {
defaults.put(name, extractSingleValueIfPossible(values));
}
});
}
private static boolean isEmptyAndHasDefault(String name, Collection<String> values, Map<String, Object> defaults) {
// STUB - This is to make the compiler happy
return false;
}
private static Object extractSingleValueIfPossible(Collection<String> values) {
return values.size() == 1 ? values.iterator().next() : ImmutableList.copyOf(values);
}
private static class UploadedFileWrapper implements JsonSerializable {
private final UploadedFile file;
private UploadedFileWrapper(UploadedFile file) {
this.file = file;
}
@Override
public void serialize(Object gen, Object serializers) throws IOException {
// empty
}
@Override
public void serializeWithType(Object gen, Object serializers, Object typeSer) throws IOException {
// empty
}
}
}

View File

@@ -101,8 +101,12 @@ class Resource {
sink(form.get("questionable_parameter")); //$hasTaintFlow
sink(form.getAll().get("questionable_parameter").get(0)); //$hasTaintFlow
sink(form.getAll("questionable_parameter").get(0)); //$hasTaintFlow
sink(form.asMultimap().get("questionable_parameter")); //$hasTaintFlow // fails!
sink(form.asMultimap().asMap()); //$hasTaintFlow // fails!
sink(form.asMultimap().get("questionable_parameter")); //$hasTaintFlow
sink(form.asMultimap().asMap()); //$hasTaintFlow
form.asMultimap().asMap().forEach((name, values) -> {
sink(name); //$hasTaintFlow
sink(values); //$hasTaintFlow
});
});
}

View File

@@ -9,7 +9,9 @@ package com.fasterxml.jackson.core;
import java.util.Iterator;
public interface TreeNode {
JsonParser.NumberType numberType();
default JsonParser.NumberType numberType() {
return null;
}
int size();
@@ -35,6 +37,8 @@ public interface TreeNode {
TreeNode at(String jsonPointerExpression) throws IllegalArgumentException;
JsonParser traverse();
default JsonParser traverse() {
return null;
}
}

View File

@@ -0,0 +1,10 @@
package com.fasterxml.jackson.databind;
import java.io.IOException;
// This interface does not actually have these types.. This is a significantly oversimplified stub.
public interface JsonSerializable {
public void serialize(Object gen, Object serializers) throws IOException;
public void serializeWithType(Object gen, Object serializers, Object typeSer) throws IOException;
}

View File

@@ -0,0 +1,10 @@
package com.fasterxml.jackson.databind.node;
import com.fasterxml.jackson.databind.JsonNode;
public abstract class ArrayNode extends ContainerNode<ArrayNode> {
public ArrayNode add(JsonNode value) {
return null;
}
}

View File

@@ -0,0 +1,7 @@
package com.fasterxml.jackson.databind.node;
import com.fasterxml.jackson.databind.JsonNode;
public abstract class BaseJsonNode extends JsonNode {
}

View File

@@ -0,0 +1,5 @@
package com.fasterxml.jackson.databind.node;
public abstract class ContainerNode<T extends ContainerNode<T>> extends BaseJsonNode {
}

View File

@@ -0,0 +1,9 @@
package com.fasterxml.jackson.databind.node;
public abstract class ObjectNode extends ContainerNode<ObjectNode> {
public ArrayNode putArray(String propertyName)
{
return null;
}
}

View File

@@ -0,0 +1,76 @@
package com.fasterxml.jackson.databind.node;
import com.fasterxml.jackson.databind.JsonNode;
import java.util.List;
public class POJONode extends ValueNode {
protected final Object _value;
public POJONode(Object v) {
_value = v;
}
@Override
public boolean equals(Object o) {
return false;
}
public String toString() {
return null;
}
@Override
public <T extends JsonNode> T deepCopy() {
return null;
}
@Override
public JsonNode get(int index) {
return null;
}
@Override
public JsonNode path(String fieldName) {
return null;
}
@Override
public JsonNode path(int index) {
return null;
}
@Override
public String asText() {
return null;
}
@Override
public JsonNode findValue(String fieldName) {
return null;
}
@Override
public JsonNode findPath(String fieldName) {
return null;
}
@Override
public JsonNode findParent(String fieldName) {
return null;
}
@Override
public List<JsonNode> findValues(String fieldName, List<JsonNode> foundSoFar) {
return null;
}
@Override
public List<String> findValuesAsText(String fieldName, List<String> foundSoFar) {
return null;
}
@Override
public List<JsonNode> findParents(String fieldName, List<JsonNode> foundSoFar) {
return null;
}
}

View File

@@ -0,0 +1,5 @@
package com.fasterxml.jackson.databind.node;
public abstract class ValueNode extends BaseJsonNode {
}

View File

@@ -0,0 +1,32 @@
/*
* Copyright 2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ratpack.jackson;
import ratpack.func.Nullable;
import ratpack.core.parse.Parse;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
public abstract class Jackson {
private Jackson() {
}
public static Parse<JsonNode, JsonParseOpts> jsonNode(@Nullable ObjectMapper objectMapper) {
return null;
}
}

View File

@@ -0,0 +1,27 @@
/*
* Copyright 2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ratpack.jackson;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.util.Optional;
public interface JsonParseOpts {
Optional<ObjectMapper> getObjectMapper();
}