-
Notifications
You must be signed in to change notification settings - Fork 1.7k
Java: Simple support for Ratpack HTTP Framework #4991
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
aschackmull
merged 29 commits into
github:main
from
JLLeitschuh:feat/JLL/early_ratpack_support
Oct 27, 2021
Merged
Changes from 23 commits
Commits
Show all changes
29 commits
Select commit
Hold shift + click to select a range
f5c3723
Java: Simple support for Ratpack HTTP Framework
JLLeitschuh dabf00e
Add Tests to Ratpack Framework Support
JLLeitschuh 170657b
Add additional Ratpack test and improve Promise based dataflow tracking
JLLeitschuh b2ad128
Refactors Ratpack lambda taint tracking to use generic API
JLLeitschuh b2e3df2
Add support for Promise.value and Promise::flatMap
JLLeitschuh 18c74c5
Simplify Ratpack API using standard abstract classes
JLLeitschuh 4f658df
Apply suggestions from code review
JLLeitschuh 563e569
Refactor Ratpack to use CSV format
JLLeitschuh ac185d9
Remove RatpackGetRequestDataMethod
JLLeitschuh a3b1736
Ratpack improve support for parsing types
JLLeitschuh cdfdcc6
Ratpack fix formatting and non-ascii characters
JLLeitschuh b9dc3d0
Ratpack: Better support for Promise API
JLLeitschuh 901631c
Ratpack Promise add support for `apply` method
JLLeitschuh af90b00
Ratpack: Release note and typo fix
JLLeitschuh 6497a61
Ratpack: Drop support for `flatMap` like methods
JLLeitschuh 4f90f0a
Begin refactoring Ratpack to use functional taint tracking
JLLeitschuh 6562ac3
Ratpack conversion to new lambda model
JLLeitschuh fe374f5
Ratpack: Add support for `Promise::apply`
JLLeitschuh ebbbda7
Ratpack tests all passing
JLLeitschuh 23e60e2
Add full integration test for Ratpack example
JLLeitschuh 8fecc15
Add support for Map.forEach
JLLeitschuh 5a2bdc9
Jackson taint tracking of elements
JLLeitschuh db2892b
Resove taint tracking issues from `asMultimap`
JLLeitschuh 8231907
Ratpack code cleanup from code review
JLLeitschuh 584c27a
Move CollectionPassingTest to correct directory
JLLeitschuh cce3aad
Remove non-ASCII characters from Handler.java
JLLeitschuh 5eb2839
Remove non-ASCII characters from Promise.java
JLLeitschuh ebe2c26
Remove the last non-ascii quote from Promise
JLLeitschuh 21aeee6
Actually remove the last non-ascii quote from Promise
JLLeitschuh File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
lgtm,codescanning | ||
* Add support for [Ratpack](https://ratpack.io/) HTTP framework. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
87 changes: 87 additions & 0 deletions
87
java/ql/lib/semmle/code/java/frameworks/ratpack/Ratpack.qll
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,87 @@ | ||
/** | ||
* Provides classes and predicates related to `ratpack.*`. | ||
*/ | ||
|
||
import java | ||
private import semmle.code.java.dataflow.DataFlow | ||
private import semmle.code.java.dataflow.FlowSteps | ||
private import semmle.code.java.dataflow.ExternalFlow | ||
|
||
/** | ||
* Ratpack methods that access user-supplied request data. | ||
*/ | ||
private class RatpackHttpSource extends SourceModelCsv { | ||
override predicate row(string row) { | ||
row = | ||
["ratpack.http;", "ratpack.core.http;"] + | ||
[ | ||
"Request;true;getContentLength;;;ReturnValue;remote", | ||
"Request;true;getCookies;;;ReturnValue;remote", | ||
"Request;true;oneCookie;;;ReturnValue;remote", | ||
"Request;true;getHeaders;;;ReturnValue;remote", | ||
"Request;true;getPath;;;ReturnValue;remote", "Request;true;getQuery;;;ReturnValue;remote", | ||
"Request;true;getQueryParams;;;ReturnValue;remote", | ||
"Request;true;getRawUri;;;ReturnValue;remote", "Request;true;getUri;;;ReturnValue;remote", | ||
"Request;true;getBody;;;ReturnValue;remote" | ||
] | ||
or | ||
// All Context#parse methods that return a Promise are remote flow sources. | ||
row = | ||
["ratpack.handling;", "ratpack.core.handling;"] + "Context;true;parse;" + | ||
[ | ||
"(java.lang.Class);", "(com.google.common.reflect.TypeToken);", | ||
"(java.lang.Class,java.lang.Object);", | ||
"(com.google.common.reflect.TypeToken,java.lang.Object);", "(ratpack.core.parse.Parse);", | ||
"(ratpack.parse.Parse);" | ||
] + ";ReturnValue;remote" | ||
} | ||
} | ||
|
||
/** | ||
* Ratpack methods that propagate user-supplied request data as tainted. | ||
*/ | ||
private class RatpackModel extends SummaryModelCsv { | ||
override predicate row(string row) { | ||
row = | ||
["ratpack.http;", "ratpack.core.http;"] + | ||
[ | ||
"TypedData;true;getBuffer;;;Argument[-1];ReturnValue;taint", | ||
"TypedData;true;getBytes;;;Argument[-1];ReturnValue;taint", | ||
"TypedData;true;getContentType;;;Argument[-1];ReturnValue;taint", | ||
"TypedData;true;getInputStream;;;Argument[-1];ReturnValue;taint", | ||
"TypedData;true;getText;;;Argument[-1];ReturnValue;taint", | ||
"TypedData;true;writeTo;;;Argument[-1];Argument[0];taint", | ||
"Headers;true;get;;;Argument[-1];ReturnValue;taint", | ||
"Headers;true;getAll;;;Argument[-1];ReturnValue;taint", | ||
"Headers;true;getNames;;;Argument[-1];ReturnValue;taint", | ||
"Headers;true;asMultiValueMap;;;Argument[-1];ReturnValue;taint" | ||
] | ||
or | ||
row = | ||
["ratpack.form;", "ratpack.core.form;"] + | ||
[ | ||
"UploadedFile;true;getFileName;;;Argument[-1];ReturnValue;taint", | ||
"Form;true;file;;;Argument[-1];ReturnValue;taint", | ||
"Form;true;files;;;Argument[-1];ReturnValue;taint" | ||
] | ||
or | ||
row = | ||
["ratpack.handling;", "ratpack.core.handling;"] + | ||
[ | ||
"Context;true;parse;(ratpack.http.TypedData,ratpack.parse.Parse);;Argument[0];ReturnValue;taint", | ||
"Context;true;parse;(ratpack.core.http.TypedData,ratpack.core.parse.Parse);;Argument[0];ReturnValue;taint", | ||
"Context;true;parse;(ratpack.core.http.TypedData,ratpack.core.parse.Parse);;Argument[0];MapKey of ReturnValue;taint", | ||
"Context;true;parse;(ratpack.core.http.TypedData,ratpack.core.parse.Parse);;Argument[0];MapValue of ReturnValue;taint" | ||
] | ||
or | ||
row = | ||
["ratpack.util;", "ratpack.func;"] + | ||
[ | ||
"MultiValueMap;true;getAll;;;MapKey of Argument[-1];MapKey of ReturnValue;value", | ||
"MultiValueMap;true;getAll;();;MapValue of Argument[-1];Element of MapValue of ReturnValue;value", | ||
"MultiValueMap;true;getAll;(Object);;MapValue of Argument[-1];Element of ReturnValue;value", | ||
"MultiValueMap;true;asMultimap;;;MapKey of Argument[-1];MapKey of ReturnValue;value", | ||
"MultiValueMap;true;asMultimap;;;MapValue of Argument[-1];MapValue of ReturnValue;value" | ||
] | ||
} | ||
} |
80 changes: 80 additions & 0 deletions
80
java/ql/lib/semmle/code/java/frameworks/ratpack/RatpackExec.qll
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,80 @@ | ||
/** | ||
* Provides classes and predicates related to `ratpack.exec.*`. | ||
*/ | ||
|
||
import java | ||
private import semmle.code.java.dataflow.DataFlow | ||
private import semmle.code.java.dataflow.FlowSteps | ||
private import semmle.code.java.dataflow.ExternalFlow | ||
|
||
/** | ||
* Model for Ratpack `Promise` methods. | ||
*/ | ||
private class RatpackExecModel extends SummaryModelCsv { | ||
override predicate row(string row) { | ||
//"namespace;type;overrides;name;signature;ext;inputspec;outputspec;kind", | ||
row = | ||
["ratpack.exec;Promise;true;"] + | ||
[ | ||
// `Promise` creation methods | ||
"value;;;Argument[0];Element of ReturnValue;value", | ||
"flatten;;;Element of ReturnValue of Argument[0];Element of ReturnValue;value", | ||
"sync;;;ReturnValue of Argument[0];Element of ReturnValue;value", | ||
// `Promise` value transformation methods | ||
"map;;;Element of Argument[-1];Parameter[0] of Argument[0];value", | ||
"map;;;ReturnValue of Argument[0];Element of ReturnValue;value", | ||
"blockingMap;;;Element of Argument[-1];Parameter[0] of Argument[0];value", | ||
"blockingMap;;;ReturnValue of Argument[0];Element of ReturnValue;value", | ||
"mapError;;;ReturnValue of Argument[1];Element of ReturnValue;value", | ||
// `apply` passes the qualifier to the function as the first argument | ||
"apply;;;Element of Argument[-1];Element of Parameter[0] of Argument[0];value", | ||
"apply;;;Element of ReturnValue of Argument[0];Element of ReturnValue;value", | ||
// `Promise` termination method | ||
"then;;;Element of Argument[-1];Parameter[0] of Argument[0];value", | ||
// 'next' accesses qualfier the 'Promise' value and also returns the qualifier | ||
"next;;;Element of Argument[-1];Parameter[0] of Argument[0];value", | ||
"next;;;Argument[-1];ReturnValue;value", | ||
// 'cacheIf' accesses qualfier the 'Promise' value and also returns the qualifier | ||
"cacheIf;;;Element of Argument[-1];Parameter[0] of Argument[0];value", | ||
"cacheIf;;;Argument[-1];ReturnValue;value", | ||
// 'route' accesses qualfier the 'Promise' value, and conditionally returns the qualifier or | ||
// the result of the second argument | ||
"route;;;Element of Argument[-1];Parameter[0] of Argument[0];value", | ||
"route;;;Element of Argument[-1];Parameter[0] of Argument[1];value", | ||
"route;;;Argument[-1];ReturnValue;value", | ||
// `flatMap` type methods return their returned `Promise` | ||
"flatMap;;;Element of Argument[-1];Parameter[0] of Argument[0];value", | ||
"flatMap;;;Element of ReturnValue of Argument[0];Element of ReturnValue;value", | ||
"flatMapError;;;Element of ReturnValue of Argument[1];Element of ReturnValue;value", | ||
// `mapIf` methods conditionally map their values, or return themselves | ||
"mapIf;;;Element of Argument[-1];Parameter[0] of Argument[0];value", | ||
"mapIf;;;Element of Argument[-1];Parameter[0] of Argument[1];value", | ||
"mapIf;;;Element of Argument[-1];Parameter[0] of Argument[2];value", | ||
"mapIf;;;ReturnValue of Argument[1];Element of ReturnValue;value", | ||
"mapIf;;;ReturnValue of Argument[2];Element of ReturnValue;value" | ||
] | ||
} | ||
} | ||
|
||
/** A reference type that extends a parameterization the Promise type. */ | ||
private class RatpackPromise extends RefType { | ||
RatpackPromise() { | ||
getSourceDeclaration().getASourceSupertype*().hasQualifiedName("ratpack.exec", "Promise") | ||
} | ||
} | ||
|
||
/** | ||
* Ratpack `Promise` method that will return `this`. | ||
*/ | ||
private class RatpackPromiseFluentMethod extends FluentMethod { | ||
RatpackPromiseFluentMethod() { | ||
getDeclaringType() instanceof RatpackPromise and | ||
not isStatic() and | ||
// It's generally safe to assume that if the return type exactly matches the declaring type, `this` will be returned. | ||
exists(ParameterizedType t | | ||
t instanceof RatpackPromise and | ||
t = getDeclaringType() and | ||
JLLeitschuh marked this conversation as resolved.
Show resolved
Hide resolved
|
||
t = getReturnType() | ||
) | ||
} | ||
} |
70 changes: 70 additions & 0 deletions
70
java/ql/test/library-tests/frameworks/ratpack/CollectionPassingTest.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
import com.google.common.collect.ImmutableList; | ||
import com.google.common.collect.ImmutableMap; | ||
|
||
import ratpack.core.handling.Context; | ||
import ratpack.core.form.Form; | ||
|
||
import java.util.Collection; | ||
import java.util.HashMap; | ||
import java.util.Map; | ||
import java.util.List; | ||
import java.util.function.Predicate; | ||
|
||
public class CollectionPassingTest { | ||
|
||
void sink(Object o) {} | ||
|
||
String taint() { | ||
return null; | ||
} | ||
|
||
void test_1(Context ctx) { | ||
// Given | ||
ctx | ||
.getRequest() | ||
.getBody() | ||
.map(data -> ctx.parse(data, Form.form())) | ||
.then(form -> { | ||
// When | ||
Map<String, Object> pojoMap = new HashMap<>(); | ||
merge(form.asMultimap().asMap(), pojoMap); | ||
// Then | ||
sink(pojoMap.get("value")); //$hasTaintFlow | ||
pojoMap.forEach((key, value) -> { | ||
sink(value); //$hasTaintFlow | ||
List<Object> values = (List<Object>) value; | ||
sink(values.get(0)); //$hasTaintFlow | ||
}); | ||
}); | ||
} | ||
|
||
void test_2() { | ||
// Given | ||
Map<String, Collection<String>> taintedMap = new HashMap<>(); | ||
taintedMap.put("value", ImmutableList.of(taint())); | ||
Map<String, Object> pojoMap = new HashMap<>(); | ||
// When | ||
merge(taintedMap, pojoMap); | ||
// Then | ||
sink(pojoMap.get("value")); //$hasTaintFlow | ||
pojoMap.forEach((key, value) -> { | ||
sink(value); //$hasTaintFlow | ||
List<Object> values = (List<Object>) value; | ||
sink(values.get(0)); //$hasTaintFlow | ||
}); | ||
} | ||
|
||
|
||
private static void merge(Map<String, Collection<String>> params, Map<String, Object> defaults) { | ||
for(Map.Entry<String, Collection<String>> entry : params.entrySet()) { | ||
String name = entry.getKey(); | ||
Collection<String> values = entry.getValue(); | ||
defaults.put(name, extractSingleValueIfPossible(values)); | ||
} | ||
} | ||
|
||
private static Object extractSingleValueIfPossible(Collection<String> values) { | ||
return values.size() == 1 ? values.iterator().next() : ImmutableList.copyOf(values); | ||
} | ||
|
||
} |
Empty file.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
import java | ||
import semmle.code.java.dataflow.TaintTracking | ||
import semmle.code.java.dataflow.FlowSources | ||
import TestUtilities.InlineExpectationsTest | ||
|
||
class Conf extends TaintTracking::Configuration { | ||
Conf() { this = "qltest:frameworks:ratpack" } | ||
|
||
override predicate isSource(DataFlow::Node n) { | ||
n.asExpr().(MethodAccess).getMethod().hasName("taint") | ||
or | ||
n instanceof RemoteFlowSource | ||
} | ||
|
||
override predicate isSink(DataFlow::Node n) { | ||
exists(MethodAccess ma | ma.getMethod().hasName("sink") | n.asExpr() = ma.getAnArgument()) | ||
} | ||
} | ||
|
||
class HasFlowTest extends InlineExpectationsTest { | ||
HasFlowTest() { this = "HasFlowTest" } | ||
|
||
override string getARelevantTag() { result = "hasTaintFlow" } | ||
|
||
override predicate hasActualResult(Location location, string element, string tag, string value) { | ||
tag = "hasTaintFlow" and | ||
exists(DataFlow::Node src, DataFlow::Node sink, Conf conf | conf.hasFlow(src, sink) | | ||
sink.getLocation() = location and | ||
element = sink.toString() and | ||
value = "" | ||
) | ||
} | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +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 |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.