Skip to content

Commit

Permalink
ServerCookie ignores empty domain with double quotes
Browse files Browse the repository at this point in the history
  • Loading branch information
rstoyanchev committed Mar 12, 2020
1 parent 4aedf2e commit a599859
Show file tree
Hide file tree
Showing 4 changed files with 63 additions and 23 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 the original author or authors.
* Copyright 2002-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -185,9 +185,28 @@ public String toString() {
* with a name-value pair and may also include attributes.
* @param name the cookie name
* @param value the cookie value
* @return the created cookie instance
* @return a builder to create the cookie with
*/
public static ResponseCookieBuilder from(final String name, final String value) {
return from(name, value, false);
}

/**
* Factory method to obtain a builder for a server-defined cookie. Unlike
* {@link #from(String, String)} this option assumes input from a remote
* server, which can be handled more leniently, e.g. ignoring a empty domain
* name with double quotes.
* @param name the cookie name
* @param value the cookie value
* @return a builder to create the cookie with
* @since 5.2.5
*/
public static ResponseCookieBuilder fromClientResponse(final String name, final String value) {
return from(name, value, true);
}


private static ResponseCookieBuilder from(final String name, final String value, boolean lenient) {

return new ResponseCookieBuilder() {

Expand Down Expand Up @@ -220,10 +239,23 @@ public ResponseCookieBuilder maxAge(long maxAgeSeconds) {

@Override
public ResponseCookieBuilder domain(String domain) {
this.domain = domain;
this.domain = initDomain(domain);
return this;
}

@Nullable
private String initDomain(String domain) {
if (lenient) {
String s = domain.trim();
if (s.startsWith("\"") && s.endsWith("\"")) {
if (s.substring(1, domain.length() - 1).trim().isEmpty()) {
return null;
}
}
}
return domain;
}

@Override
public ResponseCookieBuilder path(String path) {
this.path = path;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 the original author or authors.
* Copyright 2002-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -66,16 +66,14 @@ public MultiValueMap<String, ResponseCookie> getCookies() {
MultiValueMap<String, ResponseCookie> result = new LinkedMultiValueMap<>();
List<String> cookieHeader = getHeaders().get(HttpHeaders.SET_COOKIE);
if (cookieHeader != null) {
cookieHeader.forEach(header ->
HttpCookie.parse(header)
.forEach(cookie -> result.add(cookie.getName(),
ResponseCookie.from(cookie.getName(), cookie.getValue())
.domain(cookie.getDomain())
.path(cookie.getPath())
.maxAge(cookie.getMaxAge())
.secure(cookie.getSecure())
.httpOnly(cookie.isHttpOnly())
.build()))
cookieHeader.forEach(header -> HttpCookie.parse(header)
.forEach(c -> result.add(c.getName(), ResponseCookie.fromClientResponse(c.getName(), c.getValue())
.domain(c.getDomain())
.path(c.getPath())
.maxAge(c.getMaxAge())
.secure(c.getSecure())
.httpOnly(c.isHttpOnly())
.build()))
);
}
return CollectionUtils.unmodifiableMultiValueMap(result);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 the original author or authors.
* Copyright 2002-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -100,13 +100,13 @@ public int getRawStatusCode() {
public MultiValueMap<String, ResponseCookie> getCookies() {
MultiValueMap<String, ResponseCookie> result = new LinkedMultiValueMap<>();
this.response.cookies().values().stream().flatMap(Collection::stream)
.forEach(cookie ->
result.add(cookie.name(), ResponseCookie.from(cookie.name(), cookie.value())
.domain(cookie.domain())
.path(cookie.path())
.maxAge(cookie.maxAge())
.secure(cookie.isSecure())
.httpOnly(cookie.isHttpOnly())
.forEach(c ->
result.add(c.name(), ResponseCookie.fromClientResponse(c.name(), c.value())
.domain(c.domain())
.path(c.path())
.maxAge(c.maxAge())
.secure(c.isSecure())
.httpOnly(c.isHttpOnly())
.build()));
return CollectionUtils.unmodifiableMultiValueMap(result);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 the original author or authors.
* Copyright 2002-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -81,4 +81,14 @@ public void domainChecks() {
.hasMessageContaining("invalid cookie domain char"));
}

@Test // gh-24663
public void domainWithEmptyDoubleQuotes() {

Arrays.asList("\"\"", "\t\"\" ", " \" \t \"\t")
.forEach(domain -> {
ResponseCookie cookie = ResponseCookie.fromClientResponse("id", "1fWa").domain("\"\"").build();
assertThat(cookie.getDomain()).isNull();
});

}
}

0 comments on commit a599859

Please sign in to comment.