/* * Zed Attack Proxy (ZAP) and its related class files. * * ZAP is an HTTP/HTTPS proxy for assessing web application security. * * Copyright 2016 The ZAP Development Team * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.parosproxy.paros.core.scanner; import static org.hamcrest.Matchers.contains; import static org.hamcrest.Matchers.empty; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.is; import static org.junit.Assert.assertThat; import java.util.List; import org.hamcrest.BaseMatcher; import org.hamcrest.Description; import org.hamcrest.Matcher; import org.junit.Test; import org.parosproxy.paros.network.HttpHeaderField; import org.parosproxy.paros.network.HttpMalformedHeaderException; import org.parosproxy.paros.network.HttpMessage; import org.parosproxy.paros.network.HttpRequestHeader; /** * Unit test for {@link VariantHeader}. */ public class VariantHeaderUnitTest { @Test public void shouldHaveParametersListEmptyByDefault() { // Given VariantHeader variantHeader = new VariantHeader(); // When List<NameValuePair> parameters = variantHeader.getParamList(); // Then assertThat(parameters, is(empty())); } @Test(expected = UnsupportedOperationException.class) public void shouldNotAllowToModifyReturnedParametersList() { // Given VariantHeader variantHeader = new VariantHeader(); // When variantHeader.getParamList().add(header("Name", "Value", 0)); // Then = UnsupportedOperationException } @Test(expected = IllegalArgumentException.class) public void shouldFailToExtractParametersFromUndefinedMessage() { // Given VariantHeader variantHeader = new VariantHeader(); HttpMessage undefinedMessage = null; // When variantHeader.setMessage(undefinedMessage); // Then = IllegalArgumentException } @Test public void shouldNotExtractAnyParameterIfThereAreNoHeaders() { // Given VariantHeader variantHeader = new VariantHeader(); HttpMessage messageWithHeaders = createMessageWithoutInjectableHeaders(); // When variantHeader.setMessage(messageWithHeaders); // Then assertThat(variantHeader.getParamList(), is(empty())); } @Test public void shouldNotExtractAnyParameterIfThereAreNoInjectableHeaders() { // Given VariantHeader variantHeader = new VariantHeader(); HttpMessage messageWithHeaders = createMessageWithHeaders( header(HttpRequestHeader.CONTENT_LENGTH, "A"), header(HttpRequestHeader.PRAGMA, "1"), header(HttpRequestHeader.CACHE_CONTROL, "B"), header(HttpRequestHeader.COOKIE, "3"), header(HttpRequestHeader.AUTHORIZATION, "C"), header(HttpRequestHeader.PROXY_AUTHORIZATION, "5"), header(HttpRequestHeader.CONNECTION, "D"), header(HttpRequestHeader.PROXY_CONNECTION, "7"), header(HttpRequestHeader.IF_MODIFIED_SINCE, "E"), header(HttpRequestHeader.IF_NONE_MATCH, "9"), header(HttpRequestHeader.X_CSRF_TOKEN, "F"), header(HttpRequestHeader.X_CSRFTOKEN, "11"), header(HttpRequestHeader.X_XSRF_TOKEN, "G"), header(HttpRequestHeader.X_ZAP_SCAN_ID, "13"), header(HttpRequestHeader.X_ZAP_REQUESTID, "H"), header(HttpRequestHeader.X_SECURITY_PROXY, "15")); // When variantHeader.setMessage(messageWithHeaders); // Then assertThat(variantHeader.getParamList(), is(empty())); } @Test public void shouldExtractParametersFromInjectableHeaders() { // Given VariantHeader variantHeader = new VariantHeader(); HttpMessage messageWithHeaders = createMessageWithHeaders( header("X-Header-A", "X"), header("X-Header-B", "Y"), header("X-Header-C", "Z")); // When variantHeader.setMessage(messageWithHeaders); // Then assertThat(variantHeader.getParamList().size(), is(equalTo(3))); assertThat( variantHeader.getParamList(), contains(header("X-Header-A", "X", 0), header("X-Header-B", "Y", 1), header("X-Header-C", "Z", 2))); } @Test public void shouldExtractParametersFromInjectableHeadersEvenIfThereAreNoInjectableHeaders() { // Given VariantHeader variantHeader = new VariantHeader(); HttpMessage messageWithHeaders = createMessageWithHeaders( header(HttpRequestHeader.CONTENT_LENGTH, "A"), header("X-Header-A", "X"), header(HttpRequestHeader.CONNECTION, "D"), header("X-Header-B", "Y"), header(HttpRequestHeader.PROXY_AUTHORIZATION, "5"), header("X-Header-C", "Z")); // When variantHeader.setMessage(messageWithHeaders); // Then assertThat(variantHeader.getParamList().size(), is(equalTo(3))); assertThat( variantHeader.getParamList(), contains(header("X-Header-A", "X", 0), header("X-Header-B", "Y", 1), header("X-Header-C", "Z", 2))); } @Test public void shouldNotAccumulateExtractedParameters() { // Given VariantHeader variantHeader = new VariantHeader(); HttpMessage messageWithHeaders = createMessageWithHeaders( header("X-Header-A", "X"), header("X-Header-B", "Y"), header("X-Header-C", "Z")); HttpMessage otherMessageWithHeaders = createMessageWithHeaders( header("X-Header-D", "1"), header("X-Header-E", "2"), header("X-Header-F", "3")); // When variantHeader.setMessage(messageWithHeaders); variantHeader.setMessage(otherMessageWithHeaders); // Then assertThat(variantHeader.getParamList().size(), is(equalTo(3))); assertThat( variantHeader.getParamList(), contains(header("X-Header-D", "1", 0), header("X-Header-E", "2", 1), header("X-Header-F", "3", 2))); } @Test public void shouldInjectHeaderValueModification() { // Given VariantHeader variantHeader = new VariantHeader(); HttpMessage message = createMessageWithHeaders( header("X-Header-A", "X"), header("X-Header-B", "Y"), header("X-Header-C", "Z")); variantHeader.setMessage(message); // When String injectedHeader = variantHeader.setParameter(message, header("X-Header-A", "X", 0), "X-Header-A", "Value"); // Then assertThat(injectedHeader, is(equalTo("X-Header-A: Value"))); assertThat(message, containsHeader("X-Header-A", "Value")); } @Test public void shouldRemoveHeaderIfInjectedHeaderValueIsNull() { // Given VariantHeader variantHeader = new VariantHeader(); HttpMessage message = createMessageWithHeaders( header("X-Header-A", "X"), header("X-Header-B", "Y"), header("X-Header-C", "Z")); variantHeader.setMessage(message); // When String injectedHeader = variantHeader.setParameter(message, header("X-Header-A", "X", 0), "X-Header-A", null); // Then assertThat(injectedHeader, is(equalTo(""))); assertThat(message.getRequestHeader().getHeader("X-Header-A"), is((String) null)); } @Test public void shouldIgnoreChangesToHeaderName() { // Given VariantHeader variantHeader = new VariantHeader(); HttpMessage message = createMessageWithHeaders( header("X-Header-A", "X"), header("X-Header-B", "Y"), header("X-Header-C", "Z")); variantHeader.setMessage(message); // When String injectedHeader = variantHeader.setParameter(message, header("X-Header-A", "X", 0), "X-Header-Z", "X"); // Then assertThat(injectedHeader, is(equalTo("X-Header-A: X"))); assertThat(message, containsHeader("X-Header-A", "X")); } @Test public void shouldHaveSameEffectInjectingEscapedHeaderValueModification() { // Given VariantHeader variantHeader = new VariantHeader(); HttpMessage message = createMessageWithHeaders( header("X-Header-A", "X"), header("X-Header-B", "Y"), header("X-Header-C", "Z")); variantHeader.setMessage(message); // When String injectedHeader = variantHeader.setEscapedParameter(message, header("X-Header-A", "X", 0), "X-Header-A", "Value"); // Then assertThat(injectedHeader, is(equalTo("X-Header-A: Value"))); assertThat(message, containsHeader("X-Header-A", "Value")); } private static HttpMessage createMessageWithoutInjectableHeaders() { HttpMessage message = new HttpMessage(); try { message.setRequestHeader("GET / HTTP/1.1\r\n"); } catch (HttpMalformedHeaderException e) { throw new RuntimeException(e); } return message; } private static HttpMessage createMessageWithHeaders(NameValuePair... headers) { HttpMessage message = new HttpMessage(); try { StringBuilder requestHeaderBuilder = new StringBuilder("GET / HTTP/1.1\r\n"); for (NameValuePair header : headers) { requestHeaderBuilder.append(header.getName()); requestHeaderBuilder.append(": "); requestHeaderBuilder.append(header.getValue()); requestHeaderBuilder.append("\r\n"); } message.setRequestHeader(requestHeaderBuilder.toString()); } catch (HttpMalformedHeaderException e) { throw new RuntimeException(e); } return message; } private static NameValuePair header(String name, String value) { return new NameValuePair(NameValuePair.TYPE_HEADER, name, value, 0); } private static NameValuePair header(String name, String value, int position) { return new NameValuePair(NameValuePair.TYPE_HEADER, name, value, position); } private static Matcher<HttpMessage> containsHeader(final String name, final String value) { return new BaseMatcher<HttpMessage>() { @Override public boolean matches(Object actualValue) { HttpMessage message = (HttpMessage) actualValue; List<HttpHeaderField> headers = message.getRequestHeader().getHeaders(); if (headers.isEmpty()) { return false; } for (HttpHeaderField header : headers) { if (name.equals(header.getName()) && value.equals(header.getValue())) { return true; } } return false; } @Override public void describeTo(Description description) { description.appendText("header ").appendValue(name + ": " + value); } public void describeMismatch(Object item, Description description) { HttpMessage message = (HttpMessage) item; List<HttpHeaderField> headers = message.getRequestHeader().getHeaders(); if (headers.isEmpty()) { description.appendText("has no headers"); } else { description.appendText("was ").appendValue(headers); } } }; } }