diff --git a/src/Appwrite/GraphQL/Types/Mapper.php b/src/Appwrite/GraphQL/Types/Mapper.php index c9ae84f1c3..5af70a72a4 100644 --- a/src/Appwrite/GraphQL/Types/Mapper.php +++ b/src/Appwrite/GraphQL/Types/Mapper.php @@ -275,7 +275,7 @@ class Mapper case 'Appwrite\Network\Validator\CNAME': case 'Appwrite\Network\Validator\Email': case 'Appwrite\Network\Validator\Redirect': - case 'Appwrite\Network\Validator\DNS': + case 'Utopia\DNS\Validator\DNS': case 'Appwrite\Network\Validator\Origin': case 'Appwrite\Task\Validator\Cron': case 'Appwrite\Utopia\Database\Validator\CustomId': diff --git a/src/Appwrite/Network/Validator/DNS.php b/src/Appwrite/Network/Validator/DNS.php deleted file mode 100644 index e3c1d38015..0000000000 --- a/src/Appwrite/Network/Validator/DNS.php +++ /dev/null @@ -1,116 +0,0 @@ -server = $server ?: System::getEnv('_APP_DNS', '8.8.8.8'); - } - - public function getDescription(): string - { - return 'Invalid DNS record.'; - } - - public function isValid($value): bool - { - if (!is_string($value) || trim($value) === '') { - return false; - } - - $client = new Client($this->server); - try { - $response = $client->query(Message::query( - new Question($value, $this->type) - )); - } catch (\Throwable) { - return false; - } - - $typeMatches = array_filter( - $response->answers, - fn (Record $record) => $record->type === $this->type - ); - - if (empty($typeMatches)) { - if ($this->type === Record::TYPE_CAA) { - return $this->validateParentCAA($value); - } - - return false; - } - - foreach ($typeMatches as $record) { - if ($this->type === Record::TYPE_CAA) { - $valuePart = $this->extractCAAValue($record->rdata); - if ($valuePart !== '' && $valuePart === $this->target) { - return true; - } - } - - if ($record->rdata === $this->target) { - return true; - } - } - - return false; - } - - private function validateParentCAA(string $domain): bool - { - try { - $domainInfo = new Domain($domain); - } catch (\Throwable) { - return false; - } - - if ($domainInfo->get() === $domainInfo->getApex()) { - return true; - } - - $parts = explode('.', $domainInfo->get()); - array_shift($parts); - $parent = implode('.', $parts); - - if ($parent === '') { - return false; - } - - $validator = new self($this->target, Record::TYPE_CAA, $this->server); - return $validator->isValid($parent); - } - - private function extractCAAValue(string $rdata): string - { - $parts = explode(' ', $rdata, 3); - if (count($parts) < 3) { - return ''; - } - - $value = trim($parts[2], '"'); - return explode(';', $value)[0] ?? ''; - } - - public function isArray(): bool - { - return false; - } - - public function getType(): string - { - return self::TYPE_STRING; - } -} 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 ff92b3a408..6aadeb1285 100644 --- a/src/Appwrite/Platform/Modules/Proxy/Http/Rules/API/Create.php +++ b/src/Appwrite/Platform/Modules/Proxy/Http/Rules/API/Create.php @@ -5,7 +5,6 @@ namespace Appwrite\Platform\Modules\Proxy\Http\Rules\API; use Appwrite\Event\Certificate; use Appwrite\Event\Event; use Appwrite\Extend\Exception; -use Appwrite\Network\Validator\DNS; use Appwrite\SDK\AuthType; use Appwrite\SDK\Method; use Appwrite\SDK\Response as SDKResponse; @@ -15,6 +14,7 @@ use Utopia\Database\Document; use Utopia\Database\Exception\Duplicate; use Utopia\Database\Helpers\ID; use Utopia\DNS\Message\Record; +use Utopia\DNS\Validator\DNS; use Utopia\Domains\Domain; use Utopia\Platform\Action; use Utopia\Platform\Scope\HTTP; 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 6e6d9905a8..01c445040d 100644 --- a/src/Appwrite/Platform/Modules/Proxy/Http/Rules/Function/Create.php +++ b/src/Appwrite/Platform/Modules/Proxy/Http/Rules/Function/Create.php @@ -5,7 +5,6 @@ namespace Appwrite\Platform\Modules\Proxy\Http\Rules\Function; use Appwrite\Event\Certificate; use Appwrite\Event\Event; use Appwrite\Extend\Exception; -use Appwrite\Network\Validator\DNS; use Appwrite\SDK\AuthType; use Appwrite\SDK\Method; use Appwrite\SDK\Response as SDKResponse; @@ -16,6 +15,7 @@ use Utopia\Database\Exception\Duplicate; use Utopia\Database\Helpers\ID; use Utopia\Database\Validator\UID; use Utopia\DNS\Message\Record; +use Utopia\DNS\Validator\DNS; use Utopia\Domains\Domain; use Utopia\Platform\Action; use Utopia\Platform\Scope\HTTP; 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 e2cc51d91f..38dd3b19b4 100644 --- a/src/Appwrite/Platform/Modules/Proxy/Http/Rules/Redirect/Create.php +++ b/src/Appwrite/Platform/Modules/Proxy/Http/Rules/Redirect/Create.php @@ -5,7 +5,6 @@ namespace Appwrite\Platform\Modules\Proxy\Http\Rules\Redirect; use Appwrite\Event\Certificate; use Appwrite\Event\Event; use Appwrite\Extend\Exception; -use Appwrite\Network\Validator\DNS; use Appwrite\SDK\AuthType; use Appwrite\SDK\Method; use Appwrite\SDK\Response as SDKResponse; @@ -16,6 +15,7 @@ use Utopia\Database\Exception\Duplicate; use Utopia\Database\Helpers\ID; use Utopia\Database\Validator\UID; use Utopia\DNS\Message\Record; +use Utopia\DNS\Validator\DNS; use Utopia\Domains\Domain; use Utopia\Platform\Action; use Utopia\Platform\Scope\HTTP; 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 5154a82e16..151740c370 100644 --- a/src/Appwrite/Platform/Modules/Proxy/Http/Rules/Site/Create.php +++ b/src/Appwrite/Platform/Modules/Proxy/Http/Rules/Site/Create.php @@ -5,7 +5,6 @@ namespace Appwrite\Platform\Modules\Proxy\Http\Rules\Site; use Appwrite\Event\Certificate; use Appwrite\Event\Event; use Appwrite\Extend\Exception; -use Appwrite\Network\Validator\DNS; use Appwrite\SDK\AuthType; use Appwrite\SDK\Method; use Appwrite\SDK\Response as SDKResponse; @@ -16,6 +15,7 @@ use Utopia\Database\Exception\Duplicate; use Utopia\Database\Helpers\ID; use Utopia\Database\Validator\UID; use Utopia\DNS\Message\Record; +use Utopia\DNS\Validator\DNS; use Utopia\Domains\Domain; use Utopia\Platform\Action; use Utopia\Platform\Scope\HTTP; diff --git a/src/Appwrite/Platform/Modules/Proxy/Http/Rules/Verification/Update.php b/src/Appwrite/Platform/Modules/Proxy/Http/Rules/Verification/Update.php index af61f25f05..4e755a905a 100644 --- a/src/Appwrite/Platform/Modules/Proxy/Http/Rules/Verification/Update.php +++ b/src/Appwrite/Platform/Modules/Proxy/Http/Rules/Verification/Update.php @@ -5,7 +5,6 @@ namespace Appwrite\Platform\Modules\Proxy\Http\Rules\Verification; use Appwrite\Event\Certificate; use Appwrite\Event\Event; use Appwrite\Extend\Exception; -use Appwrite\Network\Validator\DNS; use Appwrite\SDK\AuthType; use Appwrite\SDK\Method; use Appwrite\SDK\Response as SDKResponse; @@ -14,6 +13,7 @@ use Utopia\Database\Database; use Utopia\Database\Document; use Utopia\Database\Validator\UID; use Utopia\DNS\Message\Record; +use Utopia\DNS\Validator\DNS; use Utopia\Domains\Domain; use Utopia\Logger\Log; use Utopia\Platform\Action; diff --git a/src/Appwrite/Platform/Workers/Certificates.php b/src/Appwrite/Platform/Workers/Certificates.php index ac3deb31af..d8a7a180ca 100644 --- a/src/Appwrite/Platform/Workers/Certificates.php +++ b/src/Appwrite/Platform/Workers/Certificates.php @@ -8,7 +8,6 @@ use Appwrite\Event\Func; use Appwrite\Event\Mail; use Appwrite\Event\Realtime; use Appwrite\Event\Webhook; -use Appwrite\Network\Validator\DNS; use Appwrite\Template\Template; use Appwrite\Utopia\Response\Model\Rule; use Exception; @@ -23,6 +22,7 @@ use Utopia\Database\Exception\Structure; use Utopia\Database\Helpers\ID; use Utopia\Database\Query; use Utopia\DNS\Message\Record; +use Utopia\DNS\Validator\DNS; use Utopia\Domains\Domain; use Utopia\Locale\Locale; use Utopia\Logger\Log; diff --git a/tests/unit/Network/Validators/DNSTest.php b/tests/unit/Network/Validators/DNSTest.php deleted file mode 100644 index 9f8928b87f..0000000000 --- a/tests/unit/Network/Validators/DNSTest.php +++ /dev/null @@ -1,98 +0,0 @@ -assertEquals($validator->isValid(''), false); - $this->assertEquals($validator->isValid(null), false); - $this->assertEquals($validator->isValid(false), false); - $this->assertEquals($validator->isValid('cname-unit-test.appwrite.org'), true); - $this->assertEquals($validator->isValid('test1.appwrite.org'), false); - } - - public function testA(): void - { - // IPv4 for documentation purposes - $validator = new DNS('203.0.113.1', Record::TYPE_A); - $this->assertEquals($validator->isValid(''), false); - $this->assertEquals($validator->isValid(null), false); - $this->assertEquals($validator->isValid(false), false); - $this->assertEquals($validator->isValid('a-unit-test.appwrite.org'), true); - $this->assertEquals($validator->isValid('test1.appwrite.org'), false); - } - - public function testAAAA(): void - { - // IPv6 for documentation purposes - $validator = new DNS('2001:db8::1', Record::TYPE_AAAA); - $this->assertEquals($validator->isValid(''), false); - $this->assertEquals($validator->isValid(null), false); - $this->assertEquals($validator->isValid(false), false); - $this->assertEquals($validator->isValid('aaaa-unit-test.appwrite.org'), true); - $this->assertEquals($validator->isValid('test1.appwrite.org'), false); - } - - #[Retry(count: 5)] - public function testCAA(): void - { - $certainly = new DNS('certainly.com', Record::TYPE_CAA, 'ns1.digitalocean.com'); - $letsencrypt = new DNS('letsencrypt.org', Record::TYPE_CAA, 'ns1.digitalocean.com'); - - // No CAA record succeeds on main domain & subdomains for any issuer - $this->assertEquals($certainly->isValid('caa.appwrite.org'), true); - $this->assertEquals($certainly->isValid('sub.caa.appwrite.org'), true); - $this->assertEquals($certainly->isValid('sub.sub.caa.appwrite.org'), true); - - $this->assertEquals($letsencrypt->isValid('caa.appwrite.org'), true); - $this->assertEquals($letsencrypt->isValid('sub.caa.appwrite.org'), true); - $this->assertEquals($letsencrypt->isValid('sub.sub.caa.appwrite.org'), true); - - // Custom flags and tag is allowed, but only for Certainly - $this->assertEquals($certainly->isValid('certainly-full.caa.appwrite.org'), true); - $this->assertEquals($letsencrypt->isValid('certainly-full.caa.appwrite.org'), false); - - // Custom flags&tag are not allowed if validator includes specific flags&tag - $certainlyFull = new DNS('0 issue "certainly.com"', Record::TYPE_CAA); - $this->assertEquals($certainlyFull->isValid('certainly-full.caa.appwrite.org'), false); - - // Custom flags&tag still allows if they match exactly - $certainlyFull = new DNS('128 issuewild "certainly.com;account=123456;validationmethods=dns-01"', Record::TYPE_CAA); - $this->assertEquals($certainlyFull->isValid('certainly-full.caa.appwrite.org'), true); - - // Certainly CAA allows Certainly, but not LetsEncrypt; Same for subdomains - $this->assertEquals($certainly->isValid('certainly.caa.appwrite.org'), true); - $this->assertEquals($letsencrypt->isValid('certainly.caa.appwrite.org'), false); - - $this->assertEquals($certainly->isValid('sub.certainly.caa.appwrite.org'), true); - $this->assertEquals($letsencrypt->isValid('sub.certainly.caa.appwrite.org'), false); - - $this->assertEquals($certainly->isValid('sub.sub.certainly.caa.appwrite.org'), true); - $this->assertEquals($letsencrypt->isValid('sub.sub.certainly.caa.appwrite.org'), false); - - // LetsEncrypt CAA on subdomain with parent allowing Certainly. Only LetsEncrypt is allowed; Same for subdomains - $this->assertEquals($certainly->isValid('letsencrypt.certainly.caa.appwrite.org'), false); - $this->assertEquals($letsencrypt->isValid('letsencrypt.certainly.caa.appwrite.org'), true); - - $this->assertEquals($certainly->isValid('sub.letsencrypt.certainly.caa.appwrite.org'), false); - $this->assertEquals($letsencrypt->isValid('sub.letsencrypt.certainly.caa.appwrite.org'), true); - - $this->assertEquals($certainly->isValid('sub.sub.letsencrypt.certainly.caa.appwrite.org'), false); - $this->assertEquals($letsencrypt->isValid('sub.sub.letsencrypt.certainly.caa.appwrite.org'), true); - } -} diff --git a/tests/unit/Network/Validators/EmailTest.php b/tests/unit/Network/Validators/EmailTest.php deleted file mode 100755 index f629ed6ddc..0000000000 --- a/tests/unit/Network/Validators/EmailTest.php +++ /dev/null @@ -1,69 +0,0 @@ - - * @version 1.0 RC4 - * @license The MIT License (MIT) - */ - -namespace Tests\Unit\Network\Validators; - -use Appwrite\Network\Validator\Email; -use PHPUnit\Framework\TestCase; - -class EmailTest extends TestCase -{ - protected ?Email $email = null; - - public function setUp(): void - { - $this->email = new Email(); - } - - public function tearDown(): void - { - $this->email = null; - } - - public function testIsValid(): void - { - $this->assertEquals(true, $this->email->isValid('email@domain.com')); - $this->assertEquals(true, $this->email->isValid('firstname.lastname@domain.com')); - $this->assertEquals(true, $this->email->isValid('email@subdomain.domain.com')); - $this->assertEquals(true, $this->email->isValid('firstname+lastname@domain.com')); - $this->assertEquals(true, $this->email->isValid('email@[123.123.123.123]')); - $this->assertEquals(true, $this->email->isValid('"email"@domain.com')); - $this->assertEquals(true, $this->email->isValid('1234567890@domain.com')); - $this->assertEquals(true, $this->email->isValid('email@domain-one.com')); - $this->assertEquals(true, $this->email->isValid('_______@domain.com')); - $this->assertEquals(true, $this->email->isValid('email@domain.name')); - $this->assertEquals(true, $this->email->isValid('email@domain.co.jp')); - $this->assertEquals(true, $this->email->isValid('firstname-lastname@domain.com')); - $this->assertEquals(false, $this->email->isValid(false)); - $this->assertEquals(false, $this->email->isValid(['string', 'string'])); - $this->assertEquals(false, $this->email->isValid(1)); - $this->assertEquals(false, $this->email->isValid(1.2)); - $this->assertEquals(false, $this->email->isValid('plainaddress')); // Missing @ sign and domain - $this->assertEquals(false, $this->email->isValid('@domain.com')); // Missing username - $this->assertEquals(false, $this->email->isValid('#@%^%#$@#$@#.com')); // Garbage - $this->assertEquals(false, $this->email->isValid('Joe Smith ')); // Encoded html within email is invalid - $this->assertEquals(false, $this->email->isValid('email.domain.com')); // Missing @ - $this->assertEquals(false, $this->email->isValid('email@domain@domain.com')); // Two @ sign - $this->assertEquals(false, $this->email->isValid('.email@domain.com')); // Leading dot in address is not allowed - $this->assertEquals(false, $this->email->isValid('email.@domain.com')); // Trailing dot in address is not allowed - $this->assertEquals(false, $this->email->isValid('email..email@domain.com')); // Multiple dots - $this->assertEquals(false, $this->email->isValid('あいうえお@domain.com')); // Unicode char as address - $this->assertEquals(false, $this->email->isValid('email@domain.com (Joe Smith)')); // Text followed email is not allowed - $this->assertEquals(false, $this->email->isValid('email@domain')); // Missing top level domain (.com/.net/.org/etc) - $this->assertEquals(false, $this->email->isValid('email@-domain.com')); // Leading dash in front of domain is invalid - $this->assertEquals(false, $this->email->isValid('email@111.222.333.44444')); // Invalid IP format - $this->assertEquals(false, $this->email->isValid('email@domain..com')); // Multiple dot in the domain portion is invalid - $this->assertEquals($this->email->getType(), 'string'); - } -} diff --git a/tests/unit/Network/Validators/OriginTest.php b/tests/unit/Network/Validators/OriginTest.php deleted file mode 100644 index d312f8c5a5..0000000000 --- a/tests/unit/Network/Validators/OriginTest.php +++ /dev/null @@ -1,113 +0,0 @@ - ID::custom('platforms'), - 'name' => 'Production', - 'type' => Platform::TYPE_WEB, - 'hostname' => 'appwrite.io', - ], - [ - '$collection' => ID::custom('platforms'), - 'name' => 'Development', - 'type' => Platform::TYPE_WEB, - 'hostname' => 'appwrite.test', - ], - [ - '$collection' => ID::custom('platforms'), - 'name' => 'Localhost', - 'type' => Platform::TYPE_WEB, - 'hostname' => 'localhost', - ], - [ - '$collection' => ID::custom('platforms'), - 'name' => 'Flutter', - 'type' => Platform::TYPE_FLUTTER_WEB, - 'hostname' => 'appwrite.flutter', - ], - [ - '$collection' => ID::custom('platforms'), - 'name' => 'Expo', - 'type' => Platform::TYPE_SCHEME, - 'key' => 'exp', - ], - [ - '$collection' => ID::custom('platforms'), - 'name' => 'Appwrite Callback', - 'type' => Platform::TYPE_SCHEME, - 'key' => 'appwrite-callback-123', - ], - ]); - - $this->assertEquals(false, $validator->isValid('')); - $this->assertEquals(false, $validator->isValid('/')); - - $this->assertEquals(true, $validator->isValid('https://localhost')); - $this->assertEquals(true, $validator->isValid('http://localhost')); - $this->assertEquals(true, $validator->isValid('http://localhost:80')); - - $this->assertEquals(true, $validator->isValid('https://appwrite.io')); - $this->assertEquals(true, $validator->isValid('http://appwrite.io')); - $this->assertEquals(true, $validator->isValid('http://appwrite.io:80')); - - $this->assertEquals(true, $validator->isValid('https://appwrite.test')); - $this->assertEquals(true, $validator->isValid('http://appwrite.test')); - $this->assertEquals(true, $validator->isValid('http://appwrite.test:80')); - - $this->assertEquals(true, $validator->isValid('https://appwrite.flutter')); - $this->assertEquals(true, $validator->isValid('http://appwrite.flutter')); - $this->assertEquals(true, $validator->isValid('http://appwrite.flutter:80')); - - $this->assertEquals(false, $validator->isValid('https://example.com')); - $this->assertEquals(false, $validator->isValid('http://example.com')); - $this->assertEquals(false, $validator->isValid('http://example.com:80')); - - $this->assertEquals(true, $validator->isValid('exp://')); - $this->assertEquals(true, $validator->isValid('exp:///')); - $this->assertEquals(true, $validator->isValid('exp://index')); - - $this->assertEquals(true, $validator->isValid('appwrite-callback-123://')); - $this->assertEquals(false, $validator->isValid('appwrite-callback-456://')); - - $this->assertEquals(false, $validator->isValid('appwrite-ios://com.company.appname')); - $this->assertEquals('Invalid Origin. Register your new client (com.company.appname) as a new iOS platform on your project console dashboard', $validator->getDescription()); - - $this->assertEquals(false, $validator->isValid('appwrite-android://com.company.appname')); - $this->assertEquals('Invalid Origin. Register your new client (com.company.appname) as a new Android platform on your project console dashboard', $validator->getDescription()); - - $this->assertEquals(false, $validator->isValid('appwrite-macos://com.company.appname')); - $this->assertEquals('Invalid Origin. Register your new client (com.company.appname) as a new macOS platform on your project console dashboard', $validator->getDescription()); - - $this->assertEquals(false, $validator->isValid('appwrite-linux://com.company.appname')); - $this->assertEquals('Invalid Origin. Register your new client (com.company.appname) as a new Linux platform on your project console dashboard', $validator->getDescription()); - - $this->assertEquals(false, $validator->isValid('appwrite-windows://com.company.appname')); - $this->assertEquals('Invalid Origin. Register your new client (com.company.appname) as a new Windows platform on your project console dashboard', $validator->getDescription()); - - $this->assertEquals(false, $validator->isValid('chrome-extension://com.company.appname')); - $this->assertEquals('Invalid Origin. Register your new client (com.company.appname) as a new Web (Chrome Extension) platform on your project console dashboard', $validator->getDescription()); - - $this->assertEquals(false, $validator->isValid('moz-extension://com.company.appname')); - $this->assertEquals('Invalid Origin. Register your new client (com.company.appname) as a new Web (Firefox Extension) platform on your project console dashboard', $validator->getDescription()); - - $this->assertEquals(false, $validator->isValid('safari-web-extension://com.company.appname')); - $this->assertEquals('Invalid Origin. Register your new client (com.company.appname) as a new Web (Safari Extension) platform on your project console dashboard', $validator->getDescription()); - - $this->assertEquals(false, $validator->isValid('ms-browser-extension://com.company.appname')); - $this->assertEquals('Invalid Origin. Register your new client (com.company.appname) as a new Web (Edge Extension) platform on your project console dashboard', $validator->getDescription()); - - $this->assertEquals(false, $validator->isValid('random-scheme://localhost')); - $this->assertEquals('Invalid Scheme. The scheme used (random-scheme) in the Origin (random-scheme://localhost) is not supported. If you are using a custom scheme, please change it to `appwrite-callback-`', $validator->getDescription()); - } -}