Issue #1546 - Adding tests for CookieCutter
+ CookieCutterTest adds basic examples from RFCs + CookieCutter_LenientTest adds many examples of problematic lenient cookie parsing.
This commit is contained in:
parent
991f9a1edb
commit
dea2c340a4
|
@ -0,0 +1,200 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
|
||||
// ------------------------------------------------------------------------
|
||||
// All rights reserved. This program and the accompanying materials
|
||||
// are made available under the terms of the Eclipse Public License v1.0
|
||||
// and Apache License v2.0 which accompanies this distribution.
|
||||
//
|
||||
// The Eclipse Public License is available at
|
||||
// http://www.eclipse.org/legal/epl-v10.html
|
||||
//
|
||||
// The Apache License v2.0 is available at
|
||||
// http://www.opensource.org/licenses/apache2.0.php
|
||||
//
|
||||
// You may elect to redistribute this code under either of these licenses.
|
||||
// ========================================================================
|
||||
//
|
||||
|
||||
package org.eclipse.jetty.server;
|
||||
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
import javax.servlet.http.Cookie;
|
||||
|
||||
import org.hamcrest.Matcher;
|
||||
import org.junit.Test;
|
||||
|
||||
public class CookieCutterTest
|
||||
{
|
||||
private Cookie[] parseCookieHeaders(String... headers)
|
||||
{
|
||||
CookieCutter cutter = new CookieCutter();
|
||||
for (String header : headers)
|
||||
{
|
||||
cutter.addCookieField(header);
|
||||
}
|
||||
return cutter.getCookies();
|
||||
}
|
||||
|
||||
private void assertCookie(String prefix, Cookie cookie,
|
||||
String expectedName,
|
||||
String expectedValue,
|
||||
int expectedVersion,
|
||||
String expectedPath)
|
||||
{
|
||||
assertThat(prefix + ".name", cookie.getName(), is(expectedName));
|
||||
assertThat(prefix + ".value", cookie.getValue(), is(expectedValue));
|
||||
assertThat(prefix + ".version", cookie.getVersion(), is(expectedVersion));
|
||||
assertThat(prefix + ".path", cookie.getPath(), is(expectedPath));
|
||||
}
|
||||
|
||||
private void assertCookieComment(String prefix, Cookie cookie, Matcher<String> commentMatcher)
|
||||
{
|
||||
assertThat(prefix + ".comment", cookie.getComment(), commentMatcher);
|
||||
}
|
||||
|
||||
/**
|
||||
* Example from RFC2109 and RFC2965
|
||||
*/
|
||||
@Test
|
||||
public void testRFC_Single()
|
||||
{
|
||||
String rawCookie = "$Version=\"1\"; Customer=\"WILE_E_COYOTE\"; $Path=\"/acme\"";
|
||||
|
||||
Cookie cookies[] = parseCookieHeaders(rawCookie);
|
||||
|
||||
assertThat("Cookies.length", cookies.length, is(1));
|
||||
assertCookie("Cookies[0]", cookies[0], "Customer", "WILE_E_COYOTE", 1, "/acme");
|
||||
}
|
||||
|
||||
/**
|
||||
* Example from RFC2109 and RFC2965
|
||||
*/
|
||||
@Test
|
||||
public void testRFC_Double()
|
||||
{
|
||||
String rawCookie = "$Version=\"1\"; " +
|
||||
"Customer=\"WILE_E_COYOTE\"; $Path=\"/acme\"; " +
|
||||
"Part_Number=\"Rocket_Launcher_0001\"; $Path=\"/acme\"";
|
||||
|
||||
Cookie cookies[] = parseCookieHeaders(rawCookie);
|
||||
|
||||
assertThat("Cookies.length", cookies.length, is(2));
|
||||
assertCookie("Cookies[0]", cookies[0], "Customer", "WILE_E_COYOTE", 1, "/acme");
|
||||
assertCookie("Cookies[1]", cookies[1], "Part_Number", "Rocket_Launcher_0001", 1, "/acme");
|
||||
}
|
||||
|
||||
/**
|
||||
* Example from RFC2109 and RFC2965
|
||||
*/
|
||||
@Test
|
||||
public void testRFC_Triple()
|
||||
{
|
||||
String rawCookie = "$Version=\"1\"; " +
|
||||
"Customer=\"WILE_E_COYOTE\"; $Path=\"/acme\"; " +
|
||||
"Part_Number=\"Rocket_Launcher_0001\"; $Path=\"/acme\"; " +
|
||||
"Shipping=\"FedEx\"; $Path=\"/acme\"";
|
||||
|
||||
Cookie cookies[] = parseCookieHeaders(rawCookie);
|
||||
|
||||
assertThat("Cookies.length", cookies.length, is(3));
|
||||
assertCookie("Cookies[0]", cookies[0], "Customer", "WILE_E_COYOTE", 1, "/acme");
|
||||
assertCookie("Cookies[1]", cookies[1], "Part_Number", "Rocket_Launcher_0001", 1, "/acme");
|
||||
assertCookie("Cookies[2]", cookies[2], "Shipping", "FedEx", 1, "/acme");
|
||||
}
|
||||
|
||||
/**
|
||||
* Example from RFC2109 and RFC2965
|
||||
*/
|
||||
@Test
|
||||
public void testRFC_PathExample()
|
||||
{
|
||||
String rawCookie = "$Version=\"1\"; " +
|
||||
"Part_Number=\"Riding_Rocket_0023\"; $Path=\"/acme/ammo\"; " +
|
||||
"Part_Number=\"Rocket_Launcher_0001\"; $Path=\"/acme\"";
|
||||
|
||||
Cookie cookies[] = parseCookieHeaders(rawCookie);
|
||||
|
||||
assertThat("Cookies.length", cookies.length, is(2));
|
||||
assertCookie("Cookies[0]", cookies[0], "Part_Number", "Riding_Rocket_0023", 1, "/acme/ammo");
|
||||
assertCookie("Cookies[1]", cookies[1], "Part_Number", "Rocket_Launcher_0001", 1, "/acme");
|
||||
}
|
||||
|
||||
/**
|
||||
* Example from RFC2109
|
||||
*/
|
||||
@Test
|
||||
public void testRFC2109_CookieSpoofingExample()
|
||||
{
|
||||
String rawCookie = "$Version=\"1\"; " +
|
||||
"session_id=\"1234\"; " +
|
||||
"session_id=\"1111\"; $Domain=\".cracker.edu\"";
|
||||
|
||||
Cookie cookies[] = parseCookieHeaders(rawCookie);
|
||||
|
||||
assertThat("Cookies.length", cookies.length, is(2));
|
||||
assertCookie("Cookies[0]", cookies[0], "session_id", "1234", 1, null);
|
||||
assertCookie("Cookies[1]", cookies[1], "session_id", "1111", 1, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Example from RFC2965
|
||||
*/
|
||||
@Test
|
||||
public void testRFC2965_CookieSpoofingExample()
|
||||
{
|
||||
String rawCookie = "$Version=\"1\"; session_id=\"1234\", " +
|
||||
"$Version=\"1\"; session_id=\"1111\"; $Domain=\".cracker.edu\"";
|
||||
|
||||
Cookie cookies[] = parseCookieHeaders(rawCookie);
|
||||
|
||||
assertThat("Cookies.length", cookies.length, is(2));
|
||||
assertCookie("Cookies[0]", cookies[0], "session_id", "1234", 1, null);
|
||||
assertCookie("Cookies[1]", cookies[1], "session_id", "1111", 1, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Example from RFC6265
|
||||
*/
|
||||
@Test
|
||||
public void testRFC6265_SidExample()
|
||||
{
|
||||
String rawCookie = "SID=31d4d96e407aad42";
|
||||
|
||||
Cookie cookies[] = parseCookieHeaders(rawCookie);
|
||||
|
||||
assertThat("Cookies.length", cookies.length, is(1));
|
||||
assertCookie("Cookies[0]", cookies[0], "SID", "31d4d96e407aad42", 1, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Example from RFC6265
|
||||
*/
|
||||
@Test
|
||||
public void testRFC6265_SidLangExample()
|
||||
{
|
||||
String rawCookie = "SID=31d4d96e407aad42; lang=en-US";
|
||||
|
||||
Cookie cookies[] = parseCookieHeaders(rawCookie);
|
||||
|
||||
assertThat("Cookies.length", cookies.length, is(2));
|
||||
assertCookie("Cookies[0]", cookies[0], "SID", "31d4d96e407aad42", 0, null);
|
||||
assertCookie("Cookies[1]", cookies[1], "lang", "en-US", 0, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Basic key=value, following RFC6265 rules
|
||||
*/
|
||||
@Test
|
||||
public void testKeyValue()
|
||||
{
|
||||
String rawCookie = "key=value";
|
||||
|
||||
Cookie cookies[] = parseCookieHeaders(rawCookie);
|
||||
|
||||
assertThat("Cookies.length", cookies.length, is(1));
|
||||
assertCookie("Cookies[0]", cookies[0], "key", "value", 0, null);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,137 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
|
||||
// ------------------------------------------------------------------------
|
||||
// All rights reserved. This program and the accompanying materials
|
||||
// are made available under the terms of the Eclipse Public License v1.0
|
||||
// and Apache License v2.0 which accompanies this distribution.
|
||||
//
|
||||
// The Eclipse Public License is available at
|
||||
// http://www.eclipse.org/legal/epl-v10.html
|
||||
//
|
||||
// The Apache License v2.0 is available at
|
||||
// http://www.opensource.org/licenses/apache2.0.php
|
||||
//
|
||||
// You may elect to redistribute this code under either of these licenses.
|
||||
// ========================================================================
|
||||
//
|
||||
|
||||
package org.eclipse.jetty.server;
|
||||
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import javax.servlet.http.Cookie;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.junit.runners.Parameterized;
|
||||
|
||||
/**
|
||||
* Tests of poor various name=value scenarios and expectations of results
|
||||
* due to our efforts at being lenient with what we receive.
|
||||
*/
|
||||
@RunWith(Parameterized.class)
|
||||
public class CookieCutter_LenientTest
|
||||
{
|
||||
@Parameterized.Parameters(name = "{0}")
|
||||
public static List<String[]> data()
|
||||
{
|
||||
List<String[]> ret = new ArrayList<>();
|
||||
|
||||
// Simple test to verify behavior
|
||||
ret.add(new String[]{"key=value", "key", "value"});
|
||||
|
||||
// Tests that conform to RFC2109
|
||||
// RFC2109 - token values
|
||||
// token = 1*<any CHAR except CTLs or tspecials>
|
||||
// CHAR = <any US-ASCII character (octets 0 - 127)>
|
||||
// CTL = <any US-ASCII control character
|
||||
// (octets 0 - 31) and DEL (127)>
|
||||
// SP = <US-ASCII SP, space (32)>
|
||||
// HT = <US-ASCII HT, horizontal-tab (9)>
|
||||
// tspecials = "(" | ")" | "<" | ">" | "@"
|
||||
// | "," | ";" | ":" | "\" | <">
|
||||
// | "/" | "[" | "]" | "?" | "="
|
||||
// | "{" | "}" | SP | HT
|
||||
ret.add(new String[]{"abc=xyz", "abc", "xyz"});
|
||||
ret.add(new String[]{"abc=!bar", "abc", "!bar"});
|
||||
ret.add(new String[]{"abc=#hash", "abc", "#hash"});
|
||||
ret.add(new String[]{"abc=#hash", "abc", "#hash"});
|
||||
// RFC2109 - quoted-string values
|
||||
// quoted-string = ( <"> *(qdtext) <"> )
|
||||
// qdtext = <any TEXT except <">>
|
||||
// The backslash character ("\") may be used as a single-character quoting
|
||||
// mechanism only within quoted-string and comment constructs.
|
||||
// quoted-pair = "\" CHAR
|
||||
ret.add(new String[]{"abc=\"xyz\"", "abc", "xyz"});
|
||||
ret.add(new String[]{"abc=\"!bar\"", "abc", "!bar"});
|
||||
ret.add(new String[]{"abc=\"#hash\"", "abc", "#hash"});
|
||||
// RFC2109 - other valid name types that conform to 'attr'/'token' syntax
|
||||
ret.add(new String[]{"!f!o!o!=wat", "!f!o!o!", "wat"});
|
||||
ret.add(new String[]{"__MyHost=Foo", "__MyHost", "Foo"});
|
||||
ret.add(new String[]{"some-thing-else=to-parse", "some-thing-else", "to-parse"});
|
||||
// RFC2109 - names with attr/token syntax starting with '$' (and not a cookie reserved word)
|
||||
// See https://tools.ietf.org/html/draft-ietf-httpbis-cookie-prefixes-00#section-5.2
|
||||
ret.add(new String[]{"$foo=bar", "$foo", "bar"});
|
||||
ret.add(new String[]{"$Unexpected=Spanish_Inquisition", "$Unexpected", "Spanish_Inquisition"});
|
||||
|
||||
// Tests that conform to RFC6265
|
||||
ret.add(new String[]{"abc=foobar!", "abc", "foobar!"});
|
||||
ret.add(new String[]{"abc=\"foobar!\"", "abc", "foobar!"});
|
||||
|
||||
// Internal quotes
|
||||
ret.add(new String[]{"foo=\"bar\"baz\"", "foo", "bar\"baz"});
|
||||
ret.add(new String[]{"foo=\"bar\"-\"baz\"", "foo", "bar\"-\"baz"});
|
||||
ret.add(new String[]{"foo=\"bar'-\"baz\"", "foo", "bar'-\"baz"});
|
||||
ret.add(new String[]{"foo=\"bar''-\"baz\"", "foo", "bar''-\"baz"});
|
||||
// These seem dubious until you realize the "lots of equals signs" below works
|
||||
ret.add(new String[]{"foo=\"bar\"=\"baz\"", "foo", "bar\"=\"baz"});
|
||||
ret.add(new String[]{"query=\"?b=c\"&\"d=e\"", "foo", "?b=c\"&\"d=e"});
|
||||
// Escaped quotes
|
||||
ret.add(new String[]{"foo=\"bar\\\"=\\\"baz\"", "foo", "bar\\\"=\\\"baz"});
|
||||
|
||||
// UTF-8 values
|
||||
ret.add(new String[]{"2sides=\u262F", "2sides", "\u262f"}); // 2 byte
|
||||
ret.add(new String[]{"currency=\"\u20AC\"", "currency", "\u20AC"}); // 3 byte
|
||||
ret.add(new String[]{"gothic=\"\uD800\uDF48\"", "gothic", "\uD800\uDF48"}); // 4 byte
|
||||
|
||||
// Lots of equals signs
|
||||
ret.add(new String[]{"query=b=c&d=e", "query", "b=c&d=e"});
|
||||
|
||||
// Escaping
|
||||
ret.add(new String[]{"query=%7B%22sessionCount%22%3A5%2C%22sessionTime%22%3A14151%7D", "query", "%7B%22sessionCount%22%3A5%2C%22sessionTime%22%3A14151%7D"});
|
||||
|
||||
// Google cookies (seen in wild, has `tspecials` of ':' in value)
|
||||
ret.add(new String[]{"GAPS=1:A1aaaAaAA1aaAAAaa1a11a:aAaaAa-aaA1-", "GAPS", "1:A1aaaAaAA1aaAAAaa1a11a:aAaaAa-aaA1-"});
|
||||
|
||||
// Strong abuse of cookie spec (lots of tspecials)
|
||||
ret.add(new String[]{"$Version=0; rToken=F_TOKEN''!--\"</a>=&{()}", "rToken", "F_TOKEN''!--\"</a>=&{()}"});
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@Parameterized.Parameter
|
||||
public String rawHeader;
|
||||
|
||||
@Parameterized.Parameter(1)
|
||||
public String expectedName;
|
||||
|
||||
@Parameterized.Parameter(2)
|
||||
public String expectedValue;
|
||||
|
||||
@Test
|
||||
public void testLenientBehavior()
|
||||
{
|
||||
CookieCutter cutter = new CookieCutter();
|
||||
cutter.addCookieField(rawHeader);
|
||||
Cookie[] cookies = cutter.getCookies();
|
||||
|
||||
assertThat("Cookies.length", cookies.length, is(1));
|
||||
assertThat("Cookie.name", cookies[0].getName(), is(expectedName));
|
||||
assertThat("Cookie.value", cookies[0].getValue(), is(expectedValue));
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue