/* * 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.nullValue; import static org.hamcrest.Matchers.is; import static org.junit.Assert.assertThat; import java.util.List; import java.util.Vector; import org.hamcrest.BaseMatcher; import org.hamcrest.Description; import org.hamcrest.Matcher; import org.junit.Test; import org.parosproxy.paros.network.HttpHeader; import org.parosproxy.paros.network.HttpMalformedHeaderException; import org.parosproxy.paros.network.HttpMessage; /** * Unit test for {@link VariantCookie}. */ public class VariantCookieUnitTest { @Test public void shouldHaveParametersListEmptyByDefault() { // Given VariantCookie variantCookie = new VariantCookie(); // When List<NameValuePair> parameters = variantCookie.getParamList(); // Then assertThat(parameters, is(empty())); } @Test(expected = UnsupportedOperationException.class) public void shouldNotAllowToModifyReturnedParametersList() { // Given VariantCookie variantCookie = new VariantCookie(); // When variantCookie.getParamList().add(cookie("Name", "Value", 0)); // Then = UnsupportedOperationException } @Test(expected = IllegalArgumentException.class) public void shouldFailToExtractParametersFromUndefinedMessage() { // Given VariantCookie variantCookie = new VariantCookie(); HttpMessage undefinedMessage = null; // When variantCookie.setMessage(undefinedMessage); // Then = IllegalArgumentException } @Test public void shouldNotExtractAnyParameterIfThereAreNoCookieHeaders() { // Given VariantCookie variantCookie = new VariantCookie(); HttpMessage messageWithCookies = createMessageWithoutCookies(); // When variantCookie.setMessage(messageWithCookies); // Then assertThat(variantCookie.getParamList(), is(empty())); } @Test public void shouldNotExtractAnyParameterIfTheCookieHeadersDontHaveCoookies() { // Given VariantCookie variantCookie = new VariantCookie(); HttpMessage messageWithCookies = createMessageWithCookies("", ""); // When variantCookie.setMessage(messageWithCookies); // Then assertThat(variantCookie.getParamList(), is(empty())); } @Test public void shouldExtractParametersFromWellformedCookieHeader() { // Given VariantCookie variantCookie = new VariantCookie(); HttpMessage messageWithCookies = createMessageWithCookies("a=b; c=\"d\"; e=f"); // When variantCookie.setMessage(messageWithCookies); // Then assertThat(variantCookie.getParamList().size(), is(equalTo(3))); assertThat(variantCookie.getParamList(), contains(cookie("a", "b", 0), cookie("c", "\"d\"", 1), cookie("e", "f", 2))); } @Test public void shouldExtractParametersFromWellformedCookieHeaders() { // Given VariantCookie variantCookie = new VariantCookie(); HttpMessage messageWithCookies = createMessageWithCookies("a=b; c=d; e=f", "g=h; i=j; k=l"); // When variantCookie.setMessage(messageWithCookies); // Then assertThat(variantCookie.getParamList().size(), is(equalTo(6))); assertThat(variantCookie.getParamList(), contains( cookie("a", "b", 0), cookie("c", "d", 1), cookie("e", "f", 2), cookie("g", "h", 3), cookie("i", "j", 4), cookie("k", "l", 5))); } @Test public void shouldExtractParametersFromMalformedCookieHeader() { // Given VariantCookie variantCookie = new VariantCookie(); HttpMessage messageWithCookies = createMessageWithCookies("a=; =d;e;g=\"h; i=j\""); // When variantCookie.setMessage(messageWithCookies); // Then assertThat(variantCookie.getParamList().size(), is(equalTo(5))); assertThat(variantCookie.getParamList(), contains( cookie("a", "", 0), cookie("", "d", 1), cookie("e", null, 2), cookie("g", "\"h", 3), cookie("i", "j\"", 4))); } @Test public void shouldExtractParametersFromMalformedCookieHeaders() { // Given VariantCookie variantCookie = new VariantCookie(); HttpMessage messageWithCookies = createMessageWithCookies("a=;=d; e", "g; =j;l=", "n=\"", "=\""); // When variantCookie.setMessage(messageWithCookies); // Then assertThat(variantCookie.getParamList().size(), is(equalTo(8))); assertThat( variantCookie.getParamList(), contains( cookie("a", "", 0), cookie("", "d", 1), cookie("e", null, 2), cookie("g", null, 3), cookie("", "j", 4), cookie("l", "", 5), cookie("n", "\"", 6), cookie("", "\"", 7))); } @Test public void shouldDecodeValueFromExtractedParameters() { // Given VariantCookie variantCookie = new VariantCookie(); HttpMessage messageWithCookies = createMessageWithCookies("a=b; c=d; e=%26%27%28%29%2A", "=%27"); // When variantCookie.setMessage(messageWithCookies); // Then assertThat(variantCookie.getParamList().size(), is(equalTo(4))); assertThat( variantCookie.getParamList(), contains(cookie("a", "b", 0), cookie("c", "d", 1), cookie("e", "&'()*", 2), cookie("", "'", 3))); } @Test public void shouldNotDecodeNameFromExtractedParameters() { // Given VariantCookie variantCookie = new VariantCookie(); HttpMessage messageWithCookies = createMessageWithCookies("%29=b; c=d; e=f", "%26"); // When variantCookie.setMessage(messageWithCookies); // Then assertThat(variantCookie.getParamList().size(), is(equalTo(4))); assertThat( variantCookie.getParamList(), contains(cookie("%29", "b", 0), cookie("c", "d", 1), cookie("e", "f", 2), cookie("%26", null, 3))); } @Test public void shouldNotAccumulateExtractedParameters() { // Given VariantCookie variantCookie = new VariantCookie(); HttpMessage messageWithCookies = createMessageWithCookies("a=b; c=d; e=f"); HttpMessage otherMessageWithCookies = createMessageWithCookies("g=h; i=j; k=l"); // When variantCookie.setMessage(messageWithCookies); variantCookie.setMessage(otherMessageWithCookies); // Then assertThat(variantCookie.getParamList().size(), is(equalTo(3))); assertThat(variantCookie.getParamList(), contains(cookie("g", "h", 0), cookie("i", "j", 1), cookie("k", "l", 2))); } @Test public void shouldInjectCookieModificationOnWellformedHeader() { // Given VariantCookie variantCookie = new VariantCookie(); HttpMessage message = createMessageWithCookies("a=b; c=d; e=f"); variantCookie.setMessage(message); // When String injectedCookie = variantCookie.setParameter(message, cookie("a", "b", 0), "y", "z"); // Then assertThat(injectedCookie, is(equalTo("y=z"))); assertThat(message, containsCookieHeader("y=z; c=d; e=f")); } @Test public void shouldInjectCookieModificationOnMalformedHeader() { // Given VariantCookie variantCookie = new VariantCookie(); HttpMessage message = createMessageWithCookies("a; =b; =d; e=;"); variantCookie.setMessage(message); // When String injectedCookie = variantCookie.setParameter(message, cookie("b", null, 1), "y", "z"); // Then assertThat(injectedCookie, is(equalTo("y=z"))); assertThat(message, containsCookieHeader("a; y=z; =d; e=")); } @Test public void shouldInjectUnescapedCookieValueModification() { // Given VariantCookie variantCookie = new VariantCookie(); HttpMessage message = createMessageWithCookies("a=b; c=d; e=f"); variantCookie.setMessage(message); // When String injectedCookie = variantCookie.setParameter(message, cookie("a", "b", 0), "y", "&'()"); // Then assertThat(injectedCookie, is(equalTo("y=%26%27%28%29"))); assertThat(message, containsCookieHeader("y=%26%27%28%29; c=d; e=f")); } @Test public void shouldInjectEscapedCookieValueModification() { // Given VariantCookie variantCookie = new VariantCookie(); HttpMessage message = createMessageWithCookies("a=b; c=d; e=f"); variantCookie.setMessage(message); // When String injectedCookie = variantCookie.setEscapedParameter(message, cookie("a", "b", 0), "y", "%26%27%28%29"); // Then assertThat(injectedCookie, is(equalTo("y=%26%27%28%29"))); assertThat(message, containsCookieHeader("y=%26%27%28%29; c=d; e=f")); } @Test public void shouldIgnorePreviouslyInjectCookieModifications() { // Given VariantCookie variantCookie = new VariantCookie(); HttpMessage message = createMessageWithCookies("a=b; c=d; e=f"); variantCookie.setMessage(message); // When String previouslyInjectedCookie = variantCookie.setParameter(message, cookie("a", "b", 0), "y", "z"); String injectedCookie = variantCookie.setParameter(message, cookie("e", "f", 2), "i", "j"); // Then assertThat(previouslyInjectedCookie, is(equalTo("y=z"))); assertThat(injectedCookie, is(equalTo("i=j"))); assertThat(message, containsCookieHeader("a=b; c=d; i=j")); } @Test public void shouldMergeCookieHeadersWhenInjectingCookieModifications() { // Given VariantCookie variantCookie = new VariantCookie(); HttpMessage message = createMessageWithCookies("a=b", "c=d", "e=f"); variantCookie.setMessage(message); // When String injectedCookie = variantCookie.setParameter(message, cookie("c", "d", 1), "y", "z"); // Then assertThat(injectedCookie, is(equalTo("y=z"))); assertThat(message, containsCookieHeader("a=b; y=z; e=f")); } @Test public void shouldIgnoreNameOfCookieAndUsePositionWhenInjectingCookieModifications() { // Given VariantCookie variantCookie = new VariantCookie(); HttpMessage message = createMessageWithCookies("a=b; c=d"); variantCookie.setMessage(message); // When String injectedCookie = variantCookie.setParameter(message, cookie("DifferentName", "d", 1), "y", "z"); // Then assertThat(injectedCookie, is(equalTo("y=z"))); assertThat(message, containsCookieHeader("a=b; y=z")); } @Test public void shouldIgnoreValueOfOriginalCookieAndUsePositionWhenInjectingCookieModifications() { // Given VariantCookie variantCookie = new VariantCookie(); HttpMessage message = createMessageWithCookies("a=b; c=d"); variantCookie.setMessage(message); // When String injectedCookie = variantCookie.setParameter(message, cookie("c", "DifferentValue", 1), "y", "z"); // Then assertThat(injectedCookie, is(equalTo("y=z"))); assertThat(message, containsCookieHeader("a=b; y=z")); } @Test public void shouldNotInjectCookieModificationsIfPositionOfCookieDoesNotExist() { // Given VariantCookie variantCookie = new VariantCookie(); HttpMessage message = createMessageWithCookies("a=b; c=d"); variantCookie.setMessage(message); // When variantCookie.setParameter(message, cookie("c", "d", 3), "y", "z"); // Then assertThat(message, containsCookieHeader("a=b; c=d")); } @Test public void shouldRemoveCookieNameIfNameNotInjected() { // Given VariantCookie variantCookie = new VariantCookie(); HttpMessage message = createMessageWithCookies("a=b; c=d; e=f"); variantCookie.setMessage(message); // When String injectedCookie = variantCookie.setParameter(message, cookie("e", "f", 2), null, "z"); // Then assertThat(injectedCookie, is(equalTo("=z"))); assertThat(message, containsCookieHeader("a=b; c=d; =z")); } @Test public void shouldRemoveCookieValueIfValueNotInjected() { // Given VariantCookie variantCookie = new VariantCookie(); HttpMessage message = createMessageWithCookies("a=b; c=d; e=f"); variantCookie.setMessage(message); // When String injectedCookie = variantCookie.setParameter(message, cookie("c", "d", 1), "c", null); // Then assertThat(injectedCookie, is(equalTo("c"))); assertThat(message, containsCookieHeader("a=b; c; e=f")); } @Test public void shouldRemoveCookieIfNameAndValueAreNotInjected() { // Given VariantCookie variantCookie = new VariantCookie(); HttpMessage message = createMessageWithCookies("a=b; c=d; e=f"); variantCookie.setMessage(message); // When String injectedCookie = variantCookie.setParameter(message, cookie("c", "d", 1), null, null); // Then assertThat(injectedCookie, is(nullValue())); assertThat(message, containsCookieHeader("a=b; e=f")); } @Test public void shouldRemoveCookieHeaderIfOnlyCookieIsRemoved() { // Given VariantCookie variantCookie = new VariantCookie(); HttpMessage message = createMessageWithCookies("a=b"); variantCookie.setMessage(message); // When String injectedCookie = variantCookie.setParameter(message, cookie("a", "b", 0), null, null); // Then assertThat(injectedCookie, is(nullValue())); assertThat(message, hasNoCookieHeaders()); } private static HttpMessage createMessageWithoutCookies() { HttpMessage message = new HttpMessage(); try { message.setRequestHeader("GET / HTTP/1.1\r\nHost: example.com\r\n"); } catch (HttpMalformedHeaderException e) { throw new RuntimeException(e); } return message; } private static HttpMessage createMessageWithCookies(String... cookieHeaders) { HttpMessage message = new HttpMessage(); try { StringBuilder requestHeaderBuilder = new StringBuilder("GET / HTTP/1.1\r\nHost: example.com\r\n"); for (String cookieHeader : cookieHeaders) { requestHeaderBuilder.append("Cookie: "); requestHeaderBuilder.append(cookieHeader); requestHeaderBuilder.append("\r\n"); } message.setRequestHeader(requestHeaderBuilder.toString()); } catch (HttpMalformedHeaderException e) { throw new RuntimeException(e); } return message; } private static NameValuePair cookie(String name, String value, int position) { return new NameValuePair(NameValuePair.TYPE_COOKIE, name, value, position); } private static Matcher<HttpMessage> containsCookieHeader(final String cookies) { return new BaseMatcher<HttpMessage>() { @Override public boolean matches(Object actualValue) { HttpMessage message = (HttpMessage) actualValue; Vector<String> cookieLines = message.getRequestHeader().getHeaders(HttpHeader.COOKIE); if (cookieLines == null || cookieLines.size() != 1) { return false; } return cookies.equals(cookieLines.get(0)); } @Override public void describeTo(Description description) { description.appendText("cookie header ").appendValue(cookies); } public void describeMismatch(Object item, Description description) { HttpMessage message = (HttpMessage) item; Vector<String> cookieLines = message.getRequestHeader().getHeaders(HttpHeader.COOKIE); if (cookieLines == null) { description.appendText("has no cookie headers"); } else if (cookieLines.size() == 1) { description.appendText("was ").appendValue(cookieLines.get(0)); } else { description.appendText("has multiple cookie headers ").appendValue(cookieLines); } } }; } private static Matcher<HttpMessage> hasNoCookieHeaders() { return new BaseMatcher<HttpMessage>() { @Override public boolean matches(Object actualValue) { HttpMessage message = (HttpMessage) actualValue; Vector<String> cookieLines = message.getRequestHeader().getHeaders(HttpHeader.COOKIE); if (cookieLines == null || cookieLines.isEmpty()) { return true; } return false; } @Override public void describeTo(Description description) { description.appendText("no cookie header"); } public void describeMismatch(Object item, Description description) { HttpMessage message = (HttpMessage) item; Vector<String> cookieLines = message.getRequestHeader().getHeaders(HttpHeader.COOKIE); if (cookieLines.size() == 1) { description.appendText("has one cookie header ").appendValue(cookieLines.get(0)); } else { description.appendText("has multiple cookie headers ").appendValue(cookieLines); } } }; } }