From fef2d501593db7ce541b839ebce365e1cd6e96aa Mon Sep 17 00:00:00 2001 From: Harsh Mahajan <127186841+HarshMN2345@users.noreply.github.com> Date: Fri, 25 Jul 2025 17:50:45 +0530 Subject: [PATCH] fix: create a validator isntead of redundant code --- docker-compose.yml | 2 +- .../Validator/AppwriteNetworkDomain.php | 44 ++++++++ .../Modules/Console/Http/Resources/Get.php | 10 +- .../Modules/Proxy/Http/Rules/API/Create.php | 10 +- .../Proxy/Http/Rules/Function/Create.php | 10 +- .../Proxy/Http/Rules/Redirect/Create.php | 10 +- .../Modules/Proxy/Http/Rules/Site/Create.php | 10 +- .../Services/Proxy/ProxyCustomServerTest.php | 27 ----- .../Validators/AppwriteNetworkDomainTest.php | 102 ++++++++++++++++++ 9 files changed, 172 insertions(+), 53 deletions(-) create mode 100644 src/Appwrite/Network/Validator/AppwriteNetworkDomain.php create mode 100644 tests/unit/Network/Validators/AppwriteNetworkDomainTest.php diff --git a/docker-compose.yml b/docker-compose.yml index 58b78fcd8e..afc32a518b 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1033,7 +1033,7 @@ services: volumes: - appwrite-mariadb:/var/lib/mysql:rw ports: - - "3306:3306" + - "3307:3306" environment: - MYSQL_ROOT_PASSWORD=${_APP_DB_ROOT_PASS} - MYSQL_DATABASE=${_APP_DB_SCHEMA} diff --git a/src/Appwrite/Network/Validator/AppwriteNetworkDomain.php b/src/Appwrite/Network/Validator/AppwriteNetworkDomain.php new file mode 100644 index 0000000000..ae7af41554 --- /dev/null +++ b/src/Appwrite/Network/Validator/AppwriteNetworkDomain.php @@ -0,0 +1,44 @@ +isValid($value)) { + throw new Exception(Exception::GENERAL_ARGUMENT_INVALID, $appwriteNetworkValidator->getDescription()); } if (!$validator->isValid($value)) { diff --git a/src/Appwrite/Platform/Modules/Proxy/Http/Rules/API/Create.php b/src/Appwrite/Platform/Modules/Proxy/Http/Rules/API/Create.php index 9d7aca5b43..48c03b76a4 100644 --- a/src/Appwrite/Platform/Modules/Proxy/Http/Rules/API/Create.php +++ b/src/Appwrite/Platform/Modules/Proxy/Http/Rules/API/Create.php @@ -5,6 +5,7 @@ namespace Appwrite\Platform\Modules\Proxy\Http\Rules\API; use Appwrite\Event\Certificate; use Appwrite\Event\Event; use Appwrite\Extend\Exception; +use Appwrite\Network\Validator\AppwriteNetworkDomain; use Appwrite\Network\Validator\DNS; use Appwrite\SDK\AuthType; use Appwrite\SDK\Method; @@ -106,11 +107,10 @@ class Create extends Action throw new Exception(Exception::GENERAL_ARGUMENT_INVALID, 'This domain name is not allowed. Please use a different domain.'); } - if (\str_ends_with(\strtolower($domain), '.appwrite.network')) { - $subdomain = \str_replace('.appwrite.network', '', \strtolower($domain)); - if (empty($subdomain) || \str_contains($subdomain, '.')) { - throw new Exception(Exception::GENERAL_ARGUMENT_INVALID, 'Sub-subdomains are not allowed for appwrite.network. Only one level of subdomain is permitted.'); - } + + $appwriteNetworkValidator = new AppwriteNetworkDomain(); + if (!$appwriteNetworkValidator->isValid($domain)) { + throw new Exception(Exception::GENERAL_ARGUMENT_INVALID, $appwriteNetworkValidator->getDescription()); } try { diff --git a/src/Appwrite/Platform/Modules/Proxy/Http/Rules/Function/Create.php b/src/Appwrite/Platform/Modules/Proxy/Http/Rules/Function/Create.php index 4872e22386..5d437ebdc3 100644 --- a/src/Appwrite/Platform/Modules/Proxy/Http/Rules/Function/Create.php +++ b/src/Appwrite/Platform/Modules/Proxy/Http/Rules/Function/Create.php @@ -5,6 +5,7 @@ namespace Appwrite\Platform\Modules\Proxy\Http\Rules\Function; use Appwrite\Event\Certificate; use Appwrite\Event\Event; use Appwrite\Extend\Exception; +use Appwrite\Network\Validator\AppwriteNetworkDomain; use Appwrite\Network\Validator\DNS; use Appwrite\SDK\AuthType; use Appwrite\SDK\Method; @@ -111,11 +112,10 @@ class Create extends Action throw new Exception(Exception::GENERAL_ARGUMENT_INVALID, 'This domain name is not allowed. Please use a different domain.'); } - if (\str_ends_with(\strtolower($domain), '.appwrite.network')) { - $subdomain = \str_replace('.appwrite.network', '', strtolower($domain)); - if (\str_contains($subdomain, '.')) { - throw new Exception(Exception::GENERAL_ARGUMENT_INVALID, 'Sub-subdomains are not allowed for appwrite.network. Only one level of subdomain is permitted.'); - } + + $appwriteNetworkValidator = new AppwriteNetworkDomain(); + if (!$appwriteNetworkValidator->isValid($domain)) { + throw new Exception(Exception::GENERAL_ARGUMENT_INVALID, $appwriteNetworkValidator->getDescription()); } try { diff --git a/src/Appwrite/Platform/Modules/Proxy/Http/Rules/Redirect/Create.php b/src/Appwrite/Platform/Modules/Proxy/Http/Rules/Redirect/Create.php index fd6588a589..6c27bd94f9 100644 --- a/src/Appwrite/Platform/Modules/Proxy/Http/Rules/Redirect/Create.php +++ b/src/Appwrite/Platform/Modules/Proxy/Http/Rules/Redirect/Create.php @@ -5,6 +5,7 @@ namespace Appwrite\Platform\Modules\Proxy\Http\Rules\Redirect; use Appwrite\Event\Certificate; use Appwrite\Event\Event; use Appwrite\Extend\Exception; +use Appwrite\Network\Validator\AppwriteNetworkDomain; use Appwrite\Network\Validator\DNS; use Appwrite\SDK\AuthType; use Appwrite\SDK\Method; @@ -114,11 +115,10 @@ class Create extends Action throw new Exception(Exception::GENERAL_ARGUMENT_INVALID, 'This domain name is not allowed. Please use a different domain.'); } - if (\str_ends_with(\strtolower($domain), '.appwrite.network')) { - $subdomain = \str_replace('.appwrite.network', '', strtolower($domain)); - if (\str_contains($subdomain, '.')) { - throw new Exception(Exception::GENERAL_ARGUMENT_INVALID, 'Sub-subdomains are not allowed for appwrite.network. Only one level of subdomain is permitted.'); - } + + $appwriteNetworkValidator = new AppwriteNetworkDomain(); + if (!$appwriteNetworkValidator->isValid($domain)) { + throw new Exception(Exception::GENERAL_ARGUMENT_INVALID, $appwriteNetworkValidator->getDescription()); } try { diff --git a/src/Appwrite/Platform/Modules/Proxy/Http/Rules/Site/Create.php b/src/Appwrite/Platform/Modules/Proxy/Http/Rules/Site/Create.php index 7460951969..8eb363495b 100644 --- a/src/Appwrite/Platform/Modules/Proxy/Http/Rules/Site/Create.php +++ b/src/Appwrite/Platform/Modules/Proxy/Http/Rules/Site/Create.php @@ -5,6 +5,7 @@ namespace Appwrite\Platform\Modules\Proxy\Http\Rules\Site; use Appwrite\Event\Certificate; use Appwrite\Event\Event; use Appwrite\Extend\Exception; +use Appwrite\Network\Validator\AppwriteNetworkDomain; use Appwrite\Network\Validator\DNS; use Appwrite\SDK\AuthType; use Appwrite\SDK\Method; @@ -111,11 +112,10 @@ class Create extends Action throw new Exception(Exception::GENERAL_ARGUMENT_INVALID, 'This domain name is not allowed. Please use a different domain.'); } - if (\str_ends_with(\strtolower($domain), '.appwrite.network')) { - $subdomain = \str_replace('.appwrite.network', '', strtolower($domain)); - if (\str_contains($subdomain, '.')) { - throw new Exception(Exception::GENERAL_ARGUMENT_INVALID, 'Sub-subdomains are not allowed for appwrite.network. Only one level of subdomain is permitted.'); - } + + $appwriteNetworkValidator = new AppwriteNetworkDomain(); + if (!$appwriteNetworkValidator->isValid($domain)) { + throw new Exception(Exception::GENERAL_ARGUMENT_INVALID, $appwriteNetworkValidator->getDescription()); } try { diff --git a/tests/e2e/Services/Proxy/ProxyCustomServerTest.php b/tests/e2e/Services/Proxy/ProxyCustomServerTest.php index ccdcffe477..ea310d5449 100644 --- a/tests/e2e/Services/Proxy/ProxyCustomServerTest.php +++ b/tests/e2e/Services/Proxy/ProxyCustomServerTest.php @@ -81,33 +81,6 @@ class ProxyCustomServerTest extends Scope $this->cleanupRule($rule['body']['$id']); } - public function testCreateAPIRuleAppwriteNetworkValidation(): void - { - // Test vallid single subdomain for appwrite.network - $validDomain = \uniqid() . '.appwrite.network'; - $rule = $this->createAPIRule($validDomain); - $this->assertEquals(201, $rule['headers']['status-code']); - $this->cleanupRule($rule['body']['$id']); - - // Test invalid sub-subdomain for appwrite.network - $invalidDomain = 'api.staging.appwrite.network'; - $rule = $this->createAPIRule($invalidDomain); - $this->assertEquals(400, $rule['headers']['status-code']); - $this->assertStringContainsString('Sub-subdomains are not allowed for appwrite.network', $rule['body']['message']); - - // Test another invalid sub-subdomain with multiple dots - $invalidDomain2 = 'asadsdsa.sddsa.appwrite.network'; - $rule = $this->createAPIRule($invalidDomain2); - $this->assertEquals(400, $rule['headers']['status-code']); - $this->assertStringContainsString('Sub-subdomains are not allowed for appwrite.network', $rule['body']['message']); - - // Test valid domain that doesn't end with appwrite.network (should pass) - $otherDomain = \uniqid() . '.example.com'; - $rule = $this->createAPIRule($otherDomain); - $this->assertEquals(201, $rule['headers']['status-code']); - $this->cleanupRule($rule['body']['$id']); - } - public function testCreateAPIRule(): void { $domain = \uniqid() . '-api.custom.localhost'; diff --git a/tests/unit/Network/Validators/AppwriteNetworkDomainTest.php b/tests/unit/Network/Validators/AppwriteNetworkDomainTest.php new file mode 100644 index 0000000000..55e3ddb35f --- /dev/null +++ b/tests/unit/Network/Validators/AppwriteNetworkDomainTest.php @@ -0,0 +1,102 @@ +validator = new AppwriteNetworkDomain(); + } + + public function tearDown(): void + { + $this->validator = null; + } + + public function testValidSingleSubdomains(): void + { + // Valid single-level subdomains for appwrite.network + $this->assertEquals(true, $this->validator->isValid('api.appwrite.network')); + $this->assertEquals(true, $this->validator->isValid('app.appwrite.network')); + $this->assertEquals(true, $this->validator->isValid('test.appwrite.network')); + $this->assertEquals(true, $this->validator->isValid('myapp.appwrite.network')); + $this->assertEquals(true, $this->validator->isValid('123.appwrite.network')); + $this->assertEquals(true, $this->validator->isValid('my-app.appwrite.network')); + $this->assertEquals(true, $this->validator->isValid('a.appwrite.network')); + + // Test case insensitivity + $this->assertEquals(true, $this->validator->isValid('API.APPWRITE.NETWORK')); + $this->assertEquals(true, $this->validator->isValid('Api.Appwrite.Network')); + } + + public function testInvalidSubSubdomains(): void + { + // Invalid sub-subdomains for appwrite.network + $this->assertEquals(false, $this->validator->isValid('api.staging.appwrite.network')); + $this->assertEquals(false, $this->validator->isValid('app.dev.appwrite.network')); + $this->assertEquals(false, $this->validator->isValid('test.beta.appwrite.network')); + $this->assertEquals(false, $this->validator->isValid('foo.bar.appwrite.network')); + $this->assertEquals(false, $this->validator->isValid('a.b.appwrite.network')); + $this->assertEquals(false, $this->validator->isValid('very.long.subdomain.appwrite.network')); + $this->assertEquals(false, $this->validator->isValid('multi.level.deep.appwrite.network')); + + // Test case insensitivity + $this->assertEquals(false, $this->validator->isValid('API.STAGING.APPWRITE.NETWORK')); + $this->assertEquals(false, $this->validator->isValid('Api.Dev.Appwrite.Network')); + } + + public function testNonAppwriteNetworkDomains(): void + { + // Non-appwrite.network domains should pass validation (not our concern) + $this->assertEquals(true, $this->validator->isValid('example.com')); + $this->assertEquals(true, $this->validator->isValid('api.example.com')); + $this->assertEquals(true, $this->validator->isValid('deep.nested.example.com')); + $this->assertEquals(true, $this->validator->isValid('google.com')); + $this->assertEquals(true, $this->validator->isValid('sub.domain.test.io')); + $this->assertEquals(true, $this->validator->isValid('localhost')); + $this->assertEquals(true, $this->validator->isValid('127.0.0.1')); + + // Similar but different domains + $this->assertEquals(true, $this->validator->isValid('appwrite.com')); + $this->assertEquals(true, $this->validator->isValid('api.appwrite.com')); + $this->assertEquals(true, $this->validator->isValid('test.appwrite.org')); + $this->assertEquals(true, $this->validator->isValid('notappwrite.network')); + } + + public function testDoubleSubdomainCustomDomain(): void + { + // Double subdomain for a custom domain should be allowed + $this->assertEquals(true, $this->validator->isValid('stage.dashboard.example.com')); + $this->assertEquals(true, $this->validator->isValid('foo.bar.baz.example.com')); + } + + public function testEdgeCases(): void + { + // Empty and invalid values should pass (let other validators handle them) + $this->assertEquals(true, $this->validator->isValid('')); + $this->assertEquals(true, $this->validator->isValid(null)); + $this->assertEquals(true, $this->validator->isValid(false)); + $this->assertEquals(true, $this->validator->isValid(123)); + $this->assertEquals(true, $this->validator->isValid([])); + + // Just the root domain (unlikely but should be valid) + $this->assertEquals(true, $this->validator->isValid('appwrite.network')); + + // Domain with trailing/leading dots + $this->assertEquals(false, $this->validator->isValid('api.test.appwrite.network.')); + $this->assertEquals(false, $this->validator->isValid('.api.test.appwrite.network')); + } + + public function testValidatorProperties(): void + { + $this->assertEquals('Sub-subdomains are not allowed for appwrite.network. Only one level of subdomain is permitted.', $this->validator->getDescription()); + $this->assertEquals(false, $this->validator->isArray()); + $this->assertEquals('string', $this->validator->getType()); + } +} \ No newline at end of file