diff --git a/app/controllers/api/account.php b/app/controllers/api/account.php index fd09aaa3d..0a2615199 100644 --- a/app/controllers/api/account.php +++ b/app/controllers/api/account.php @@ -6,10 +6,10 @@ use Utopia\Exception; use Utopia\Config\Config; use Utopia\Validator\Assoc; use Utopia\Validator\Text; -use Utopia\Validator\Email; +use Appwrite\Network\Validator\Email; use Utopia\Validator\WhiteList; -use Utopia\Validator\Host; -use Utopia\Validator\URL; +use Appwrite\Network\Validator\Host; +use Appwrite\Network\Validator\URL; use Utopia\Audit\Audit; use Utopia\Audit\Adapters\MySQL as AuditAdapter; use Appwrite\Auth\Auth; diff --git a/app/controllers/api/avatars.php b/app/controllers/api/avatars.php index a094dc672..b5c47c198 100644 --- a/app/controllers/api/avatars.php +++ b/app/controllers/api/avatars.php @@ -14,7 +14,7 @@ use Utopia\Validator\Boolean; use Utopia\Validator\HexColor; use Utopia\Validator\Range; use Utopia\Validator\Text; -use Utopia\Validator\URL; +use Appwrite\Network\Validator\URL; use Utopia\Validator\WhiteList; $avatarCallback = function ($type, $code, $width, $height, $quality, $response) { diff --git a/app/controllers/api/projects.php b/app/controllers/api/projects.php index 63c99ffae..573771461 100644 --- a/app/controllers/api/projects.php +++ b/app/controllers/api/projects.php @@ -6,7 +6,7 @@ use Utopia\Validator\ArrayList; use Utopia\Validator\Boolean; use Utopia\Validator\Text; use Utopia\Validator\WhiteList; -use Utopia\Validator\URL; +use Appwrite\Network\Validator\URL; use Utopia\Validator\Range; use Utopia\Config\Config; use Utopia\Domains\Domain; diff --git a/app/controllers/api/teams.php b/app/controllers/api/teams.php index c4a9e4875..847b37484 100644 --- a/app/controllers/api/teams.php +++ b/app/controllers/api/teams.php @@ -3,9 +3,9 @@ use Utopia\App; use Utopia\Exception; use Utopia\Config\Config; -use Utopia\Validator\Email; +use Appwrite\Network\Validator\Email; use Utopia\Validator\Text; -use Utopia\Validator\Host; +use Appwrite\Network\Validator\Host; use Utopia\Validator\Range; use Utopia\Validator\ArrayList; use Utopia\Validator\WhiteList; diff --git a/app/controllers/api/users.php b/app/controllers/api/users.php index 958dfc148..58ec3ee97 100644 --- a/app/controllers/api/users.php +++ b/app/controllers/api/users.php @@ -4,7 +4,7 @@ use Utopia\App; use Utopia\Exception; use Utopia\Validator\Assoc; use Utopia\Validator\WhiteList; -use Utopia\Validator\Email; +use Appwrite\Network\Validator\Email; use Utopia\Validator\Text; use Utopia\Validator\Range; use Utopia\Audit\Audit; diff --git a/src/Appwrite/Network/Validator/Email.php b/src/Appwrite/Network/Validator/Email.php new file mode 100644 index 000000000..ca9ba1d53 --- /dev/null +++ b/src/Appwrite/Network/Validator/Email.php @@ -0,0 +1,56 @@ +whitelist = $whitelist; + } + + /** + * Get Description + * + * Returns validator description + * + * @return string + */ + public function getDescription() + { + return 'URL host must be one of: ' . \implode(', ', $this->whitelist); + } + + /** + * Get Type + * + * Returns validator type. + * + * @return string + */ + public function getType() + { + return 'string'; + } + + /** + * Is valid + * + * Validation will pass when $value starts with one of the given hosts + * + * @param mixed $value + * @return bool + */ + public function isValid($value) + { + $urlValidator = new URL(); + + if (!$urlValidator->isValid($value)) { + return false; + } + + if (\in_array(\parse_url($value, PHP_URL_HOST), $this->whitelist)) { + return true; + } + + return false; + } +} diff --git a/src/Appwrite/Network/Validator/IP.php b/src/Appwrite/Network/Validator/IP.php new file mode 100644 index 000000000..523147eb2 --- /dev/null +++ b/src/Appwrite/Network/Validator/IP.php @@ -0,0 +1,102 @@ +type = $type; + } + + /** + * Get Description + * + * Returns validator description + * + * @return string + */ + public function getDescription() + { + return 'Value must be a valid IP address'; + } + + /** + * Get Type + * + * Returns validator type. + * + * @return string + */ + public function getType() + { + return 'string'; + } + + /** + * Is valid + * + * Validation will pass when $value is valid IP address. + * + * @param mixed $value + * @return bool + */ + public function isValid($value) + { + switch ($this->type) { + case self::ALL: + if (\filter_var($value, FILTER_VALIDATE_IP)) { + return true; + } + break; + + case self::V4: + if (\filter_var($value, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)) { + return true; + } + break; + + case self::V6: + if (\filter_var($value, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) { + return true; + } + break; + + default: + return false; + break; + } + + return false; + } +} diff --git a/src/Appwrite/Network/Validator/URL.php b/src/Appwrite/Network/Validator/URL.php new file mode 100644 index 000000000..bd1546e11 --- /dev/null +++ b/src/Appwrite/Network/Validator/URL.php @@ -0,0 +1,56 @@ + + * @version 1.0 RC4 + * @license The MIT License (MIT) + */ + +namespace Appwrite\Network\Validator; + +use PHPUnit\Framework\TestCase; + +class EmailTest extends TestCase +{ + /** + * @var Email + */ + protected $email = null; + + public function setUp():void + { + $this->email = new Email(); + } + + public function tearDown():void + { + $this->email = null; + } + + public function testIsValid() + { + // Assertions + $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/HostTest.php b/tests/unit/Network/Validators/HostTest.php new file mode 100755 index 000000000..724ad7415 --- /dev/null +++ b/tests/unit/Network/Validators/HostTest.php @@ -0,0 +1,45 @@ + + * @version 1.0 RC4 + * @license The MIT License (MIT) + */ + +namespace Appwrite\Network\Validator; + +use PHPUnit\Framework\TestCase; + +class HostTest extends TestCase +{ + /** + * @var Host + */ + protected $host = null; + + public function setUp():void + { + $this->host = new Host(['appwrite.io', 'subdomain.appwrite.test', 'localhost']); + } + + public function tearDown():void + { + $this->host = null; + } + + public function testIsValid() + { + // Assertions + $this->assertEquals($this->host->isValid('https://appwrite.io/link'), true); + $this->assertEquals($this->host->isValid('https://localhost'), true); + $this->assertEquals($this->host->isValid('localhost'), false); + $this->assertEquals($this->host->isValid('http://subdomain.appwrite.test/path'), true); + $this->assertEquals($this->host->isValid('http://test.subdomain.appwrite.test/path'), false); + $this->assertEquals($this->host->getType(), 'string'); + } +} diff --git a/tests/unit/Network/Validators/IPTest.php b/tests/unit/Network/Validators/IPTest.php new file mode 100755 index 000000000..8ebd12dd0 --- /dev/null +++ b/tests/unit/Network/Validators/IPTest.php @@ -0,0 +1,81 @@ + + * @version 1.0 RC4 + * @license The MIT License (MIT) + */ + +namespace Appwrite\Network\Validator; + +use PHPUnit\Framework\TestCase; + +class IPTest extends TestCase +{ + public function tearDown():void + { + $this->validator = null; + } + + public function testIsValidIP() + { + $validator = new IP(); + + // Assertions + $this->assertEquals($validator->isValid('2001:0db8:85a3:08d3:1319:8a2e:0370:7334'), true); + $this->assertEquals($validator->isValid('109.67.204.101'), true); + $this->assertEquals($validator->isValid(23.5), false); + $this->assertEquals($validator->isValid('23.5'), false); + $this->assertEquals($validator->isValid(null), false); + $this->assertEquals($validator->isValid(true), false); + $this->assertEquals($validator->isValid(false), false); + $this->assertEquals($validator->getType(), 'string'); + } + + public function testIsValidIPALL() + { + $validator = new IP(IP::ALL); + + // Assertions + $this->assertEquals($validator->isValid('2001:0db8:85a3:08d3:1319:8a2e:0370:7334'), true); + $this->assertEquals($validator->isValid('109.67.204.101'), true); + $this->assertEquals($validator->isValid(23.5), false); + $this->assertEquals($validator->isValid('23.5'), false); + $this->assertEquals($validator->isValid(null), false); + $this->assertEquals($validator->isValid(true), false); + $this->assertEquals($validator->isValid(false), false); + } + + public function testIsValidIPV4() + { + $validator = new IP(IP::V4); + + // Assertions + $this->assertEquals($validator->isValid('2001:0db8:85a3:08d3:1319:8a2e:0370:7334'), false); + $this->assertEquals($validator->isValid('109.67.204.101'), true); + $this->assertEquals($validator->isValid(23.5), false); + $this->assertEquals($validator->isValid('23.5'), false); + $this->assertEquals($validator->isValid(null), false); + $this->assertEquals($validator->isValid(true), false); + $this->assertEquals($validator->isValid(false), false); + } + + public function testIsValidIPV6() + { + $validator = new IP(IP::V6); + + // Assertions + $this->assertEquals($validator->isValid('2001:0db8:85a3:08d3:1319:8a2e:0370:7334'), true); + $this->assertEquals($validator->isValid('109.67.204.101'), false); + $this->assertEquals($validator->isValid(23.5), false); + $this->assertEquals($validator->isValid('23.5'), false); + $this->assertEquals($validator->isValid(null), false); + $this->assertEquals($validator->isValid(true), false); + $this->assertEquals($validator->isValid(false), false); + } +} diff --git a/tests/unit/Network/Validators/URLTest.php b/tests/unit/Network/Validators/URLTest.php new file mode 100755 index 000000000..458637da6 --- /dev/null +++ b/tests/unit/Network/Validators/URLTest.php @@ -0,0 +1,48 @@ + + * @version 1.0 RC4 + * @license The MIT License (MIT) + */ + +namespace Appwrite\Network\Validator; + +use PHPUnit\Framework\TestCase; + +class URLTest extends TestCase +{ + /** + * @var Domain + */ + protected $url = null; + + public function setUp():void + { + $this->url = new URL(); + } + + public function tearDown():void + { + $this->url = null; + } + + public function testIsValid() + { + // Assertions + $this->assertEquals(true, $this->url->isValid('http://example.com')); + $this->assertEquals(true, $this->url->isValid('https://example.com')); + $this->assertEquals(true, $this->url->isValid('htts://example.com')); // does not validate protocol + $this->assertEquals(false, $this->url->isValid('example.com')); // though, requires some kind of protocol + $this->assertEquals(false, $this->url->isValid('http:/example.com')); + $this->assertEquals(true, $this->url->isValid('http://exa-mple.com')); + $this->assertEquals(false, $this->url->isValid('htt@s://example.com')); + $this->assertEquals(true, $this->url->isValid('http://www.example.com/foo%2\u00c2\u00a9zbar')); + $this->assertEquals(true, $this->url->isValid('http://www.example.com/?q=%3Casdf%3E')); + } +}