Fix case sensitive headers comparison

Closes gh-10557
This commit is contained in:
Steve Riesenberg 2021-12-01 12:36:22 -06:00
parent 01be7eca6e
commit 1d814f95d5
2 changed files with 32 additions and 6 deletions

View File

@ -16,7 +16,6 @@
package org.springframework.security.web.server.header; package org.springframework.security.web.server.header;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections;
import org.springframework.http.HttpHeaders; import org.springframework.http.HttpHeaders;
import org.springframework.web.server.ServerWebExchange; import org.springframework.web.server.ServerWebExchange;
@ -42,11 +41,17 @@ public class StaticServerHttpHeadersWriter implements ServerHttpHeadersWriter {
@Override @Override
public Mono<Void> writeHttpHeaders(ServerWebExchange exchange) { public Mono<Void> writeHttpHeaders(ServerWebExchange exchange) {
HttpHeaders headers = exchange.getResponse().getHeaders(); HttpHeaders headers = exchange.getResponse().getHeaders();
boolean containsOneHeaderToAdd = Collections.disjoint(headers.keySet(), this.headersToAdd.keySet()); // Note: We need to ensure that the following algorithm compares headers
if (containsOneHeaderToAdd) { // case insensitively, which should be true of headers.containsKey().
this.headersToAdd.forEach((name, values) -> { boolean containsNoHeadersToAdd = true;
headers.put(name, values); for (String headerName : this.headersToAdd.keySet()) {
}); if (headers.containsKey(headerName)) {
containsNoHeadersToAdd = false;
break;
}
}
if (containsNoHeadersToAdd) {
this.headersToAdd.forEach(headers::put);
} }
return Mono.empty(); return Mono.empty();
} }

View File

@ -17,10 +17,13 @@ package org.springframework.security.web.server.header;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
import java.util.Locale;
import org.junit.Test; import org.junit.Test;
import org.springframework.http.HttpHeaders; import org.springframework.http.HttpHeaders;
import org.springframework.mock.http.server.reactive.MockServerHttpRequest; import org.springframework.mock.http.server.reactive.MockServerHttpRequest;
import org.springframework.mock.web.server.MockServerWebExchange; import org.springframework.mock.web.server.MockServerWebExchange;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.web.server.ServerWebExchange; import org.springframework.web.server.ServerWebExchange;
/** /**
@ -56,6 +59,24 @@ public class StaticServerHttpHeadersWriterTests {
assertThat(headers.get(ContentTypeOptionsServerHttpHeadersWriter.X_CONTENT_OPTIONS)).containsOnly(headerValue); assertThat(headers.get(ContentTypeOptionsServerHttpHeadersWriter.X_CONTENT_OPTIONS)).containsOnly(headerValue);
} }
// gh-10557
@Test
public void writeHeadersWhenHeaderWrittenWithDifferentCaseThenDoesNotWriteHeaders() {
String headerName = HttpHeaders.CACHE_CONTROL.toLowerCase(Locale.ROOT);
String headerValue = "max-age=120";
this.headers.set(headerName, headerValue);
// Note: This test inverts which collection uses case sensitive headers,
// due to the fact that gh-10557 reports NettyHeadersAdapter as the
// response headers implementation, which is not accessible here.
HttpHeaders caseSensitiveHeaders = new HttpHeaders(new LinkedMultiValueMap<>());
caseSensitiveHeaders.set(HttpHeaders.CACHE_CONTROL, CacheControlServerHttpHeadersWriter.CACHE_CONTRTOL_VALUE);
caseSensitiveHeaders.set(HttpHeaders.PRAGMA, CacheControlServerHttpHeadersWriter.PRAGMA_VALUE);
caseSensitiveHeaders.set(HttpHeaders.EXPIRES, CacheControlServerHttpHeadersWriter.EXPIRES_VALUE);
this.writer = new StaticServerHttpHeadersWriter(caseSensitiveHeaders);
this.writer.writeHttpHeaders(this.exchange);
assertThat(this.headers.get(headerName)).containsOnly(headerValue);
}
@Test @Test
public void writeHeadersWhenMultiHeaderThenWritesAllHeaders() { public void writeHeadersWhenMultiHeaderThenWritesAllHeaders() {
writer = StaticServerHttpHeadersWriter.builder() writer = StaticServerHttpHeadersWriter.builder()