Add ! to allowed cookie value chars

Motivation:

! is missing from allowed cookie value chars, as per https://tools.ietf.org/html/rfc6265#section-4.1.1.
Issue was originally reported on Play!, see https://github.com/playframework/playframework/issues/4460#issuecomment-198177302.

Modifications:

Stick to RFC6265 ranges.

Result:

RFC6265 compliance, ! is supported
This commit is contained in:
Stephane Landelle 2016-03-18 09:23:24 +01:00 committed by Norman Maurer
parent b6868077f3
commit 0310d79edf
2 changed files with 59 additions and 31 deletions

View File

@ -24,45 +24,43 @@ final class CookieUtil {
private static final BitSet VALID_COOKIE_VALUE_OCTETS = validCookieValueOctets();
private static final BitSet VALID_COOKIE_NAME_OCTETS = validCookieNameOctets(VALID_COOKIE_VALUE_OCTETS);
private static final BitSet VALID_COOKIE_NAME_OCTETS = validCookieNameOctets();
// cookie-octet = %x21 / %x23-2B / %x2D-3A / %x3C-5B / %x5D-7E
// US-ASCII characters excluding CTLs, whitespace, DQUOTE, comma, semicolon, and backslash
private static BitSet validCookieValueOctets() {
BitSet bits = new BitSet(8);
for (int i = 35; i < 127; i++) {
// US-ASCII characters excluding CTLs (%x00-1F / %x7F)
BitSet bits = new BitSet();
bits.set(0x21);
for (int i = 0x23; i <= 0x2B; i++) {
bits.set(i);
}
for (int i = 0x2D; i <= 0x3A; i++) {
bits.set(i);
}
for (int i = 0x3C; i <= 0x5B; i++) {
bits.set(i);
}
for (int i = 0x5D; i <= 0x7E; i++) {
bits.set(i);
}
bits.set('"', false); // exclude DQUOTE = %x22
bits.set(',', false); // exclude comma = %x2C
bits.set(';', false); // exclude semicolon = %x3B
bits.set('\\', false); // exclude backslash = %x5C
return bits;
}
// token = 1*<any CHAR except CTLs or separators>
// separators = "(" | ")" | "<" | ">" | "@"
// | "," | ";" | ":" | "\" | <">
// | "/" | "[" | "]" | "?" | "="
// | "{" | "}" | SP | HT
private static BitSet validCookieNameOctets(BitSet validCookieValueOctets) {
BitSet bits = new BitSet(8);
bits.or(validCookieValueOctets);
bits.set('(', false);
bits.set(')', false);
bits.set('<', false);
bits.set('>', false);
bits.set('@', false);
bits.set(':', false);
bits.set('/', false);
bits.set('[', false);
bits.set(']', false);
bits.set('?', false);
bits.set('=', false);
bits.set('{', false);
bits.set('}', false);
bits.set(' ', false);
bits.set('\t', false);
// token = 1*<any CHAR except CTLs or separators>
// separators = "(" | ")" | "<" | ">" | "@"
// | "," | ";" | ":" | "\" | <">
// | "/" | "[" | "]" | "?" | "="
// | "{" | "}" | SP | HT
private static BitSet validCookieNameOctets() {
BitSet bits = new BitSet();
for (int i = 32; i < 127; i++) {
bits.set(i);
}
int[] separators = new int[]
{ '(', ')', '<', '>', '@', ',', ';', ':', '\\', '"', '/', '[', ']', '?', '=', '{', '}', ' ', '\t' };
for (int separator : separators) {
bits.set(separator, false);
}
return bits;
}

View File

@ -24,7 +24,9 @@ import io.netty.handler.codec.http.HttpHeaderDateFormat;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@ -76,6 +78,34 @@ public class ServerCookieEncoderTest {
assertEquals(result, encodedCookies);
}
@Test
public void illegalCharInCookieValueMakesStrictEncoderThrowsException() {
Set<Character> illegalChars = new HashSet<Character>();
// CTLs
for (int i = 0x00; i <= 0x1F; i++) {
illegalChars.add((char) i);
}
illegalChars.add((char) 0x7F);
// whitespace, DQUOTE, comma, semicolon, and backslash
for (char c : new char[] { ' ', '"', ',', ';', '\\' }) {
illegalChars.add(c);
}
int exceptions = 0;
for (char c : illegalChars) {
Cookie cookie = new DefaultCookie("name", "value" + c);
try {
ServerCookieEncoder.STRICT.encode(cookie);
} catch (IllegalArgumentException e) {
exceptions++;
}
}
assertEquals(illegalChars.size(), exceptions);
}
@Test
public void testEncodingMultipleCookiesLax() {
List<String> result = new ArrayList<String>();