Skip to content

Commit c49c493

Browse files
committed
Properly validate cookie chars, close AsyncHttpClient#1116
1 parent 83eb11d commit c49c493

File tree

2 files changed

+59
-69
lines changed

2 files changed

+59
-69
lines changed

client/src/main/java/org/asynchttpclient/cookie/Cookie.java

Lines changed: 2 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -12,64 +12,12 @@
1212
*/
1313
package org.asynchttpclient.cookie;
1414

15-
import static org.asynchttpclient.util.Assertions.*;
15+
import static org.asynchttpclient.cookie.CookieUtil.*;
1616

1717
public class Cookie {
1818

1919
public static Cookie newValidCookie(String name, String value, boolean wrap, String domain, String path, long maxAge, boolean secure, boolean httpOnly) {
20-
21-
name = assertNotNull(name, "name").trim();
22-
assertNotEmpty(name, "name");
23-
24-
for (int i = 0; i < name.length(); i++) {
25-
char c = name.charAt(i);
26-
if (c > 127) {
27-
throw new IllegalArgumentException("name contains non-ascii character: " + name);
28-
}
29-
30-
// Check prohibited characters.
31-
switch (c) {
32-
case '\t':
33-
case '\n':
34-
case 0x0b:
35-
case '\f':
36-
case '\r':
37-
case ' ':
38-
case ',':
39-
case ';':
40-
case '=':
41-
throw new IllegalArgumentException("name contains one of the following prohibited characters: " + "=,; \\t\\r\\n\\v\\f: " + name);
42-
}
43-
}
44-
45-
if (name.charAt(0) == '$') {
46-
throw new IllegalArgumentException("name starting with '$' not allowed: " + name);
47-
}
48-
49-
return new Cookie(name, assertNotNull(value, "value"), wrap, validateValue("domain", domain), validateValue("path", path), maxAge, secure, httpOnly);
50-
}
51-
52-
private static String validateValue(String name, String value) {
53-
if (value == null) {
54-
return null;
55-
}
56-
value = value.trim();
57-
if (value.length() == 0) {
58-
return null;
59-
}
60-
61-
for (int i = 0; i < value.length(); i++) {
62-
char c = value.charAt(i);
63-
switch (c) {
64-
case '\r':
65-
case '\n':
66-
case '\f':
67-
case 0x0b:
68-
case ';':
69-
throw new IllegalArgumentException(name + " contains one of the following prohibited characters: " + ";\\r\\n\\f\\v (" + value + ')');
70-
}
71-
}
72-
return value;
20+
return new Cookie(validateCookieName(name), validateCookieValue(value), wrap, validateCookieAttribute("domain", domain), validateCookieAttribute("path", path), maxAge, secure, httpOnly);
7321
}
7422

7523
private final String name;

client/src/main/java/org/asynchttpclient/cookie/CookieUtil.java

Lines changed: 57 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,33 @@
1212
*/
1313
package org.asynchttpclient.cookie;
1414

15+
import static org.asynchttpclient.util.Assertions.*;
16+
1517
import java.util.BitSet;
1618
import java.util.Date;
1719

1820
public class CookieUtil {
1921

20-
private static final BitSet VALID_COOKIE_VALUE_OCTETS = validCookieValueOctets();
21-
2222
private static final BitSet VALID_COOKIE_NAME_OCTETS = validCookieNameOctets();
23+
private static final BitSet VALID_COOKIE_VALUE_OCTETS = validCookieValueOctets();
24+
private static final BitSet VALID_COOKIE_ATTRIBUTE_VALUE_OCTETS = validCookieAttributeValueOctets();
25+
26+
// token = 1*<any CHAR except CTLs or separators>
27+
// separators = "(" | ")" | "<" | ">" | "@"
28+
// | "," | ";" | ":" | "\" | <">
29+
// | "/" | "[" | "]" | "?" | "="
30+
// | "{" | "}" | SP | HT
31+
private static BitSet validCookieNameOctets() {
32+
BitSet bits = new BitSet();
33+
for (int i = 32; i < 127; i++) {
34+
bits.set(i);
35+
}
36+
int[] separators = new int[] { '(', ')', '<', '>', '@', ',', ';', ':', '\\', '"', '/', '[', ']', '?', '=', '{', '}', ' ', '\t' };
37+
for (int separator : separators) {
38+
bits.set(separator, false);
39+
}
40+
return bits;
41+
}
2342

2443
// cookie-octet = %x21 / %x23-2B / %x2D-3A / %x3C-5B / %x5D-7E
2544
// US-ASCII characters excluding CTLs, whitespace, DQUOTE, comma, semicolon, and backslash
@@ -40,28 +59,51 @@ private static BitSet validCookieValueOctets() {
4059
}
4160
return bits;
4261
}
43-
44-
// token = 1*<any CHAR except CTLs or separators>
45-
// separators = "(" | ")" | "<" | ">" | "@"
46-
// | "," | ";" | ":" | "\" | <">
47-
// | "/" | "[" | "]" | "?" | "="
48-
// | "{" | "}" | SP | HT
49-
private static BitSet validCookieNameOctets() {
62+
63+
private static BitSet validCookieAttributeValueOctets() {
5064
BitSet bits = new BitSet();
5165
for (int i = 32; i < 127; i++) {
5266
bits.set(i);
5367
}
54-
int[] separators = new int[] { '(', ')', '<', '>', '@', ',', ';', ':', '\\', '"', '/', '[', ']', '?', '=', '{', '}', ' ', '\t' };
55-
for (int separator : separators) {
56-
bits.set(separator, false);
57-
}
68+
bits.set(';', false);
5869
return bits;
5970
}
6071

61-
static int firstInvalidCookieNameOctet(CharSequence cs) {
62-
return firstInvalidOctet(cs, VALID_COOKIE_NAME_OCTETS);
72+
static String validateCookieName(String name) {
73+
name = assertNotNull(name, "name").trim();
74+
assertNotEmpty(name, "name");
75+
int i = firstInvalidOctet(name, VALID_COOKIE_NAME_OCTETS);
76+
if (i != -1) {
77+
throw new IllegalArgumentException("name contains prohibited character: " + name.charAt(i));
78+
}
79+
return name;
80+
}
81+
82+
static String validateCookieValue(String value) {
83+
value = assertNotNull(value, "name").trim();
84+
CharSequence unwrappedValue = unwrapValue(value);
85+
int i = firstInvalidOctet(unwrappedValue, VALID_COOKIE_VALUE_OCTETS);
86+
if (i != -1) {
87+
throw new IllegalArgumentException("value contains prohibited character: " + unwrappedValue.charAt(i));
88+
}
89+
return value;
6390
}
6491

92+
static String validateCookieAttribute(String name, String value) {
93+
if (value == null) {
94+
return null;
95+
}
96+
value = value.trim();
97+
if (value.length() == 0) {
98+
return null;
99+
}
100+
int i = firstInvalidOctet(value, VALID_COOKIE_ATTRIBUTE_VALUE_OCTETS);
101+
if (i != -1) {
102+
throw new IllegalArgumentException(name + " contains prohibited character: " + value.charAt(i));
103+
}
104+
return value;
105+
}
106+
65107
static int firstInvalidCookieValueOctet(CharSequence cs) {
66108
return firstInvalidOctet(cs, VALID_COOKIE_VALUE_OCTETS);
67109
}

0 commit comments

Comments
 (0)