1<?php 2 3/* 4 * This file is part of the Symfony package. 5 * 6 * (c) Fabien Potencier <fabien@symfony.com> 7 * 8 * For the full copyright and license information, please view the LICENSE 9 * file that was distributed with this source code. 10 */ 11 12namespace Symfony\Component\Validator\Tests\Constraints; 13 14use Symfony\Bridge\PhpUnit\DnsMock; 15use Symfony\Component\Validator\Constraints\Url; 16use Symfony\Component\Validator\Constraints\UrlValidator; 17use Symfony\Component\Validator\Test\ConstraintValidatorTestCase; 18 19/** 20 * @group dns-sensitive 21 */ 22class UrlValidatorTest extends ConstraintValidatorTestCase 23{ 24 protected function createValidator() 25 { 26 return new UrlValidator(); 27 } 28 29 public function testNullIsValid() 30 { 31 $this->validator->validate(null, new Url()); 32 33 $this->assertNoViolation(); 34 } 35 36 public function testEmptyStringIsValid() 37 { 38 $this->validator->validate('', new Url()); 39 40 $this->assertNoViolation(); 41 } 42 43 public function testEmptyStringFromObjectIsValid() 44 { 45 $this->validator->validate(new EmailProvider(), new Url()); 46 47 $this->assertNoViolation(); 48 } 49 50 public function testExpectsStringCompatibleType() 51 { 52 $this->expectException('Symfony\Component\Validator\Exception\UnexpectedTypeException'); 53 $this->validator->validate(new \stdClass(), new Url()); 54 } 55 56 /** 57 * @dataProvider getValidUrls 58 */ 59 public function testValidUrls($url) 60 { 61 $this->validator->validate($url, new Url()); 62 63 $this->assertNoViolation(); 64 } 65 66 public function getValidUrls() 67 { 68 return [ 69 ['http://a.pl'], 70 ['http://www.example.com'], 71 ['http://www.example.com.'], 72 ['http://www.example.museum'], 73 ['https://example.com/'], 74 ['https://example.com:80/'], 75 ['http://examp_le.com'], 76 ['http://www.sub_domain.examp_le.com'], 77 ['http://www.example.coop/'], 78 ['http://www.test-example.com/'], 79 ['http://www.symfony.com/'], 80 ['http://symfony.fake/blog/'], 81 ['http://symfony.com/?'], 82 ['http://symfony.com/search?type=&q=url+validator'], 83 ['http://symfony.com/#'], 84 ['http://symfony.com/#?'], 85 ['http://www.symfony.com/doc/current/book/validation.html#supported-constraints'], 86 ['http://very.long.domain.name.com/'], 87 ['http://localhost/'], 88 ['http://myhost123/'], 89 ['http://127.0.0.1/'], 90 ['http://127.0.0.1:80/'], 91 ['http://[::1]/'], 92 ['http://[::1]:80/'], 93 ['http://[1:2:3::4:5:6:7]/'], 94 ['http://sãopaulo.com/'], 95 ['http://xn--sopaulo-xwa.com/'], 96 ['http://sãopaulo.com.br/'], 97 ['http://xn--sopaulo-xwa.com.br/'], 98 ['http://пример.испытание/'], 99 ['http://xn--e1afmkfd.xn--80akhbyknj4f/'], 100 ['http://مثال.إختبار/'], 101 ['http://xn--mgbh0fb.xn--kgbechtv/'], 102 ['http://例子.测试/'], 103 ['http://xn--fsqu00a.xn--0zwm56d/'], 104 ['http://例子.測試/'], 105 ['http://xn--fsqu00a.xn--g6w251d/'], 106 ['http://例え.テスト/'], 107 ['http://xn--r8jz45g.xn--zckzah/'], 108 ['http://مثال.آزمایشی/'], 109 ['http://xn--mgbh0fb.xn--hgbk6aj7f53bba/'], 110 ['http://실례.테스트/'], 111 ['http://xn--9n2bp8q.xn--9t4b11yi5a/'], 112 ['http://العربية.idn.icann.org/'], 113 ['http://xn--ogb.idn.icann.org/'], 114 ['http://xn--e1afmkfd.xn--80akhbyknj4f.xn--e1afmkfd/'], 115 ['http://xn--espaa-rta.xn--ca-ol-fsay5a/'], 116 ['http://xn--d1abbgf6aiiy.xn--p1ai/'], 117 ['http://☎.com/'], 118 ['http://username:password@symfony.com'], 119 ['http://user.name:password@symfony.com'], 120 ['http://user_name:pass_word@symfony.com'], 121 ['http://username:pass.word@symfony.com'], 122 ['http://user.name:pass.word@symfony.com'], 123 ['http://user-name@symfony.com'], 124 ['http://user_name@symfony.com'], 125 ['http://u%24er:password@symfony.com'], 126 ['http://user:pa%24%24word@symfony.com'], 127 ['http://symfony.com?'], 128 ['http://symfony.com?query=1'], 129 ['http://symfony.com/?query=1'], 130 ['http://symfony.com#'], 131 ['http://symfony.com#fragment'], 132 ['http://symfony.com/#fragment'], 133 ['http://symfony.com/#one_more%20test'], 134 ]; 135 } 136 137 /** 138 * @dataProvider getInvalidUrls 139 */ 140 public function testInvalidUrls($url) 141 { 142 $constraint = new Url([ 143 'message' => 'myMessage', 144 ]); 145 146 $this->validator->validate($url, $constraint); 147 148 $this->buildViolation('myMessage') 149 ->setParameter('{{ value }}', '"'.$url.'"') 150 ->setCode(Url::INVALID_URL_ERROR) 151 ->assertRaised(); 152 } 153 154 public function getInvalidUrls() 155 { 156 return [ 157 ['example.com'], 158 ['://example.com'], 159 ['http ://example.com'], 160 ['http:/example.com'], 161 ['http://example.com::aa'], 162 ['http://example.com:aa'], 163 ['ftp://example.fr'], 164 ['faked://example.fr'], 165 ['http://127.0.0.1:aa/'], 166 ['ftp://[::1]/'], 167 ['http://[::1'], 168 ['http://hello.☎/'], 169 ['http://:password@symfony.com'], 170 ['http://:password@@symfony.com'], 171 ['http://username:passwordsymfony.com'], 172 ['http://usern@me:password@symfony.com'], 173 ['http://nota%hex:password@symfony.com'], 174 ['http://username:nota%hex@symfony.com'], 175 ['http://example.com/exploit.html?<script>alert(1);</script>'], 176 ['http://example.com/exploit.html?hel lo'], 177 ['http://example.com/exploit.html?not_a%hex'], 178 ['http://'], 179 ]; 180 } 181 182 /** 183 * @dataProvider getValidCustomUrls 184 */ 185 public function testCustomProtocolIsValid($url) 186 { 187 $constraint = new Url([ 188 'protocols' => ['ftp', 'file', 'git'], 189 ]); 190 191 $this->validator->validate($url, $constraint); 192 193 $this->assertNoViolation(); 194 } 195 196 public function getValidCustomUrls() 197 { 198 return [ 199 ['ftp://example.com'], 200 ['file://127.0.0.1'], 201 ['git://[::1]/'], 202 ]; 203 } 204 205 /** 206 * @dataProvider getCheckDns 207 * @requires function Symfony\Bridge\PhpUnit\DnsMock::withMockedHosts 208 */ 209 public function testCheckDns($violation) 210 { 211 DnsMock::withMockedHosts(['example.com' => [['type' => $violation ? '' : 'A']]]); 212 213 $constraint = new Url([ 214 'checkDNS' => 'ANY', 215 'dnsMessage' => 'myMessage', 216 ]); 217 218 $this->validator->validate('http://example.com', $constraint); 219 220 if (!$violation) { 221 $this->assertNoViolation(); 222 } else { 223 $this->buildViolation('myMessage') 224 ->setParameter('{{ value }}', '"example.com"') 225 ->setCode(Url::INVALID_URL_ERROR) 226 ->assertRaised(); 227 } 228 } 229 230 public function getCheckDns() 231 { 232 return [[true], [false]]; 233 } 234 235 /** 236 * @dataProvider getCheckDnsTypes 237 * @requires function Symfony\Bridge\PhpUnit\DnsMock::withMockedHosts 238 */ 239 public function testCheckDnsByType($type) 240 { 241 DnsMock::withMockedHosts(['example.com' => [['type' => $type]]]); 242 243 $constraint = new Url([ 244 'checkDNS' => $type, 245 'dnsMessage' => 'myMessage', 246 ]); 247 248 $this->validator->validate('http://example.com', $constraint); 249 250 $this->assertNoViolation(); 251 } 252 253 public function getCheckDnsTypes() 254 { 255 return [ 256 ['ANY'], 257 ['A'], 258 ['A6'], 259 ['AAAA'], 260 ['CNAME'], 261 ['MX'], 262 ['NAPTR'], 263 ['NS'], 264 ['PTR'], 265 ['SOA'], 266 ['SRV'], 267 ['TXT'], 268 ]; 269 } 270 271 /** 272 * @group legacy 273 */ 274 public function testCheckDnsWithBoolean() 275 { 276 DnsMock::withMockedHosts(['example.com' => [['type' => 'A']]]); 277 278 $constraint = new Url([ 279 'checkDNS' => true, 280 'dnsMessage' => 'myMessage', 281 ]); 282 283 $this->validator->validate('http://example.com', $constraint); 284 285 $this->assertNoViolation(); 286 } 287 288 /** 289 * @requires function Symfony\Bridge\PhpUnit\DnsMock::withMockedHosts 290 */ 291 public function testCheckDnsWithInvalidType() 292 { 293 $this->expectException('Symfony\Component\Validator\Exception\InvalidOptionsException'); 294 DnsMock::withMockedHosts(['example.com' => [['type' => 'A']]]); 295 296 $constraint = new Url([ 297 'checkDNS' => 'BOGUS', 298 'dnsMessage' => 'myMessage', 299 ]); 300 301 $this->validator->validate('http://example.com', $constraint); 302 } 303} 304 305class EmailProvider 306{ 307 public function __toString() 308 { 309 return ''; 310 } 311} 312