Skip to content

Commit c97a2c4

Browse files
committed
Attempt at identifying behavior of query extract/parsing in Jetty 11
1 parent c9200a5 commit c97a2c4

File tree

1 file changed

+148
-0
lines changed

1 file changed

+148
-0
lines changed

jetty-server/src/test/java/org/eclipse/jetty/server/RequestTest.java

+148
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
import java.time.Duration;
3131
import java.util.ArrayList;
3232
import java.util.Arrays;
33+
import java.util.Collections;
3334
import java.util.EnumSet;
3435
import java.util.Enumeration;
3536
import java.util.List;
@@ -88,12 +89,15 @@
8889
import org.junit.jupiter.api.Test;
8990
import org.junit.jupiter.api.extension.ExtendWith;
9091
import org.junit.jupiter.params.ParameterizedTest;
92+
import org.junit.jupiter.params.provider.Arguments;
93+
import org.junit.jupiter.params.provider.MethodSource;
9194
import org.junit.jupiter.params.provider.ValueSource;
9295
import org.slf4j.Logger;
9396
import org.slf4j.LoggerFactory;
9497

9598
import static org.hamcrest.MatcherAssert.assertThat;
9699
import static org.hamcrest.Matchers.containsString;
100+
import static org.hamcrest.Matchers.hasItem;
97101
import static org.hamcrest.Matchers.is;
98102
import static org.hamcrest.Matchers.not;
99103
import static org.hamcrest.Matchers.nullValue;
@@ -667,6 +671,149 @@ public void testBadUtf8ParamExtraction() throws Exception
667671
assertThat(responses, startsWith("HTTP/1.1 200"));
668672
}
669673

