Skip to content

Commit 3aa2a60

Browse files
author
Steve Riesenberg
committed
Fix case sensitive headers comparison
Closes gh-10557
1 parent 606bf6b commit 3aa2a60

File tree

2 files changed

+32
-6
lines changed

2 files changed

+32
-6
lines changed

web/src/main/java/org/springframework/security/web/server/header/StaticServerHttpHeadersWriter.java

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@
1616
package org.springframework.security.web.server.header;
1717

1818
import java.util.Arrays;
19-
import java.util.Collections;
2019

2120
import org.springframework.http.HttpHeaders;
2221
import org.springframework.web.server.ServerWebExchange;
@@ -42,11 +41,17 @@ public StaticServerHttpHeadersWriter(HttpHeaders headersToAdd) {
4241
@Override
4342
public Mono<Void> writeHttpHeaders(ServerWebExchange exchange) {
4443
HttpHeaders headers = exchange.getResponse().getHeaders();
45-
boolean containsOneHeaderToAdd = Collections.disjoint(headers.keySet(), this.headersToAdd.keySet());
46-
if (containsOneHeaderToAdd) {
47-
this.headersToAdd.forEach((name, values) -> {
48-
headers.put(name, values);
49-
});
44+
// Note: We need to ensure that the following algorithm compares headers
45+
// case insensitively, which should be true of headers.containsKey().
46+
boolean containsNoHeadersToAdd = true;
47+
for (String headerName : this.headersToAdd.keySet()) {
48+
if (headers.containsKey(headerName)) {
49+
containsNoHeadersToAdd = false;
50+
break;
51+
}
52+
}
53+
if (containsNoHeadersToAdd) {
54+
this.headersToAdd.forEach(headers::put);
5055
}
5156
return Mono.empty();
5257
}

web/src/test/java/org/springframework/security/web/server/header/StaticServerHttpHeadersWriterTests.java

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,13 @@
1717

1818
import static org.assertj.core.api.Assertions.assertThat;
1919

20+
import java.util.Locale;
21+
2022
import org.junit.Test;
2123
import org.springframework.http.HttpHeaders;
2224
import org.springframework.mock.http.server.reactive.MockServerHttpRequest;
2325
import org.springframework.mock.web.server.MockServerWebExchange;
26+
import org.springframework.util.LinkedMultiValueMap;
2427
import org.springframework.web.server.ServerWebExchange;
2528

2629
/**
@@ -56,6 +59,24 @@ public void writeHeadersWhenSingleHeaderAndHeaderWrittenThenSuccess() {
5659
assertThat(headers.get(ContentTypeOptionsServerHttpHeadersWriter.X_CONTENT_OPTIONS)).containsOnly(headerValue);
5760
}
5861

62+
// gh-10557
63+
@Test
64+
public void writeHeadersWhenHeaderWrittenWithDifferentCaseThenDoesNotWriteHeaders() {
65+
String headerName = HttpHeaders.CACHE_CONTROL.toLowerCase(Locale.ROOT);
66+
String headerValue = "max-age=120";
67+
this.headers.set(headerName, headerValue);
68+
// Note: This test inverts which collection uses case sensitive headers,
69+
// due to the fact that gh-10557 reports NettyHeadersAdapter as the
70+
// response headers implementation, which is not accessible here.
71+
HttpHeaders caseSensitiveHeaders = new HttpHeaders(new LinkedMultiValueMap<>());
72+
caseSensitiveHeaders.set(HttpHeaders.CACHE_CONTROL, CacheControlServerHttpHeadersWriter.CACHE_CONTRTOL_VALUE);
73+
caseSensitiveHeaders.set(HttpHeaders.PRAGMA, CacheControlServerHttpHeadersWriter.PRAGMA_VALUE);
74+
caseSensitiveHeaders.set(HttpHeaders.EXPIRES, CacheControlServerHttpHeadersWriter.EXPIRES_VALUE);
75+
this.writer = new StaticServerHttpHeadersWriter(caseSensitiveHeaders);
76+
this.writer.writeHttpHeaders(this.exchange);
77+
assertThat(this.headers.get(headerName)).containsOnly(headerValue);
78+
}
79+
5980
@Test
6081
public void writeHeadersWhenMultiHeaderThenWritesAllHeaders() {
6182
writer = StaticServerHttpHeadersWriter.builder()

0 commit comments

Comments
 (0)