diff --git a/src/Appwrite/Network/Cors.php b/src/Appwrite/Network/Cors.php index 88d0158379..9fc47c5808 100644 --- a/src/Appwrite/Network/Cors.php +++ b/src/Appwrite/Network/Cors.php @@ -2,6 +2,8 @@ namespace Appwrite\Network; +use Utopia\Validator\Hostname; + /** * Generate CORS response headers for an incoming request. * @@ -76,7 +78,8 @@ final class Cors } // Match only by host - if (!\in_array($host, $this->allowedHosts, true)) { + $validator = new Hostname($this->allowedHosts); + if (!$validator->isValid($host)) { return $headers; } diff --git a/tests/unit/Network/CorsTest.php b/tests/unit/Network/CorsTest.php index 521bf21f1e..986e48ebb5 100644 --- a/tests/unit/Network/CorsTest.php +++ b/tests/unit/Network/CorsTest.php @@ -36,6 +36,21 @@ final class CorsTest extends TestCase $this->assertSame('https://foo.com', $result[Cors::HEADER_ALLOW_ORIGIN]); } + public function testSubdomainWildcardAllowsAnySubdomain(): void + { + $cors = new Cors( + allowedHosts: ['*.example.com'], + allowedMethods: ['GET'], + allowedHeaders: ['X-Test'], + exposedHeaders: [], + allowCredentials: false + ); + + $result = $cors->headers('https://foo.example.com'); + + $this->assertSame('https://foo.example.com', $result[Cors::HEADER_ALLOW_ORIGIN]); + } + public function testEmptyOriginReturnsStaticHeadersOnly(): void { $cors = new Cors(