674+
public static Stream<Arguments> queryBehaviors()
675+
{
676+
List<Arguments> cases = new ArrayList<>();
677+
678+
// Normal cases
679+
cases.add(Arguments.of("param=aaa", 200, "param", "aaa"));
680+
cases.add(Arguments.of("param=aaa&other=foo", 200, "param", "aaa"));
681+
cases.add(Arguments.of("param=", 200, "param", ""));
682+
cases.add(Arguments.of("param=&other=foo", 200, "param", ""));
683+
cases.add(Arguments.of("param=%E2%9C%94", 200, "param", "✔"));
684+
cases.add(Arguments.of("param=%E2%9C%94&other=foo", 200, "param", "✔"));
685+
686+
// Truncated / Insufficient Hex cases
687+
cases.add(Arguments.of("param=%E2%9C%9", 400, "param", ""));
688+
cases.add(Arguments.of("param=%E2%9C%9&other=foo", 400, "param", ""));
689+
cases.add(Arguments.of("param=%E2%9C%", 400, "param", ""));
690+
cases.add(Arguments.of("param=%E2%9C%&other=foo", 400, "param", ""));
691+
cases.add(Arguments.of("param=%E2%9C", 200, "param", "�"));
692+
cases.add(Arguments.of("param=%E2%9C&other=foo", 200, "param", "�"));
693+
cases.add(Arguments.of("param=%E2%9", 400, "param", ""));
694+
cases.add(Arguments.of("param=%E2%9&other=foo", 400, "param", ""));
695+
cases.add(Arguments.of("param=%E2%", 400, "param", ""));
696+
cases.add(Arguments.of("param=%E2%&other=foo", 400, "param", ""));
697+
cases.add(Arguments.of("param=%E2", 200, "param", "�"));
698+
cases.add(Arguments.of("param=%E2&other=foo", 200, "param", "�"));
699+
700+
// Tokenized cases
701+
cases.add(Arguments.of("param=%%TOK%%", 400, "param", ""));
702+
cases.add(Arguments.of("param=%%TOK%%&other=foo", 400, "param", ""));
703+
704+
// Bad Hex
705+
cases.add(Arguments.of("param=%xx", 400, "param", ""));
706+
cases.add(Arguments.of("param=%xx&other=foo", 400, "param", ""));
707+
708+
// Overlong UTF-8 Encoding
709+
cases.add(Arguments.of("param=%C0%AF", 400, "param", ""));
710+
cases.add(Arguments.of("param=%C0%AF&other=foo", 400, "param", ""));
711+
712+
// Out of range
713+
cases.add(Arguments.of("param=%F4%90%80%80", 400, "param", ""));
714+
cases.add(Arguments.of("param=%F4%90%80%80&other=foo", 400, "param", ""));
715+
716+
// Long surrogate
717+
cases.add(Arguments.of("param=%ED%A0%80", 400, "param", ""));
718+
cases.add(Arguments.of("param=%ED%A0%80&other=foo", 400, "param", ""));
719+
720+
// Standalone continuations
721+
cases.add(Arguments.of("param=%80", 400, "param", ""));
722+
cases.add(Arguments.of("param=%80&other=foo", 400, "param", ""));
723+
724+
// Truncated sequence
725+
cases.add(Arguments.of("param=%E2%82", 200, "param", "�"));
726+
cases.add(Arguments.of("param=%E2%82&other=foo", 200, "param", "�"));
727+
728+
// C1 never starts UTF-8
729+
cases.add(Arguments.of("param=%C1%BF", 400, "param", ""));
730+
cases.add(Arguments.of("param=%C1%BF&other=foo", 400, "param", ""));
731+
732+
// E0 must be followed by A0-BF
733+
cases.add(Arguments.of("param=%E0%9F%80", 400, "param", ""));
734+
cases.add(Arguments.of("param=%E0%9F%80&other=foo", 400, "param", ""));
735+
736+
// Community Examples
737+
cases.add(Arguments.of("param=f_%e0%b8", 200, "param", "f_�"));
738+
cases.add(Arguments.of("param=f_%e0%b8&other=foo", 200, "param", "f_�"));
739+
cases.add(Arguments.of("param=%£", 400, "param", ""));
740+
cases.add(Arguments.of("param=%£&other=foo", 400, "param", ""));
741+
742+
// Extra ampersands
743+
cases.add(Arguments.of("param=aaa&&&", 200, "param", "aaa"));
744+
cases.add(Arguments.of("&&&param=aaa", 200, "param", "aaa"));
745+
cases.add(Arguments.of("&&param=aaa&&other=foo", 200, "param", "aaa"));
746+
cases.add(Arguments.of("param=aaa&&other=foo&&", 200, "param", "aaa"));
747+
748+
// Encoded ampersands
749+
cases.add(Arguments.of("param=aaa%26&other=foo", 200, "param", "aaa&"));
750+
cases.add(Arguments.of("param=aaa&%26other=foo", 200, "&other", "foo"));
751+
752+
// pct-encoded parameter names ("帽子" means "hat" in japanese)
753+
cases.add(Arguments.of("%E5%B8%BD%E5%AD%90=Beret", 200, "帽子", "Beret"));
754+
cases.add(Arguments.of("%E5%B8%BD%E5%AD%90=Beret&other=foo", 200, "帽子", "Beret"));
755+
cases.add(Arguments.of("other=foo&%E5%B8%BD%E5%AD%90=Beret", 200, "帽子", "Beret"));
756+
757+
// truncated pct-encoded parameter names
758+
cases.add(Arguments.of("%E5%B8%BD%E5%AD%9=Beret", 200, "帽孝Beret", ""));
759+
cases.add(Arguments.of("%E5%B8%BD%E5%AD%=Beret", 400, "帽子", ""));
760+
cases.add(Arguments.of("%E5%B8%BD%E5%AD=Beret", 200, "帽�", "Beret"));
761+
cases.add(Arguments.of("%E5%B8%BD%E5%AD%9=Beret&other=foo", 200, "帽孝Beret", ""));
762+
cases.add(Arguments.of("%E5%B8%BD%E5%AD%=Beret&other=foo", 400, "帽子", ""));
763+
cases.add(Arguments.of("%E5%B8%BD%E5%AD=Beret&other=foo", 200, "帽�", "Beret"));
764+
765+
// raw unicode parameter names (strange replacement logic here)
766+
cases.add(Arguments.of("€=currency", 200, "?", "currency"));
767+
cases.add(Arguments.of("帽子=Beret", 200, "??", "Beret"));
768+
769+
// invalid pct-encoded parameter name
770+
cases.add(Arguments.of("foo%xx=abc", 400, "foo�", ""));
771+
cases.add(Arguments.of("foo%x=abc", 400, "foo�", ""));
772+
cases.add(Arguments.of("foo%=abc", 400, "foo�", ""));
773+
774+
return cases.stream();
775+
}
776+
777+
@ParameterizedTest
778+
@MethodSource("queryBehaviors")
779+
public void testQueryExtractionBehavior(String inputQuery, int expectedStatus, String expectedKey, String expectedValue) throws Exception
780+
{
781+
_handler._checker = (request, response) ->
782+
{
783+
switch (expectedStatus)
784+
{
785+
case 200:
786+
{
787+
// Verify that parameter exists
788+
List<String> paramNames = Collections.list(request.getParameterNames());
789+
assertThat(paramNames, hasItem(expectedKey));
790+
791+
// Get value
792+
String value = request.getParameter(expectedKey);
793+
assertThat(value, is(expectedValue));
794+
response.setStatus(200);
795+
return true;
796+
}
797+
default:
798+
{
799+
// We are expecting an exception, rethrow it.
800+
throw assertThrows(RuntimeException.class, () -> request.getParameter(expectedKey));
801+
}
802+
}
803+
};
804+
805+
//Send a request with query string with illegal hex code to cause
806+
//an exception parsing the params
807+
String rawRequest = String.format("GET /?%s HTTP/1.1\r\n" +
808+
"Host: whatever\r\n" +
809+
"Connection: close\n" +
810+
"\n", inputQuery);
811+
812+
String rawResponse = _connector.getResponse(rawRequest);
813+
HttpTester.Response response = HttpTester.parseResponse(rawResponse);
814+
assertThat(response.getStatus(), is(expectedStatus));
815+
}
816+
670817
@Test
671818
public void testEncodedParamExtraction() throws Exception
672819
{
@@ -2412,3 +2559,4 @@ public void handle(String target, Request baseRequest, HttpServletRequest reques
24122559
}
24132560
}
24142561
}
2562+

0 commit comments

Comments
 (0)