diff --git a/src/Service/PublicPathMatcher.php b/src/Service/PublicPathMatcher.php index df5a608..0951f35 100644 --- a/src/Service/PublicPathMatcher.php +++ b/src/Service/PublicPathMatcher.php @@ -79,7 +79,7 @@ final readonly class PublicPathMatcher implements PublicPathMatcherInterface $host = null; $path = $entry; - if (preg_match('/^([a-z0-9.-]+)(\/.+)$/i', $entry, $m)) { + if (preg_match('/^([a-z0-9.-]+)(\/.*)$/i', $entry, $m)) { $host = strtolower($m[1]); $path = $m[2]; } diff --git a/tests/Unit/Service/PublicPathMatcherTest.php b/tests/Unit/Service/PublicPathMatcherTest.php index 010a789..ae96b71 100644 --- a/tests/Unit/Service/PublicPathMatcherTest.php +++ b/tests/Unit/Service/PublicPathMatcherTest.php @@ -176,6 +176,25 @@ final class PublicPathMatcherTest extends TestCase self::assertFalse($matcher->matches('other.host', '/public/repo')); } + public function testDomainPrefixedRootPathMatchesRoot(): void + { + // host/ — the trailing slash is the entire path, nothing after it + $matcher = new PublicPathMatcher('code.example.com/'); + self::assertTrue($matcher->matches('code.example.com', '/')); + self::assertFalse($matcher->matches('code.example.com', '/public')); + self::assertFalse($matcher->matches('other.example.com', '/')); + } + + public function testDomainPrefixedRootWithOtherPatterns(): void + { + // The exact scenario from the bug report + $matcher = new PublicPathMatcher('code.example.com/,code.example.com/public/**'); + self::assertTrue($matcher->matches('code.example.com', '/')); + self::assertTrue($matcher->matches('code.example.com', '/public/repo')); + self::assertFalse($matcher->matches('code.example.com', '/private')); + self::assertFalse($matcher->matches('other.example.com', '/')); + } + public function testDomainPrefixIsCaseInsensitive(): void { $matcher = new PublicPathMatcher('Code.Example.COM/public/**');