Browse Source

ServerCookie ignores empty domain with double quotes

Closes gh-24663
pull/24687/head
Rossen Stoyanchev 5 years ago
parent
commit
a599859595
  1. 38
      spring-web/src/main/java/org/springframework/http/ResponseCookie.java
  2. 20
      spring-web/src/main/java/org/springframework/http/client/reactive/JettyClientHttpResponse.java
  3. 16
      spring-web/src/main/java/org/springframework/http/client/reactive/ReactorClientHttpResponse.java
  4. 12
      spring-web/src/test/java/org/springframework/http/ResponseCookieTests.java

38
spring-web/src/main/java/org/springframework/http/ResponseCookie.java

@ -1,5 +1,5 @@ @@ -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.
@ -185,9 +185,28 @@ public final class ResponseCookie extends HttpCookie { @@ -185,9 +185,28 @@ public final class ResponseCookie extends HttpCookie {
* 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() {
@ -220,10 +239,23 @@ public final class ResponseCookie extends HttpCookie { @@ -220,10 +239,23 @@ public final class ResponseCookie extends HttpCookie {
@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;

20
spring-web/src/main/java/org/springframework/http/client/reactive/JettyClientHttpResponse.java

@ -1,5 +1,5 @@ @@ -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.
@ -66,16 +66,14 @@ class JettyClientHttpResponse implements ClientHttpResponse { @@ -66,16 +66,14 @@ class JettyClientHttpResponse implements ClientHttpResponse {
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);

16
spring-web/src/main/java/org/springframework/http/client/reactive/ReactorClientHttpResponse.java

@ -1,5 +1,5 @@ @@ -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.
@ -100,13 +100,13 @@ class ReactorClientHttpResponse implements ClientHttpResponse { @@ -100,13 +100,13 @@ class ReactorClientHttpResponse implements ClientHttpResponse {
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);
}

12
spring-web/src/test/java/org/springframework/http/ResponseCookieTests.java

@ -1,5 +1,5 @@ @@ -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.
@ -81,4 +81,14 @@ public class ResponseCookieTests { @@ -81,4 +81,14 @@ public class ResponseCookieTests {
.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();
});
}
}

Loading…
Cancel
Save