Skip to content

Commit 76fe944

Browse files
committed
Consider having HeaderWriters check before writing
All HeadersWriter only write Header if its not already written. Fixes: spring-projectsgh-6454 spring-projectsgh-5193
1 parent b7ed919 commit 76fe944

16 files changed

+178
-32
lines changed

web/src/main/java/org/springframework/security/web/header/writers/ContentSecurityPolicyHeaderWriter.java

+6-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2016 the original author or authors.
2+
* Copyright 2002-2019 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -73,6 +73,7 @@
7373
* </p>
7474
*
7575
* @author Joe Grandja
76+
* @author Ankur Pathak
7677
* @since 4.1
7778
*/
7879
public final class ContentSecurityPolicyHeaderWriter implements HeaderWriter {
@@ -100,7 +101,10 @@ public ContentSecurityPolicyHeaderWriter(String policyDirectives) {
100101
*/
101102
@Override
102103
public void writeHeaders(HttpServletRequest request, HttpServletResponse response) {
103-
response.setHeader((!reportOnly ? CONTENT_SECURITY_POLICY_HEADER : CONTENT_SECURITY_POLICY_REPORT_ONLY_HEADER), policyDirectives);
104+
String headerName = !reportOnly ? CONTENT_SECURITY_POLICY_HEADER : CONTENT_SECURITY_POLICY_REPORT_ONLY_HEADER;
105+
if (!response.containsHeader(headerName)) {
106+
response.setHeader(headerName, policyDirectives);
107+
}
104108
}
105109

106110
/**

web/src/main/java/org/springframework/security/web/header/writers/FeaturePolicyHeaderWriter.java

+5-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2018 the original author or authors.
2+
* Copyright 2002-2019 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -33,6 +33,7 @@
3333
* responsible for declaring the restrictions for a particular feature type.
3434
*
3535
* @author Vedran Pavic
36+
* @author Ankur Pathak
3637
* @since 5.1
3738
*/
3839
public final class FeaturePolicyHeaderWriter implements HeaderWriter {
@@ -54,7 +55,9 @@ public FeaturePolicyHeaderWriter(String policyDirectives) {
5455

5556
@Override
5657
public void writeHeaders(HttpServletRequest request, HttpServletResponse response) {
57-
response.setHeader(FEATURE_POLICY_HEADER, this.policyDirectives);
58+
if (!response.containsHeader(FEATURE_POLICY_HEADER)) {
59+
response.setHeader(FEATURE_POLICY_HEADER, this.policyDirectives);
60+
}
5861
}
5962

6063
/**

web/src/main/java/org/springframework/security/web/header/writers/HpkpHeaderWriter.java

+6-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2016 the original author or authors.
2+
* Copyright 2002-2019 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -100,6 +100,7 @@
100100
* </p>
101101
*
102102
* @author Tim Ysewyn
103+
* @author Ankur Pathak
103104
* @since 4.1
104105
*/
105106
public final class HpkpHeaderWriter implements HeaderWriter {
@@ -174,7 +175,10 @@ public HpkpHeaderWriter() {
174175
public void writeHeaders(HttpServletRequest request, HttpServletResponse response) {
175176
if (requestMatcher.matches(request)) {
176177
if (!pins.isEmpty()) {
177-
response.setHeader(reportOnly ? HPKP_RO_HEADER_NAME : HPKP_HEADER_NAME, hpkpHeaderValue);
178+
String headerName = reportOnly ? HPKP_RO_HEADER_NAME : HPKP_HEADER_NAME;
179+
if (!response.containsHeader(headerName)) {
180+
response.setHeader(headerName, hpkpHeaderValue);
181+
}
178182
} if (logger.isDebugEnabled()) {
179183
logger.debug("Not injecting HPKP header since there aren't any pins");
180184
}

web/src/main/java/org/springframework/security/web/header/writers/HstsHeaderWriter.java

+5-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2013 the original author or authors.
2+
* Copyright 2002-2019 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -53,6 +53,7 @@
5353
* </p>
5454
*
5555
* @author Rob Winch
56+
* @author Ankur Pathak
5657
* @since 3.2
5758
*/
5859
public final class HstsHeaderWriter implements HeaderWriter {
@@ -159,7 +160,9 @@ public HstsHeaderWriter() {
159160
*/
160161
public void writeHeaders(HttpServletRequest request, HttpServletResponse response) {
161162
if (this.requestMatcher.matches(request)) {
162-
response.setHeader(HSTS_HEADER_NAME, this.hstsHeaderValue);
163+
if (!response.containsHeader(HSTS_HEADER_NAME)) {
164+
response.setHeader(HSTS_HEADER_NAME, this.hstsHeaderValue);
165+
}
163166
}
164167
else if (this.logger.isDebugEnabled()) {
165168
this.logger

web/src/main/java/org/springframework/security/web/header/writers/ReferrerPolicyHeaderWriter.java

+5-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2016 the original author or authors.
2+
* Copyright 2002-2019 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -49,6 +49,7 @@
4949
*
5050
* @author Eddú Meléndez
5151
* @author Kazuki Shimizu
52+
* @author Ankur Pathak
5253
* @since 4.2
5354
*/
5455
public class ReferrerPolicyHeaderWriter implements HeaderWriter {
@@ -89,7 +90,9 @@ public void setPolicy(ReferrerPolicy policy) {
8990
*/
9091
@Override
9192
public void writeHeaders(HttpServletRequest request, HttpServletResponse response) {
92-
response.setHeader(REFERRER_POLICY_HEADER, this.policy.getPolicy());
93+
if (!response.containsHeader(REFERRER_POLICY_HEADER)) {
94+
response.setHeader(REFERRER_POLICY_HEADER, this.policy.getPolicy());
95+
}
9396
}
9497

9598
public enum ReferrerPolicy {

web/src/main/java/org/springframework/security/web/header/writers/StaticHeadersWriter.java

+7-4
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2016 the original author or authors.
2+
* Copyright 2002-2019 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -30,6 +30,7 @@
3030
*
3131
* @author Marten Deinum
3232
* @author Rob Winch
33+
* @author Ankur Pathak
3334
* @since 3.2
3435
*/
3536
public class StaticHeadersWriter implements HeaderWriter {
@@ -56,8 +57,10 @@ public StaticHeadersWriter(String headerName, String... headerValues) {
5657

5758
public void writeHeaders(HttpServletRequest request, HttpServletResponse response) {
5859
for (Header header : headers) {
59-
for (String value : header.getValues()) {
60-
response.addHeader(header.getName(), value);
60+
if (!response.containsHeader(header.getName())) {
61+
for (String value : header.getValues()) {
62+
response.addHeader(header.getName(), value);
63+
}
6164
}
6265
}
6366
}
@@ -66,4 +69,4 @@ public void writeHeaders(HttpServletRequest request, HttpServletResponse respons
6669
public String toString() {
6770
return getClass().getName() + " [headers=" + headers + "]";
6871
}
69-
}
72+
}

web/src/main/java/org/springframework/security/web/header/writers/XXssProtectionHeaderWriter.java

+5-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2013 the original author or authors.
2+
* Copyright 2002-2019 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -26,6 +26,7 @@
2626
* >X-XSS-Protection header</a>.
2727
*
2828
* @author Rob Winch
29+
* @author Ankur Pathak
2930
* @since 3.2
3031
*/
3132
public final class XXssProtectionHeaderWriter implements HeaderWriter {
@@ -47,7 +48,9 @@ public XXssProtectionHeaderWriter() {
4748
}
4849

4950
public void writeHeaders(HttpServletRequest request, HttpServletResponse response) {
50-
response.setHeader(XSS_PROTECTION_HEADER, headerValue);
51+
if (!response.containsHeader(XSS_PROTECTION_HEADER)) {
52+
response.setHeader(XSS_PROTECTION_HEADER, headerValue);
53+
}
5154
}
5255

5356
/**

web/src/main/java/org/springframework/security/web/header/writers/frameoptions/XFrameOptionsHeaderWriter.java

+9-4
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2018 the original author or authors.
2+
* Copyright 2002-2019 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -27,6 +27,7 @@
2727
*
2828
* @author Marten Deinum
2929
* @author Rob Winch
30+
* @author Ankur Pathak
3031
* @since 3.2
3132
*
3233
* @see AllowFromStrategy
@@ -84,10 +85,14 @@ public void writeHeaders(HttpServletRequest request, HttpServletResponse respons
8485
if (XFrameOptionsMode.ALLOW_FROM.equals(frameOptionsMode)) {
8586
String allowFromValue = this.allowFromStrategy.getAllowFromValue(request);
8687
if (XFrameOptionsMode.DENY.getMode().equals(allowFromValue)) {
87-
response.setHeader(XFRAME_OPTIONS_HEADER, XFrameOptionsMode.DENY.getMode());
88+
if (!response.containsHeader(XFRAME_OPTIONS_HEADER)) {
89+
response.setHeader(XFRAME_OPTIONS_HEADER, XFrameOptionsMode.DENY.getMode());
90+
}
8891
} else if (allowFromValue != null) {
89-
response.setHeader(XFRAME_OPTIONS_HEADER,
90-
XFrameOptionsMode.ALLOW_FROM.getMode() + " " + allowFromValue);
92+
if (!response.containsHeader(XFRAME_OPTIONS_HEADER)) {
93+
response.setHeader(XFRAME_OPTIONS_HEADER,
94+
XFrameOptionsMode.ALLOW_FROM.getMode() + " " + allowFromValue);
95+
}
9196
}
9297
}
9398
else {

web/src/test/java/org/springframework/security/web/header/writers/ContentSecurityPolicyHeaderWriterTests.java

+23-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2017 the original author or authors.
2+
* Copyright 2002-2019 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -24,12 +24,16 @@
2424

2525
/**
2626
* @author Joe Grandja
27+
* @author Ankur Pathak
2728
*/
2829
public class ContentSecurityPolicyHeaderWriterTests {
2930
private static final String DEFAULT_POLICY_DIRECTIVES = "default-src 'self'";
3031
private MockHttpServletRequest request;
3132
private MockHttpServletResponse response;
3233
private ContentSecurityPolicyHeaderWriter writer;
34+
private static final String CONTENT_SECURITY_POLICY_HEADER = "Content-Security-Policy";
35+
private static final String CONTENT_SECURITY_POLICY_REPORT_ONLY_HEADER = "Content-Security-Policy-Report-Only";
36+
3337

3438
@Before
3539
public void setup() {
@@ -87,4 +91,21 @@ public void writeHeadersContentSecurityPolicyInvalid() {
8791
writer = new ContentSecurityPolicyHeaderWriter(null);
8892
}
8993

90-
}
94+
95+
@Test
96+
public void writeHeaderOnlyIfNotPresentContentSecurityPolicyHeader(){
97+
String value = new String("value");
98+
this.response.setHeader(CONTENT_SECURITY_POLICY_HEADER, value);
99+
this.writer.writeHeaders(this.request, this.response);
100+
assertThat(this.response.getHeader(CONTENT_SECURITY_POLICY_HEADER)).isSameAs(value);
101+
}
102+
103+
@Test
104+
public void writeHeaderOnlyIfNotPresentContentSecurityPolicyReportOnlyHeader(){
105+
String value = new String("value");
106+
this.response.setHeader(CONTENT_SECURITY_POLICY_REPORT_ONLY_HEADER, value);
107+
this.writer.setReportOnly(true);
108+
this.writer.writeHeaders(this.request, this.response);
109+
assertThat(this.response.getHeader(CONTENT_SECURITY_POLICY_REPORT_ONLY_HEADER)).isSameAs(value);
110+
}
111+
}

web/src/test/java/org/springframework/security/web/header/writers/FeaturePolicyHeaderWriterTests.java

+12-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2018 the original author or authors.
2+
* Copyright 2002-2019 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -29,6 +29,7 @@
2929
* Tests for {@link FeaturePolicyHeaderWriter}.
3030
*
3131
* @author Vedran Pavic
32+
* @author Ankur Pathak
3233
*/
3334
public class FeaturePolicyHeaderWriterTests {
3435

@@ -40,6 +41,8 @@ public class FeaturePolicyHeaderWriterTests {
4041

4142
private FeaturePolicyHeaderWriter writer;
4243

44+
private static final String FEATURE_POLICY_HEADER = "Feature-Policy";
45+
4346
@Before
4447
public void setUp() {
4548
this.request = new MockHttpServletRequest();
@@ -70,4 +73,12 @@ public void createWriterWithEmptyDirectivesShouldThrowException() {
7073
.hasMessage("policyDirectives must not be null or empty");
7174
}
7275

76+
@Test
77+
public void writeHeaderOnlyIfNotPresent(){
78+
String value = new String("value");
79+
this.response.setHeader(FEATURE_POLICY_HEADER, value);
80+
this.writer.writeHeaders(this.request, this.response);
81+
assertThat(this.response.getHeader(FEATURE_POLICY_HEADER)).isSameAs(value);
82+
}
83+
7384
}

web/src/test/java/org/springframework/security/web/header/writers/HpkpHeaderWriterTests.java

+24-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2016 the original author or authors.
2+
* Copyright 2002-2019 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -30,6 +30,7 @@
3030

3131
/**
3232
* @author Tim Ysewyn
33+
* @author Ankur Pathak
3334
*
3435
*/
3536
public class HpkpHeaderWriterTests {
@@ -47,6 +48,10 @@ public class HpkpHeaderWriterTests {
4748

4849
private HpkpHeaderWriter writer;
4950

51+
private static final String HPKP_HEADER_NAME = "Public-Key-Pins";
52+
53+
private static final String HPKP_RO_HEADER_NAME = "Public-Key-Pins-Report-Only";
54+
5055
@Before
5156
public void setup() {
5257
request = new MockHttpServletRequest();
@@ -196,4 +201,22 @@ public void addSha256PinsWithNullPin() {
196201
public void setIncorrectReportUri() {
197202
writer.setReportUri("some url here...");
198203
}
204+
205+
@Test
206+
public void writeHeaderOnlyIfNotPresentPublicKeyPins(){
207+
String value = new String("value");
208+
this.response.setHeader(HPKP_HEADER_NAME, value);
209+
this.writer.setReportOnly(false);
210+
this.writer.writeHeaders(this.request, this.response);
211+
assertThat(this.response.getHeader(HPKP_HEADER_NAME)).isSameAs(value);
212+
}
213+
214+
@Test
215+
public void writeHeaderOnlyIfNotPresentPublicKeyPinsReportOnly(){
216+
String value = new String("value");
217+
this.response.setHeader(HPKP_RO_HEADER_NAME, value);
218+
this.writer.setReportOnly(false);
219+
this.writer.writeHeaders(this.request, this.response);
220+
assertThat(this.response.getHeader(HPKP_RO_HEADER_NAME)).isSameAs(value);
221+
}
199222
}

0 commit comments

Comments
 (0)