diff --git a/.github/badges/jacoco.svg b/.github/badges/jacoco.svg index de06873..65ea9eb 100644 --- a/.github/badges/jacoco.svg +++ b/.github/badges/jacoco.svg @@ -1 +1 @@ -coverage81.1% \ No newline at end of file +coverage81.2% \ No newline at end of file diff --git a/src/main/java/io/github/pixee/security/HostValidator.java b/src/main/java/io/github/pixee/security/HostValidator.java index dc5e764..80eb132 100644 --- a/src/main/java/io/github/pixee/security/HostValidator.java +++ b/src/main/java/io/github/pixee/security/HostValidator.java @@ -47,4 +47,16 @@ public boolean isAllowed(final String host) { static HostValidator fromAllowedHostPattern(final Pattern allowPattern) { return new PatternBasedHostValidator(allowPattern); } + + /** + * Return a {@link HostValidator} that will assure a given domain is within the allowed domain. For example, given + * a domain of "good.com", this validator will allow "good.com", "www.good.com", "internal.good.com", etc. + * + * @param domainName the domain to allow, e.g., "good.com", or "internal-host" + * @return a validator that will only allow hosts within the given domain space + */ + static HostValidator fromAllowedHostDomain(final String domainName) { + Pattern p = Pattern.compile("(.*\\." + Pattern.quote(domainName) + "|" + Pattern.quote(domainName) +")"); + return new PatternBasedHostValidator(p); + } } diff --git a/src/test/java/io/github/pixee/security/UrlsTest.java b/src/test/java/io/github/pixee/security/UrlsTest.java index 39a1fa0..c96972e 100644 --- a/src/test/java/io/github/pixee/security/UrlsTest.java +++ b/src/test/java/io/github/pixee/security/UrlsTest.java @@ -1,18 +1,19 @@ package io.github.pixee.security; -import static io.github.pixee.security.J8ApiBridge.setOf; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.*; -import static org.junit.jupiter.api.Assertions.assertThrows; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; import java.net.MalformedURLException; import java.net.URL; import java.util.regex.Pattern; import java.util.stream.Stream; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.Arguments; -import org.junit.jupiter.params.provider.MethodSource; + +import static io.github.pixee.security.J8ApiBridge.setOf; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.*; +import static org.junit.jupiter.api.Assertions.assertThrows; final class UrlsTest { @@ -138,6 +139,21 @@ void it_disallows_bad_domains() throws MalformedURLException { () -> { Urls.create("https://evil.com/", setOf(UrlProtocol.HTTPS), allowsOnlyGoodDotCom); }); + + HostValidator allowsOnlyGoodDotComByDomainString = HostValidator.fromAllowedHostDomain("good.com"); + Urls.create("https://good.com/", setOf(UrlProtocol.HTTPS), allowsOnlyGoodDotComByDomainString); + Urls.create("https://sub.good.com/", setOf(UrlProtocol.HTTPS), allowsOnlyGoodDotComByDomainString); + Urls.create("https://different-sub-123.good.com/", setOf(UrlProtocol.HTTPS), allowsOnlyGoodDotComByDomainString); + Urls.create("https://.good.com/", setOf(UrlProtocol.HTTPS), allowsOnlyGoodDotComByDomainString); + + Stream.of("https://goodAcom/", "https://evil.com", "https://good.com.evil", "https://good.com.").forEach(badDomain -> { + assertThrows( + SecurityException.class, + () -> { + Urls.create(badDomain, setOf(UrlProtocol.HTTPS), allowsOnlyGoodDotComByDomainString); + }); + }); + } @Test