diff --git a/src/main/java/org/owasp/esapi/filters/SecurityWrapperResponse.java b/src/main/java/org/owasp/esapi/filters/SecurityWrapperResponse.java index f05682cba..9702677f9 100644 --- a/src/main/java/org/owasp/esapi/filters/SecurityWrapperResponse.java +++ b/src/main/java/org/owasp/esapi/filters/SecurityWrapperResponse.java @@ -491,10 +491,16 @@ public void setDateHeader(String name, long date) { * linear white space with a single SP before interpreting the field value * or forwarding the message downstream." * http://www.w3.org/Protocols/rfc2616/rfc2616-sec2.html#sec2.2 - * @param name - * @param value + *
+ * As in the Servlet specification, a {@code null} name is ignored and a + * {@code null} value removes the header. + * @param name the header name; if {@code null}, nothing is done + * @param value the header value; if {@code null}, the header is removed */ public void setHeader(String name, String value) { + if (name == null) { + return; + } SecurityConfiguration sc = ESAPI.securityConfiguration(); String strippedName = StringUtilities.stripControls(name); String strippedValue = StringUtilities.stripControls(value); @@ -506,6 +512,13 @@ public void setHeader(String name, String value) { logger.warning(Logger.SECURITY_FAILURE, "Attempt to set invalid header NAME denied: HTTPHeaderName:"+ name, e); } + if (value == null) { + if (StringUtilities.notNullOrEmpty(safeName, true)) { + getHttpServletResponse().setHeader(safeName, null); + } + return; + } + try { safeValue = ESAPI.validator().getValidInput("setHeader", strippedValue, "HTTPHeaderValue", sc.getIntProp("HttpUtilities.MaxHeaderValueSize"), false); } catch (ValidationException e) { diff --git a/src/test/java/org/owasp/esapi/filters/SecurityWrapperResponseTest.java b/src/test/java/org/owasp/esapi/filters/SecurityWrapperResponseTest.java index f7b74c6f1..5b3c7ef60 100644 --- a/src/test/java/org/owasp/esapi/filters/SecurityWrapperResponseTest.java +++ b/src/test/java/org/owasp/esapi/filters/SecurityWrapperResponseTest.java @@ -75,6 +75,36 @@ public void setup() throws Exception { } } + @Test + public void testSetHeaderNullValueRemovesHeader() throws Exception { + PowerMockito.when(mockValidator.getValidInput(anyString(), ArgumentMatchers.eq(goodHeaderName), + ArgumentMatchers.eq(HEADER_NAME_CONTEXT), + ArgumentMatchers.eq(SecurityWrapperRequestTest.SECURITY_CONFIGURATION_TEST_LENGTH), + ArgumentMatchers.eq(false))).thenReturn(goodHeaderName); + + PowerMockito.when(mockSecConfig.getIntProp(SEC_CTX_MAX_HEADER_NAME_SIZE_ATTR)).thenReturn( + SecurityWrapperRequestTest.SECURITY_CONFIGURATION_TEST_LENGTH); + + SecurityWrapperResponse response = new SecurityWrapperResponse(mockResponse); + + response.setHeader(goodHeaderName, null); + + verify(mockValidator, times(0)).getValidInput(anyString(), ArgumentMatchers.isNull(), ArgumentMatchers.eq(HEADER_VALUE_CONTEXT), ArgumentMatchers.anyInt(), ArgumentMatchers.anyBoolean()); + verify(mockResponse, times(1)).setHeader(goodHeaderName, null); + verify(mockLogger, times(0)).warning(ArgumentMatchers.any(org.owasp.esapi.Logger.EventType.class), anyString(), ArgumentMatchers.any(Exception.class)); + } + + @Test + public void testSetHeaderNullNameIsIgnored() throws Exception { + SecurityWrapperResponse response = new SecurityWrapperResponse(mockResponse); + + response.setHeader(null, goodHeaderValue); + + verify(mockValidator, times(0)).getValidInput(anyString(), ArgumentMatchers.any(), anyString(), ArgumentMatchers.anyInt(), ArgumentMatchers.anyBoolean()); + verify(mockResponse, times(0)).setHeader(ArgumentMatchers.any(), ArgumentMatchers.any()); + verify(mockLogger, times(0)).warning(ArgumentMatchers.any(org.owasp.esapi.Logger.EventType.class), anyString(), ArgumentMatchers.any(Exception.class)); + } + @Test public void testSetHeaderHappyPath() throws Exception { String validateNameResponse = goodHeaderName;