package org.zaproxy.zap.spider; import org.apache.commons.httpclient.URI; import org.apache.commons.httpclient.URIException; import org.apache.log4j.Logger; import org.apache.log4j.varia.NullAppender; import org.junit.BeforeClass; import org.junit.Test; import org.zaproxy.zap.spider.SpiderParam.HandleParametersOption; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.is; import static org.junit.Assert.assertThat; /** * This test ensure that nothing was broken in the handling of normal URLs during * the implementation of OData support as well as ensure that the OData support is correct. * * It checks the canonicalization mechanism used to verify if a URL has already * been visited before during the spider phase. */ public class URLCanonicalizerUnitTest { @BeforeClass public static void suppressLogging() { Logger.getLogger(URLCanonicalizer.class).addAppender(new NullAppender()); } @Test public void shouldRemoveDefaultPortOfHttpUriWhenCanonicalizing() { // Given String uri = "http://example.com:80/"; // When String canonicalizedUri = URLCanonicalizer.getCanonicalURL(uri); // Then assertThat(canonicalizedUri, is(equalTo("http://example.com/"))); } @Test public void shouldNotRemoveNonDefaultPortOfHttpUriWhenCanonicalizing() { // Given String uri = "http://example.com:443/"; // When String canonicalizedUri = URLCanonicalizer.getCanonicalURL(uri); // Then assertThat(canonicalizedUri, is(equalTo("http://example.com:443/"))); } @Test public void shouldRemoveDefaultPortOfHttpsUriWhenCanonicalizing() { // Given String uri = "https://example.com:443/"; // When String canonicalizedUri = URLCanonicalizer.getCanonicalURL(uri); // Then assertThat(canonicalizedUri, is(equalTo("https://example.com/"))); } @Test public void shouldNotRemoveNonDefaultPortOfHttpsUriWhenCanonicalizing() { // Given String uri = "https://example.com:80/"; // When String canonicalizedUri = URLCanonicalizer.getCanonicalURL(uri); // Then assertThat(canonicalizedUri, is(equalTo("https://example.com:80/"))); } @Test public void shouldAddEmptyPathIfUriHasNoPathWhenCanonicalizing() { // Given String uri = "http://example.com"; // When String canonicalizedUri = URLCanonicalizer.getCanonicalURL(uri); // Then assertThat(canonicalizedUri, is(equalTo("http://example.com/"))); } @Test public void shouldCanonicalizeURIsWithAuthority() { // Given String[] uris = { "http://example.com/", "https://example.com/", "ftp://example.com/" }; for (String uri : uris) { // When String canonicalizedUri = URLCanonicalizer.getCanonicalURL(uri); // Then assertThat(canonicalizedUri, canonicalizedUri, is(equalTo(uri))); } } @Test public void shouldUseBaseURIToResolveRelativeURIsWhenCanonicalizing() { // Given String baseURI = "http://example.com/path/"; String[] relativeURIs = { "relative", "a/b/c", "../", "/absolute/path", "" }; String[] expectedCanonicalURIs = { "http://example.com/path/relative", "http://example.com/path/a/b/c", "http://example.com/", "http://example.com/absolute/path", "http://example.com/path/", }; for (int i = 0; i < relativeURIs.length; i++) { // When String canonicalizedUri = URLCanonicalizer.getCanonicalURL(relativeURIs[i], baseURI); // Then assertThat(canonicalizedUri, canonicalizedUri, is(equalTo(expectedCanonicalURIs[i]))); } } @Test public void shouldNormaliseEmptyAndDotPathSegmentsWhenCanonicalizing() { // Given String[] uris = { "http://example.com/../../x", "http://example.com/a//b/c//", "http://example.com/a/./b/./c", "http://example.com/.." }; String[] expectedCanonicalURIs = { "http://example.com/x", "http://example.com/a/b/c/", "http://example.com/a/b/c", "http://example.com/.." }; for (int i = 0; i < uris.length; i++) { // When String canonicalizedUri = URLCanonicalizer.getCanonicalURL(uris[i]); // Then assertThat(canonicalizedUri, canonicalizedUri, is(equalTo(expectedCanonicalURIs[i]))); } } @Test public void shouldIgnoreURIsWithNoAuthority() { // Given String[] uris = { "javascript:ignore()", "mailto:ignore@example.com" }; for (String uri : uris) { // When String canonicalizedUri = URLCanonicalizer.getCanonicalURL(uri); // Then assertThat(canonicalizedUri, canonicalizedUri, is(equalTo(null))); } } @Test public void shouldReturnCanonicalUriWithPercentEncodedPath() throws URIException { // Given String uri = new URI("http://example.com/path/%C3%A1/", true).toString(); // When String canonicalizedUri = URLCanonicalizer.getCanonicalURL(uri); // Then assertThat(canonicalizedUri, is(equalTo("http://example.com/path/%C3%A1/"))); } @Test public void shouldReturnCanonicalUriWithPercentEncodedQuery() throws URIException { // Given String uri = new URI("http://example.com/path/?par%C3%A2m=v%C3%A3lue", true).toString(); // When String canonicalizedUri = URLCanonicalizer.getCanonicalURL(uri); // Then assertThat(canonicalizedUri, is(equalTo("http://example.com/path/?par%C3%A2m=v%C3%A3lue"))); } @Test public void shouldCorrectlyParseQueryParameterNamesAndValuesWithAmpersandsAndEqualsWhenCanonicalizing() throws URIException { // Given String uri = new URI("http://example.com/?par%26am%3D1=val%26u%3De1", true).toString(); // When String canonicalizedUri = URLCanonicalizer.getCanonicalURL(uri); // Then assertThat(canonicalizedUri, is(equalTo("http://example.com/?par%26am%3D1=val%26u%3De1"))); } @Test public void shouldPreserveQueryParametersWithSameNameWhenCanonicalizing() throws URIException { // Given String uri = new URI("http://example.com/?name1=value1.1&name1=value1.2&name2=value2&name2=value3", true).toString(); // When String canonicalizedUri = URLCanonicalizer.getCanonicalURL(uri); // Then assertThat(canonicalizedUri, is(equalTo("http://example.com/?name1=value1.1&name1=value1.2&name2=value2&name2=value3"))); } @Test public void shouldSortQueryParametersByNameAndValueWhenCanonicalizing() throws URIException { // Given String uri = new URI("http://example.com/?&name2=value2&name3=value3&name1=value1.2&name1=value1.1", true).toString(); // When String canonicalizedUri = URLCanonicalizer.getCanonicalURL(uri); // Then assertThat(canonicalizedUri, is(equalTo("http://example.com/?name1=value1.1&name1=value1.2&name2=value2&name3=value3"))); } @Test public void shouldReturnPercentEncodedUriWhenCleaningParametersIn_USE_ALL_mode() throws URIException { // Given URI uri = new URI("http://example.com/path/%C3%A1/?par%C3%A2m=v%C3%A3lue", true); // When String cleanedUri = URLCanonicalizer.buildCleanedParametersURIRepresentation( uri, HandleParametersOption.USE_ALL, false); // Then assertThat(cleanedUri, is(equalTo("http://example.com/path/%C3%A1/?par%C3%A2m=v%C3%A3lue"))); } @Test public void shouldReturnPercentEncodedUriWhenCleaningParametersIn_IGNORE_VALUE_mode() throws URIException { // Given URI uri = new URI("http://example.com/path/%C3%A1/?par%C3%A2m=v%C3%A3lue1", true); // When String cleanedUri = URLCanonicalizer.buildCleanedParametersURIRepresentation( uri, HandleParametersOption.IGNORE_VALUE, false); // Then assertThat(cleanedUri, is(equalTo("http://example.com/path/%C3%A1/?par%C3%A2m"))); } @Test public void shouldReturnPercentEncodedUriWhenCleaningParametersIn_IGNORE_COMPLETELY_mode() throws URIException { // Given URI uri = new URI("http://example.com/path/%C3%A1/?par%C3%A2m=v%C3%A3lue", true); // When String cleanedUri = URLCanonicalizer.buildCleanedParametersURIRepresentation( uri, HandleParametersOption.IGNORE_COMPLETELY, false); // Then assertThat(cleanedUri, is(equalTo("http://example.com/path/%C3%A1/"))); } @Test public void shouldCorrectlyParseQueryParamNamesAndValuesWithAmpersandsAndEqualsWhenCleaningParametersIn_USE_ALL_mode() throws URIException { // Given URI uri = new URI("http://example.com/path/?par%3Dam1=val%26ue1&par%26am2=val%3Due2", true); // When String cleanedUri = URLCanonicalizer.buildCleanedParametersURIRepresentation( uri, HandleParametersOption.USE_ALL, false); // Then assertThat(cleanedUri, is(equalTo("http://example.com/path/?par%3Dam1=val%26ue1&par%26am2=val%3Due2"))); } @Test public void shouldCorrectlyParseQueryParamNamesAndValuesWithAmpersandsAndEqualsWhenCleaningParametersIn_IGNORE_VALUE_mode() throws URIException { // Given URI uri = new URI("http://example.com/path/?par%3Dam1=val%26ue1&par%26am2=val%3Due2", true); // When String cleanedUri = URLCanonicalizer.buildCleanedParametersURIRepresentation( uri, HandleParametersOption.IGNORE_VALUE, false); // Then assertThat(cleanedUri, is(equalTo("http://example.com/path/?par%26am2&par%3Dam1"))); } @Test public void shouldCorrectlyParseQueryParamNamesAndValuesWithAmpersandsAndEqualsWhenCleaningParametersIn_IGNORE_COMPLETELY_mode() throws URIException { // Given URI uri = new URI("http://example.com/path/?par%3Dam1=val%26ue1&par%26am2=val%3Due2", true); // When String cleanedUri = URLCanonicalizer.buildCleanedParametersURIRepresentation( uri, HandleParametersOption.IGNORE_COMPLETELY, false); // Then assertThat(cleanedUri, is(equalTo("http://example.com/path/"))); } @Test public void shouldPreserveQueryParametersWithSameNameWhenCleaningParametersIn_USE_ALL_mode() throws URIException { // Given URI uri = new URI("http://example.com/path/?param%5B%5D=value1.1¶m%5B%5D=value1.2¶m2=value2", true); // When String cleanedUri = URLCanonicalizer .buildCleanedParametersURIRepresentation(uri, HandleParametersOption.USE_ALL, false); // Then assertThat(cleanedUri, is(equalTo("http://example.com/path/?param%5B%5D=value1.1¶m%5B%5D=value1.2¶m2=value2"))); } @Test public void shouldKeepJustOneQueryParameterWithSameNameWhenCleaningParametersIn_IGNORE_VALUE_mode() throws URIException { // Given URI uri = new URI("http://example.com/path/?param1=value1.1¶m1=value1.2¶m2=value2", true); // When String cleanedUri = URLCanonicalizer .buildCleanedParametersURIRepresentation(uri, HandleParametersOption.IGNORE_VALUE, false); // Then assertThat(cleanedUri, is(equalTo("http://example.com/path/?param1¶m2"))); } @Test public void shouldRemoveAllQueryParametersWhenCleaningParametersIn_IGNORE_COMPLETELY_mode() throws URIException { // Given URI uri = new URI("http://example.com/path/?param1=value1.1¶m1=value1.2¶m2=value2", true); // When String cleanedUri = URLCanonicalizer .buildCleanedParametersURIRepresentation(uri, HandleParametersOption.IGNORE_COMPLETELY, false); // Then assertThat(cleanedUri, is(equalTo("http://example.com/path/"))); } // Test of the legacy behavior @Test public void shouldCanonicalizeNormalURLWithoutParametersIn_USE_ALL_mode() throws URIException { URI uri = new URI("http", null,"host", 9001, "/myservlet"); String visitedURI = URLCanonicalizer.buildCleanedParametersURIRepresentation(uri, HandleParametersOption.USE_ALL, false /* handleODataParametersVisited */); assertThat(visitedURI, is("http://host:9001/myservlet")); } @Test public void shouldCanonicalizeNormalURLWithoutParametersIn_IGNORE_COMPLETELY_mode() throws URIException { URI uri = new URI("http", null,"host", 9001, "/myservlet"); String visitedURI = URLCanonicalizer.buildCleanedParametersURIRepresentation(uri, HandleParametersOption.IGNORE_COMPLETELY, false /* handleODataParametersVisited */); assertThat(visitedURI, is("http://host:9001/myservlet")); } @Test public void shouldCanonicalizeNormalURLWithoutParametersIn_IGNORE_VALUE_mode() throws URIException { URI uri = new URI("http", null,"host", 9001, "/myservlet"); String visitedURI = URLCanonicalizer.buildCleanedParametersURIRepresentation(uri, HandleParametersOption.IGNORE_VALUE, false /* handleODataParametersVisited */); assertThat(visitedURI, is("http://host:9001/myservlet")); } @Test public void shouldCanonicalizeNormalURLWithParametersIn_USE_ALL_mode() throws URIException { URI uri = new URI("http", null,"host", 9001, "/myservlet","p1=2&p2=myparam"); String visitedURI = URLCanonicalizer.buildCleanedParametersURIRepresentation(uri, HandleParametersOption.USE_ALL, false /* handleODataParametersVisited */); assertThat(visitedURI, is("http://host:9001/myservlet?p1=2&p2=myparam")); } @Test public void shouldCanonicalizeNormalURLWithParametersIn_IGNORE_COMPLETELY_mode() throws URIException { URI uri = new URI("http", null,"host", 9001, "/myservlet","p1=2&p2=myparam"); String visitedURI = URLCanonicalizer.buildCleanedParametersURIRepresentation(uri, HandleParametersOption.IGNORE_COMPLETELY, false /* handleODataParametersVisited */); assertThat(visitedURI, is("http://host:9001/myservlet")); } @Test public void shouldCanonicalizeNormalURLWithParametersIn_IGNORE_VALUE_mode() throws URIException { URI uri = new URI("http", null,"host", 9001, "/myservlet","p1=2&p2=myparam"); String visitedURI = URLCanonicalizer.buildCleanedParametersURIRepresentation(uri, HandleParametersOption.IGNORE_VALUE, false /* handleODataParametersVisited */); assertThat(visitedURI, is("http://host:9001/myservlet?p1&p2")); } // Test the OData behavior @Test public void shouldCanonicalizeODataIDSimpleIn_USE_ALL_mode() throws URIException { HandleParametersOption spiderOpion = HandleParametersOption.USE_ALL; URI uri = new URI("http", null,"host", 9001, "/app.svc/Book(1)"); String visitedURI = URLCanonicalizer.buildCleanedParametersURIRepresentation(uri, spiderOpion, true /* handleODataParametersVisited */); assertThat(visitedURI, is("http://host:9001/app.svc/Book(1)")); uri = new URI("http", null,"host", 9001, "/app.svc/Book(1)/Author"); visitedURI = URLCanonicalizer.buildCleanedParametersURIRepresentation(uri, spiderOpion, true /* handleODataParametersVisited */); assertThat(visitedURI, is("http://host:9001/app.svc/Book(1)/Author")); } @Test public void shouldCanonicalizeODataIDSimpleIn_IGNORE_COMPLETELY_mode() throws URIException { HandleParametersOption spiderOpion = HandleParametersOption.IGNORE_COMPLETELY; URI uri = new URI("http", null,"host", 9001, "/app.svc/Book(1)"); String visitedURI = URLCanonicalizer.buildCleanedParametersURIRepresentation(uri, spiderOpion, true /* handleODataParametersVisited */); assertThat(visitedURI, is("http://host:9001/app.svc/Book()")); uri = new URI("http", null,"host", 9001, "/app.svc/Book(1)/Author"); visitedURI = URLCanonicalizer.buildCleanedParametersURIRepresentation(uri, spiderOpion, true /* handleODataParametersVisited */); assertThat(visitedURI, is("http://host:9001/app.svc/Book()/Author")); } @Test public void shouldCanonicalizeODataIDSimpleIn_IGNORE_VALUE_mode() throws URIException { HandleParametersOption spiderOpion = HandleParametersOption.IGNORE_VALUE; URI uri = new URI("http", null,"host", 9001, "/app.svc/Book(1)"); String visitedURI = URLCanonicalizer.buildCleanedParametersURIRepresentation(uri, spiderOpion, true /* handleODataParametersVisited */); assertThat(visitedURI, is("http://host:9001/app.svc/Book()")); uri = new URI("http", null,"host", 9001, "/app.svc/Book(1)/Author"); visitedURI = URLCanonicalizer.buildCleanedParametersURIRepresentation(uri, spiderOpion, true /* handleODataParametersVisited */); assertThat(visitedURI, is("http://host:9001/app.svc/Book()/Author")); } @Test public void shouldCanonicalizeODataIDMultipleIn_USE_ALL_mode() throws URIException { HandleParametersOption spiderOpion = HandleParametersOption.USE_ALL; URI uri = new URI("http", null,"host", 9001, "/app.svc/Book(title='dummy',year=2012)"); String visitedURI = URLCanonicalizer.buildCleanedParametersURIRepresentation(uri, spiderOpion, true /* handleODataParametersVisited */); assertThat(visitedURI, is("http://host:9001/app.svc/Book(title='dummy',year=2012)")); uri = new URI("http", null,"host", 9001, "/app.svc/Book(title='dummy',year=2012)/Author"); visitedURI = URLCanonicalizer.buildCleanedParametersURIRepresentation(uri, spiderOpion, true /* handleODataParametersVisited */); assertThat(visitedURI, is("http://host:9001/app.svc/Book(title='dummy',year=2012)/Author")); } @Test public void shouldCanonicalizeODataIDMultipleIn_IGNORE_COMPLETELY_mode() throws URIException { HandleParametersOption spiderOpion = HandleParametersOption.IGNORE_COMPLETELY; URI uri = new URI("http", null,"host", 9001, "/app.svc/Book(title='dummy',year=2012)"); String visitedURI = URLCanonicalizer.buildCleanedParametersURIRepresentation(uri, spiderOpion, true /* handleODataParametersVisited */); assertThat(visitedURI, is("http://host:9001/app.svc/Book()")); uri = new URI("http", null,"host", 9001, "/app.svc/Book(title='dummy',year=2012)/Author"); visitedURI = URLCanonicalizer.buildCleanedParametersURIRepresentation(uri, spiderOpion, true /* handleODataParametersVisited */); assertThat(visitedURI, is("http://host:9001/app.svc/Book()/Author")); } @Test public void shouldCanonicalizeODataIDMultipleIn_IGNORE_VALUE_mode() throws URIException { HandleParametersOption spiderOpion = HandleParametersOption.IGNORE_VALUE; URI uri = new URI("http", null,"host", 9001, "/app.svc/Book(title='dummy',year=2012)"); String visitedURI = URLCanonicalizer.buildCleanedParametersURIRepresentation(uri, spiderOpion, true /* handleODataParametersVisited */); assertThat(visitedURI, is("http://host:9001/app.svc/Book(title,year)")); uri = new URI("http", null,"host", 9001, "/app.svc/Book(title='dummy',year=2012)/Author"); visitedURI = URLCanonicalizer.buildCleanedParametersURIRepresentation(uri, spiderOpion, true /* handleODataParametersVisited */); assertThat(visitedURI, is("http://host:9001/app.svc/Book(title,year)/Author")); } }