Commit 238c22cd authored by Phillip Webb's avatar Phillip Webb

Polish content security policy contribution

See gh-7373
See gh-7373
parent d7bbea63
...@@ -175,15 +175,17 @@ public class SecurityProperties implements SecurityPrerequisite { ...@@ -175,15 +175,17 @@ public class SecurityProperties implements SecurityPrerequisite {
} }
public enum ContentSecurityPolicyMode { public enum ContentSecurityPolicyMode {
/** /**
* Use the {@code Content-Security-Policy} header. * Use the 'Content-Security-Policy' header.
*/ */
DEFAULT, DEFAULT,
/** /**
* Use the {@code Content-Security-Policy-Report-Only} header. * Use the 'Content-Security-Policy-Report-Only' header.
*/ */
REPORT_ONLY REPORT_ONLY
} }
/** /**
...@@ -212,7 +214,7 @@ public class SecurityProperties implements SecurityPrerequisite { ...@@ -212,7 +214,7 @@ public class SecurityProperties implements SecurityPrerequisite {
private String contentSecurityPolicy; private String contentSecurityPolicy;
/** /**
* Whether to use the "Content-Security-Policy" or "Content-Security-Policy-Report-Only" header. * Security policy mode.
*/ */
private ContentSecurityPolicyMode contentSecurityPolicyMode = ContentSecurityPolicyMode.DEFAULT; private ContentSecurityPolicyMode contentSecurityPolicyMode = ContentSecurityPolicyMode.DEFAULT;
...@@ -265,7 +267,8 @@ public class SecurityProperties implements SecurityPrerequisite { ...@@ -265,7 +267,8 @@ public class SecurityProperties implements SecurityPrerequisite {
return this.contentSecurityPolicyMode; return this.contentSecurityPolicyMode;
} }
public void setContentSecurityPolicyMode(ContentSecurityPolicyMode contentSecurityPolicyMode) { public void setContentSecurityPolicyMode(
ContentSecurityPolicyMode contentSecurityPolicyMode) {
this.contentSecurityPolicyMode = contentSecurityPolicyMode; this.contentSecurityPolicyMode = contentSecurityPolicyMode;
} }
......
...@@ -29,6 +29,7 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean ...@@ -29,6 +29,7 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication; import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
import org.springframework.boot.autoconfigure.security.SecurityProperties.Headers; import org.springframework.boot.autoconfigure.security.SecurityProperties.Headers;
import org.springframework.boot.autoconfigure.security.SecurityProperties.Headers.ContentSecurityPolicyMode;
import org.springframework.boot.autoconfigure.web.ErrorController; import org.springframework.boot.autoconfigure.web.ErrorController;
import org.springframework.boot.autoconfigure.web.ServerProperties; import org.springframework.boot.autoconfigure.web.ServerProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.boot.context.properties.EnableConfigurationProperties;
...@@ -109,12 +110,13 @@ public class SpringBootWebSecurityConfiguration { ...@@ -109,12 +110,13 @@ public class SpringBootWebSecurityConfiguration {
configurer.contentTypeOptions().disable(); configurer.contentTypeOptions().disable();
} }
if (StringUtils.hasText(headers.getContentSecurityPolicy())) { if (StringUtils.hasText(headers.getContentSecurityPolicy())) {
if (headers.getContentSecurityPolicyMode() == Headers.ContentSecurityPolicyMode.DEFAULT) { String policyDirectives = headers.getContentSecurityPolicy();
configurer.contentSecurityPolicy(headers.getContentSecurityPolicy()); ContentSecurityPolicyMode mode = headers.getContentSecurityPolicyMode();
if (mode == ContentSecurityPolicyMode.DEFAULT) {
configurer.contentSecurityPolicy(policyDirectives);
} }
else { else {
assert headers.getContentSecurityPolicyMode() == Headers.ContentSecurityPolicyMode.REPORT_ONLY; configurer.contentSecurityPolicy(policyDirectives).reportOnly();
configurer.contentSecurityPolicy(headers.getContentSecurityPolicy()).reportOnly();
} }
} }
if (!headers.isXss()) { if (!headers.isXss()) {
......
...@@ -214,7 +214,8 @@ public class SpringBootWebSecurityConfigurationTests { ...@@ -214,7 +214,8 @@ public class SpringBootWebSecurityConfigurationTests {
is(notNullValue()))) is(notNullValue())))
.andExpect(MockMvcResultMatchers.header().string("X-Frame-Options", .andExpect(MockMvcResultMatchers.header().string("X-Frame-Options",
is(notNullValue()))) is(notNullValue())))
.andExpect(MockMvcResultMatchers.header().doesNotExist("Content-Security-Policy")); .andExpect(MockMvcResultMatchers.header()
.doesNotExist("Content-Security-Policy"));
} }
@Test @Test
...@@ -250,9 +251,10 @@ public class SpringBootWebSecurityConfigurationTests { ...@@ -250,9 +251,10 @@ public class SpringBootWebSecurityConfigurationTests {
.getBean("springSecurityFilterChain", Filter.class)) .getBean("springSecurityFilterChain", Filter.class))
.build(); .build();
mockMvc.perform(MockMvcRequestBuilders.get("/")) mockMvc.perform(MockMvcRequestBuilders.get("/"))
.andExpect(MockMvcResultMatchers.header().string("Content-Security-Policy", .andExpect(MockMvcResultMatchers.header()
is("default-src 'self';"))) .string("Content-Security-Policy", is("default-src 'self';")))
.andExpect(MockMvcResultMatchers.header().doesNotExist("Content-Security-Policy-Report-Only")); .andExpect(MockMvcResultMatchers.header()
.doesNotExist("Content-Security-Policy-Report-Only"));
} }
@Test @Test
...@@ -266,9 +268,10 @@ public class SpringBootWebSecurityConfigurationTests { ...@@ -266,9 +268,10 @@ public class SpringBootWebSecurityConfigurationTests {
.getBean("springSecurityFilterChain", Filter.class)) .getBean("springSecurityFilterChain", Filter.class))
.build(); .build();
mockMvc.perform(MockMvcRequestBuilders.get("/")) mockMvc.perform(MockMvcRequestBuilders.get("/"))
.andExpect(MockMvcResultMatchers.header().string("Content-Security-Policy-Report-Only", .andExpect(MockMvcResultMatchers.header().string(
is("default-src 'self';"))) "Content-Security-Policy-Report-Only", is("default-src 'self';")))
.andExpect(MockMvcResultMatchers.header().doesNotExist("Content-Security-Policy")); .andExpect(MockMvcResultMatchers.header()
.doesNotExist("Content-Security-Policy"));
} }
@Configuration @Configuration
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment