SEC-2098, SEC-2099: Fix build
- hf.doFilter is missing FilterChain argument - response.headers does not contain the exact values for the headers so should not be used for comparison (note it is a private member so this is acceptable) - hf does not need non-null check when hf.doFilter is invoked - some of the configurations are no longer valid (i.e. ALLOW-FROM requires strategy) - Some error messages needed updated (some could still use improvement) - No validation for missing header name or value - rebased off master / merged - nsa=frame-options-strategy id should use - not = - FramewOptionsHeaderFactory did not produce "ALLOW-FROM " prefix of origin - remove @Override on interface overrides to work with JDK5
This commit is contained in:
parent
d0b40cd2ae
commit
fd754c5cab
|
@ -17,6 +17,7 @@ import org.springframework.security.util.FieldUtils
|
||||||
import javax.servlet.Filter
|
import javax.servlet.Filter
|
||||||
import javax.servlet.http.HttpServletRequest
|
import javax.servlet.http.HttpServletRequest
|
||||||
|
|
||||||
|
import org.springframework.beans.factory.BeanCreationException;
|
||||||
import org.springframework.beans.factory.parsing.BeanDefinitionParsingException
|
import org.springframework.beans.factory.parsing.BeanDefinitionParsingException
|
||||||
import org.springframework.beans.factory.xml.XmlBeanDefinitionStoreException;
|
import org.springframework.beans.factory.xml.XmlBeanDefinitionStoreException;
|
||||||
import org.springframework.mock.web.MockFilterChain
|
import org.springframework.mock.web.MockFilterChain
|
||||||
|
@ -56,11 +57,10 @@ class HttpHeadersConfigTests extends AbstractHttpConfigTests {
|
||||||
createAppContext()
|
createAppContext()
|
||||||
|
|
||||||
def hf = getFilter(HeadersFilter)
|
def hf = getFilter(HeadersFilter)
|
||||||
MockHttpServletResponse response = new MockHttpServletResponse();
|
MockHttpServletResponse response = new MockHttpServletResponse()
|
||||||
hf.doFilter(new MockHttpServletRequest(), response);
|
hf.doFilter(new MockHttpServletRequest(), response, new MockFilterChain())
|
||||||
|
|
||||||
expect:
|
expect:
|
||||||
hf
|
|
||||||
response.headers.isEmpty()
|
response.headers.isEmpty()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -73,11 +73,11 @@ class HttpHeadersConfigTests extends AbstractHttpConfigTests {
|
||||||
createAppContext()
|
createAppContext()
|
||||||
|
|
||||||
def hf = getFilter(HeadersFilter)
|
def hf = getFilter(HeadersFilter)
|
||||||
MockHttpServletResponse response = new MockHttpServletResponse();
|
MockHttpServletResponse response = new MockHttpServletResponse()
|
||||||
hf.doFilter(new MockHttpServletRequest(), response);
|
hf.doFilter(new MockHttpServletRequest(), response, new MockFilterChain())
|
||||||
|
|
||||||
expect:
|
expect:
|
||||||
hf
|
assertHeaders(response, ['X-Content-Type-Options':'nosniff'])
|
||||||
response.headers == ['X-Content-Type-Options':'nosniff']
|
|
||||||
}
|
}
|
||||||
|
|
||||||
def 'http headers frame-options defaults to DENY'() {
|
def 'http headers frame-options defaults to DENY'() {
|
||||||
|
@ -89,10 +89,11 @@ class HttpHeadersConfigTests extends AbstractHttpConfigTests {
|
||||||
createAppContext()
|
createAppContext()
|
||||||
|
|
||||||
def hf = getFilter(HeadersFilter)
|
def hf = getFilter(HeadersFilter)
|
||||||
|
MockHttpServletResponse response = new MockHttpServletResponse()
|
||||||
|
hf.doFilter(new MockHttpServletRequest(), response, new MockFilterChain())
|
||||||
|
|
||||||
expect:
|
expect:
|
||||||
hf
|
assertHeaders(response, ['X-Frame-Options':'DENY'])
|
||||||
hf.headers == ['X-Frame-Options':'DENY']
|
|
||||||
}
|
}
|
||||||
|
|
||||||
def 'http headers frame-options DENY'() {
|
def 'http headers frame-options DENY'() {
|
||||||
|
@ -104,10 +105,11 @@ class HttpHeadersConfigTests extends AbstractHttpConfigTests {
|
||||||
createAppContext()
|
createAppContext()
|
||||||
|
|
||||||
def hf = getFilter(HeadersFilter)
|
def hf = getFilter(HeadersFilter)
|
||||||
|
MockHttpServletResponse response = new MockHttpServletResponse()
|
||||||
|
hf.doFilter(new MockHttpServletRequest(), response, new MockFilterChain())
|
||||||
|
|
||||||
expect:
|
expect:
|
||||||
hf
|
assertHeaders(response, ['X-Frame-Options':'DENY'])
|
||||||
hf.headers == ['X-Frame-Options':'DENY']
|
|
||||||
}
|
}
|
||||||
|
|
||||||
def 'http headers frame-options SAMEORIGIN'() {
|
def 'http headers frame-options SAMEORIGIN'() {
|
||||||
|
@ -119,17 +121,18 @@ class HttpHeadersConfigTests extends AbstractHttpConfigTests {
|
||||||
createAppContext()
|
createAppContext()
|
||||||
|
|
||||||
def hf = getFilter(HeadersFilter)
|
def hf = getFilter(HeadersFilter)
|
||||||
|
MockHttpServletResponse response = new MockHttpServletResponse()
|
||||||
|
hf.doFilter(new MockHttpServletRequest(), response, new MockFilterChain())
|
||||||
|
|
||||||
expect:
|
expect:
|
||||||
hf
|
assertHeaders(response, ['X-Frame-Options':'SAMEORIGIN'])
|
||||||
hf.headers == ['X-Frame-Options':'SAMEORIGIN']
|
|
||||||
}
|
}
|
||||||
|
|
||||||
def 'http headers frame-options ALLOW-FROM no origin reports error'() {
|
def 'http headers frame-options ALLOW-FROM no origin reports error'() {
|
||||||
when:
|
when:
|
||||||
httpAutoConfig {
|
httpAutoConfig {
|
||||||
'headers'() {
|
'headers'() {
|
||||||
'frame-options'(policy : 'ALLOW-FROM')
|
'frame-options'(policy : 'ALLOW-FROM', strategy : 'static')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
createAppContext()
|
createAppContext()
|
||||||
|
@ -138,14 +141,14 @@ class HttpHeadersConfigTests extends AbstractHttpConfigTests {
|
||||||
|
|
||||||
then:
|
then:
|
||||||
BeanDefinitionParsingException e = thrown()
|
BeanDefinitionParsingException e = thrown()
|
||||||
e.message.contains '<frame-options policy="ALLOW-FROM"/> requires a non-empty string value for the origin attribute to be specified.'
|
e.message.contains "Strategy requires a 'value' to be set." // FIME better error message?
|
||||||
}
|
}
|
||||||
|
|
||||||
def 'http headers frame-options ALLOW-FROM spaces only origin reports error'() {
|
def 'http headers frame-options ALLOW-FROM spaces only origin reports error'() {
|
||||||
when:
|
when:
|
||||||
httpAutoConfig {
|
httpAutoConfig {
|
||||||
'headers'() {
|
'headers'() {
|
||||||
'frame-options'(policy : 'ALLOW-FROM', origin : ' ')
|
'frame-options'(policy : 'ALLOW-FROM', strategy: 'static', value : ' ')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
createAppContext()
|
createAppContext()
|
||||||
|
@ -154,23 +157,24 @@ class HttpHeadersConfigTests extends AbstractHttpConfigTests {
|
||||||
|
|
||||||
then:
|
then:
|
||||||
BeanDefinitionParsingException e = thrown()
|
BeanDefinitionParsingException e = thrown()
|
||||||
e.message.contains '<frame-options policy="ALLOW-FROM"/> requires a non-empty string value for the origin attribute to be specified.'
|
e.message.contains "Strategy requires a 'value' to be set." // FIME better error message?
|
||||||
}
|
}
|
||||||
|
|
||||||
def 'http headers frame-options ALLOW-FROM'() {
|
def 'http headers frame-options ALLOW-FROM'() {
|
||||||
when:
|
when:
|
||||||
httpAutoConfig {
|
httpAutoConfig {
|
||||||
'headers'() {
|
'headers'() {
|
||||||
'frame-options'(policy : 'ALLOW-FROM', origin : 'https://example.com')
|
'frame-options'(policy : 'ALLOW-FROM', strategy: 'static', value : 'https://example.com')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
createAppContext()
|
createAppContext()
|
||||||
|
|
||||||
def hf = getFilter(HeadersFilter)
|
def hf = getFilter(HeadersFilter)
|
||||||
|
MockHttpServletResponse response = new MockHttpServletResponse()
|
||||||
|
hf.doFilter(new MockHttpServletRequest(), response, new MockFilterChain())
|
||||||
|
|
||||||
then:
|
then:
|
||||||
hf
|
assertHeaders(response, ['X-Frame-Options':'ALLOW-FROM https://example.com'])
|
||||||
hf.headers == ['X-Frame-Options':'ALLOW-FROM https://example.com']
|
|
||||||
}
|
}
|
||||||
|
|
||||||
def 'http headers header a=b'() {
|
def 'http headers header a=b'() {
|
||||||
|
@ -183,10 +187,11 @@ class HttpHeadersConfigTests extends AbstractHttpConfigTests {
|
||||||
createAppContext()
|
createAppContext()
|
||||||
|
|
||||||
def hf = getFilter(HeadersFilter)
|
def hf = getFilter(HeadersFilter)
|
||||||
|
MockHttpServletResponse response = new MockHttpServletResponse()
|
||||||
|
hf.doFilter(new MockHttpServletRequest(), response, new MockFilterChain())
|
||||||
|
|
||||||
then:
|
then:
|
||||||
hf
|
assertHeaders(response, ['a':'b'])
|
||||||
hf.headers == ['a':'b']
|
|
||||||
}
|
}
|
||||||
|
|
||||||
def 'http headers header a=b and c=d'() {
|
def 'http headers header a=b and c=d'() {
|
||||||
|
@ -200,10 +205,11 @@ class HttpHeadersConfigTests extends AbstractHttpConfigTests {
|
||||||
createAppContext()
|
createAppContext()
|
||||||
|
|
||||||
def hf = getFilter(HeadersFilter)
|
def hf = getFilter(HeadersFilter)
|
||||||
|
MockHttpServletResponse response = new MockHttpServletResponse()
|
||||||
|
hf.doFilter(new MockHttpServletRequest(), response, new MockFilterChain())
|
||||||
|
|
||||||
then:
|
then:
|
||||||
hf
|
assertHeaders(response , ['a':'b', 'c':'d'])
|
||||||
hf.headers.sort() == ['a':'b', 'c':'d'].sort()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
def 'http headers header no name produces error'() {
|
def 'http headers header no name produces error'() {
|
||||||
|
@ -216,7 +222,7 @@ class HttpHeadersConfigTests extends AbstractHttpConfigTests {
|
||||||
createAppContext()
|
createAppContext()
|
||||||
|
|
||||||
then:
|
then:
|
||||||
thrown(XmlBeanDefinitionStoreException)
|
thrown(BeanCreationException)
|
||||||
}
|
}
|
||||||
|
|
||||||
def 'http headers header no value produces error'() {
|
def 'http headers header no value produces error'() {
|
||||||
|
@ -229,7 +235,7 @@ class HttpHeadersConfigTests extends AbstractHttpConfigTests {
|
||||||
createAppContext()
|
createAppContext()
|
||||||
|
|
||||||
then:
|
then:
|
||||||
thrown(XmlBeanDefinitionStoreException)
|
thrown(BeanCreationException)
|
||||||
}
|
}
|
||||||
|
|
||||||
def 'http headers xss-protection defaults'() {
|
def 'http headers xss-protection defaults'() {
|
||||||
|
@ -242,10 +248,11 @@ class HttpHeadersConfigTests extends AbstractHttpConfigTests {
|
||||||
createAppContext()
|
createAppContext()
|
||||||
|
|
||||||
def hf = getFilter(HeadersFilter)
|
def hf = getFilter(HeadersFilter)
|
||||||
|
MockHttpServletResponse response = new MockHttpServletResponse()
|
||||||
|
hf.doFilter(new MockHttpServletRequest(), response, new MockFilterChain())
|
||||||
|
|
||||||
then:
|
then:
|
||||||
hf
|
assertHeaders(response, ['X-XSS-Protection':'1; mode=block'])
|
||||||
hf.headers == ['X-XSS-Protection':'1; mode=block']
|
|
||||||
}
|
}
|
||||||
|
|
||||||
def 'http headers xss-protection enabled=true'() {
|
def 'http headers xss-protection enabled=true'() {
|
||||||
|
@ -258,10 +265,11 @@ class HttpHeadersConfigTests extends AbstractHttpConfigTests {
|
||||||
createAppContext()
|
createAppContext()
|
||||||
|
|
||||||
def hf = getFilter(HeadersFilter)
|
def hf = getFilter(HeadersFilter)
|
||||||
|
MockHttpServletResponse response = new MockHttpServletResponse()
|
||||||
|
hf.doFilter(new MockHttpServletRequest(), response, new MockFilterChain())
|
||||||
|
|
||||||
then:
|
then:
|
||||||
hf
|
assertHeaders(response, ['X-XSS-Protection':'1; mode=block'])
|
||||||
hf.headers == ['X-XSS-Protection':'1; mode=block']
|
|
||||||
}
|
}
|
||||||
|
|
||||||
def 'http headers xss-protection enabled=false'() {
|
def 'http headers xss-protection enabled=false'() {
|
||||||
|
@ -274,10 +282,11 @@ class HttpHeadersConfigTests extends AbstractHttpConfigTests {
|
||||||
createAppContext()
|
createAppContext()
|
||||||
|
|
||||||
def hf = getFilter(HeadersFilter)
|
def hf = getFilter(HeadersFilter)
|
||||||
|
MockHttpServletResponse response = new MockHttpServletResponse()
|
||||||
|
hf.doFilter(new MockHttpServletRequest(), response, new MockFilterChain())
|
||||||
|
|
||||||
then:
|
then:
|
||||||
hf
|
assertHeaders(response, ['X-XSS-Protection':'0'])
|
||||||
hf.headers == ['X-XSS-Protection':'0']
|
|
||||||
}
|
}
|
||||||
|
|
||||||
def 'http headers xss-protection enabled=false and block=true produces error'() {
|
def 'http headers xss-protection enabled=false and block=true produces error'() {
|
||||||
|
@ -295,4 +304,11 @@ class HttpHeadersConfigTests extends AbstractHttpConfigTests {
|
||||||
BeanDefinitionParsingException e = thrown()
|
BeanDefinitionParsingException e = thrown()
|
||||||
e.message.contains '<xss-protection enabled="false"/> does not allow block="true".'
|
e.message.contains '<xss-protection enabled="false"/> does not allow block="true".'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def assertHeaders(MockHttpServletResponse response, Map<String,String> expected) {
|
||||||
|
assert response.headerNames == expected.keySet()
|
||||||
|
expected.each { headerName, value ->
|
||||||
|
assert response.getHeaderValues(headerName) == [value]
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -319,7 +319,7 @@
|
||||||
including it in a frame it is the same as the one serving the page.
|
including it in a frame it is the same as the one serving the page.
|
||||||
</para>
|
</para>
|
||||||
</section>
|
</section>
|
||||||
<section xml:id="nsa=frame-options-strategy">
|
<section xml:id="nsa-frame-options-strategy">
|
||||||
<title><literal>frame-options-strategy</literal></title>
|
<title><literal>frame-options-strategy</literal></title>
|
||||||
<para>
|
<para>
|
||||||
Select the <classname>AllowFromStrategy</classname> to use when using the ALLOW-FROM policy.
|
Select the <classname>AllowFromStrategy</classname> to use when using the ALLOW-FROM policy.
|
||||||
|
|
|
@ -3,6 +3,8 @@ package org.springframework.security.web.headers;
|
||||||
import javax.servlet.http.HttpServletRequest;
|
import javax.servlet.http.HttpServletRequest;
|
||||||
import javax.servlet.http.HttpServletResponse;
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
|
||||||
|
import org.springframework.util.Assert;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@code HeaderFactory} implementation which returns the same {@code Header} instance.
|
* {@code HeaderFactory} implementation which returns the same {@code Header} instance.
|
||||||
*
|
*
|
||||||
|
@ -14,6 +16,9 @@ public class StaticHeaderFactory implements HeaderFactory {
|
||||||
private final Header header;
|
private final Header header;
|
||||||
|
|
||||||
public StaticHeaderFactory(String name, String... values) {
|
public StaticHeaderFactory(String name, String... values) {
|
||||||
|
Assert.hasText(name, "Header name is required");
|
||||||
|
Assert.notEmpty(values, "Header values cannot be null or empty");
|
||||||
|
Assert.noNullElements(values, "Header values cannot contain null values");
|
||||||
header = new Header(name, values);
|
header = new Header(name, values);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -33,11 +33,10 @@ public class FrameOptionsHeaderFactory implements HeaderFactory {
|
||||||
this.allowFromStrategy=allowFromStrategy;
|
this.allowFromStrategy=allowFromStrategy;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public Header create(HttpServletRequest request, HttpServletResponse response) {
|
public Header create(HttpServletRequest request, HttpServletResponse response) {
|
||||||
if (ALLOW_FROM.equals(mode)) {
|
if (ALLOW_FROM.equals(mode)) {
|
||||||
String value = allowFromStrategy.apply(request);
|
String value = allowFromStrategy.apply(request);
|
||||||
return new Header(FRAME_OPTIONS_HEADER, value);
|
return new Header(FRAME_OPTIONS_HEADER, ALLOW_FROM + " " + value);
|
||||||
} else {
|
} else {
|
||||||
return new Header(FRAME_OPTIONS_HEADER, mode);
|
return new Header(FRAME_OPTIONS_HEADER, mode);
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,7 +10,6 @@ import javax.servlet.http.HttpServletRequest;
|
||||||
* To change this template use File | Settings | File Templates.
|
* To change this template use File | Settings | File Templates.
|
||||||
*/
|
*/
|
||||||
public class NullAllowFromStrategy implements AllowFromStrategy {
|
public class NullAllowFromStrategy implements AllowFromStrategy {
|
||||||
@Override
|
|
||||||
public String apply(HttpServletRequest request) {
|
public String apply(HttpServletRequest request) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,7 +24,6 @@ public abstract class RequestParameterAllowFromStrategy implements AllowFromStra
|
||||||
protected final Log log = LogFactory.getLog(getClass());
|
protected final Log log = LogFactory.getLog(getClass());
|
||||||
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String apply(HttpServletRequest request) {
|
public String apply(HttpServletRequest request) {
|
||||||
String from = request.getParameter(parameter);
|
String from = request.getParameter(parameter);
|
||||||
if (log.isDebugEnabled()) {
|
if (log.isDebugEnabled()) {
|
||||||
|
|
|
@ -14,7 +14,6 @@ public class StaticAllowFromStrategy implements AllowFromStrategy {
|
||||||
this.uri=uri;
|
this.uri=uri;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public String apply(HttpServletRequest request) {
|
public String apply(HttpServletRequest request) {
|
||||||
return uri.toString();
|
return uri.toString();
|
||||||
}
|
}
|
||||||
|
|
|
@ -84,7 +84,6 @@ public class HeadersFilterTest {
|
||||||
private String name;
|
private String name;
|
||||||
private String value;
|
private String value;
|
||||||
|
|
||||||
@Override
|
|
||||||
public Header create(HttpServletRequest request, HttpServletResponse response) {
|
public Header create(HttpServletRequest request, HttpServletResponse response) {
|
||||||
return new Header(name, value);
|
return new Header(name, value);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue