Skip to content

Commit 3a53f2d

Browse files
garethellis36Gareth Ellis
and
Gareth Ellis
authored
#89 StringURI now handles validation of valid URIs which aren't URLs (#90)
* #89 Adds failing tests for StringURI validation * StringURI now validates all the same valid URIs as league/uri * Fixes PHPCS issues Co-authored-by: Gareth Ellis <[email protected]>
1 parent 313ecf7 commit 3a53f2d

File tree

3 files changed

+85
-9
lines changed

3 files changed

+85
-9
lines changed

composer.json

+1
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
"ext-json": "*",
2626
"cebe/php-openapi": "^1.3",
2727
"dflydev/fig-cookies": "^2.0",
28+
"league/uri": "^6.3",
2829
"psr/cache": "^1.0",
2930
"psr/http-message": "^1.0",
3031
"psr/http-server-middleware": "^1.0",

src/Schema/TypeFormats/StringURI.php

+7-5
Original file line numberDiff line numberDiff line change
@@ -4,17 +4,19 @@
44

55
namespace League\OpenAPIValidation\Schema\TypeFormats;
66

7-
use const FILTER_VALIDATE_URL;
8-
use function filter_var;
7+
use League\Uri\Exceptions\SyntaxError;
8+
use League\Uri\UriString;
99

1010
class StringURI
1111
{
1212
public function __invoke(string $value) : bool
1313
{
14-
if ($value === 'about:blank') {
14+
try {
15+
UriString::parse($value);
16+
1517
return true;
18+
} catch (SyntaxError $error) {
19+
return false;
1620
}
17-
18-
return filter_var($value, FILTER_VALIDATE_URL) !== false;
1921
}
2022
}

tests/Schema/TypeFormats/StringURITest.php

+77-4
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
use League\OpenAPIValidation\Schema\TypeFormats\StringURI;
88
use PHPUnit\Framework\TestCase;
9+
use function rawurlencode;
910

1011
class StringURITest extends TestCase
1112
{
@@ -15,8 +16,59 @@ class StringURITest extends TestCase
1516
public function greenURIDataProvider() : array
1617
{
1718
return [
18-
['http://example.com'],
19-
['about:blank'],
19+
'about:blank' => ['about:blank'],
20+
'scheme with non-leading digit' => ['s3://somebucket/somefile.txt'],
21+
'uri with host ascii version' => ['scheme://user:[email protected]'],
22+
'complete URI' => ['scheme://user:pass@host:81/path?query#fragment'],
23+
'URI is not normalized' => ['ScheMe://user:pass@HoSt:81/path?query#fragment'],
24+
'URI without scheme' => ['//user:pass@HoSt:81/path?query#fragment'],
25+
'URI without empty authority only' => ['//'],
26+
'URI without userinfo' => ['scheme://HoSt:81/path?query#fragment'],
27+
'URI with empty userinfo' => ['scheme://@HoSt:81/path?query#fragment'],
28+
'URI without port' => ['scheme://user:pass@host/path?query#fragment'],
29+
'URI with an empty port' => ['scheme://user:pass@host:/path?query#fragment'],
30+
'URI without user info and port' => ['scheme://host/path?query#fragment'],
31+
'URI with host IP' => ['scheme://10.0.0.2/p?q#f'],
32+
'URI with scoped IP' => ['scheme://[fe80:1234::%251]/p?q#f'],
33+
'URI with IP future' => ['scheme://[vAF.1::2::3]/p?q#f'],
34+
'URI without authority' => ['scheme:path?query#fragment'],
35+
'URI without authority and scheme' => ['/path'],
36+
'URI with empty host' => ['scheme:///path?query#fragment'],
37+
'URI with empty host and without scheme' => ['///path?query#fragment'],
38+
'URI without path' => ['scheme://[FEDC:BA98:7654:3210:FEDC:BA98:7654:3210]?query#fragment'],
39+
'URI without path and scheme' => ['//[FEDC:BA98:7654:3210:FEDC:BA98:7654:3210]?query#fragment'],
40+
'URI without scheme with IPv6 host and port' => ['//[FEDC:BA98:7654:3210:FEDC:BA98:7654:3210]:42?query#fragment'],
41+
'complete URI without scheme' => ['//user@[FEDC:BA98:7654:3210:FEDC:BA98:7654:3210]:42?q#f'],
42+
'URI without authority and query' => ['scheme:path#fragment'],
43+
'URI with empty query' => ['scheme:path?#fragment'],
44+
'URI with query only' => ['?query'],
45+
'URI without fragment' => ['tel:05000'],
46+
'URI with empty fragment' => ['scheme:path#'],
47+
'URI with fragment only' => ['#fragment'],
48+
'URI with empty fragment only' => ['#'],
49+
'URI without authority 2' => ['path#fragment'],
50+
'URI with empty query and fragment' => ['?#'],
51+
'URI with absolute path' => ['/?#'],
52+
'URI with absolute authority' => ['https://thephpleague.com./p?#f'],
53+
'URI with absolute path only' => ['/'],
54+
'URI with empty query only' => ['?'],
55+
'relative path' => ['../relative/path'],
56+
'complex authority' => ['http://a_.!~*\'(-)n0123Di%25%26:pass;:&=+$,[email protected]'],
57+
'complex authority without scheme' => ['//a_.!~*\'(-)n0123Di%25%26:pass;:&=+$,[email protected]'],
58+
'single word is a path' => ['http'],
59+
'URI scheme with an empty authority' => ['http://'],
60+
'single word is a path, no' => ['http:::/path'],
61+
'fragment with pseudo segment' => ['http://example.com#foo=1/bar=2'],
62+
'empty string' => [''],
63+
'complex URI' => ['htà+d/s:totot'],
64+
'scheme only URI' => ['http:'],
65+
'RFC3986 LDAP example' => ['ldap://[2001:db8::7]/c=GB?objectClass?one'],
66+
'RFC3987 example' => ['http://bébé.bé./有词法别名.zh'],
67+
'colon detection respect RFC3986 (1)' => ['http://example.org/hello:12?foo=bar#test'],
68+
'colon detection respect RFC3986 (2)' => ['/path/to/colon:34'],
69+
'scheme with hyphen' => ['android-app://org.wikipedia/http/en.m.wikipedia.org/wiki/The_Hitchhiker%27s_Guide_to_the_Galaxy'],
70+
'Authority is the colon' => ['ftp://:/p?q#f'],
71+
'URI with 0 leading port' => ['scheme://user:pass@host:000000000081/path?query#fragment'],
2072
];
2173
}
2274

@@ -26,8 +78,29 @@ public function greenURIDataProvider() : array
2678
public function redURIDataProvider() : array
2779
{
2880
return [
29-
['example.com'],
30-
['www.example.com'],
81+
'invalid scheme' => ['0scheme://host/path?query#fragment'],
82+
'invalid path' => ['://host:80/p?q#f'],
83+
'invalid port (1)' => ['//host:port/path?query#fragment'],
84+
'invalid port (2)' => ['//host:-892358/path?query#fragment'],
85+
'invalid host' => ['http://exam ple.com'],
86+
'invalid ipv6 host (1)' => ['scheme://[127.0.0.1]/path?query#fragment'],
87+
'invalid ipv6 host (2)' => ['scheme://]::1[/path?query#fragment'],
88+
'invalid ipv6 host (3)' => ['scheme://[::1|/path?query#fragment'],
89+
'invalid ipv6 host (4)' => ['scheme://|::1]/path?query#fragment'],
90+
'invalid ipv6 host (5)' => ['scheme://[::1]./path?query#fragment'],
91+
'invalid ipv6 host (6)' => ['scheme://[[::1]]:80/path?query#fragment'],
92+
'invalid ipv6 scoped (1)' => ['scheme://[::1%25%23]/path?query#fragment'],
93+
'invalid ipv6 scoped (2)' => ['scheme://[fe80::1234::%251]/path?query#fragment'],
94+
'invalid char on URI' => ["scheme://host/path/\r\n/toto"],
95+
'invalid path only URI' => ['2620:0:1cfe:face:b00c::3'],
96+
'invalid path PHP bug #72811' => ['[::1]:80'],
97+
'invalid ipvfuture' => ['//[v6.::1]/p?q#f'],
98+
'invalid RFC3987 host' => ['//a⒈com/p?q#f'],
99+
'invalid RFC3987 host URL encoded' => ['//' . rawurlencode('a⒈com') . '/p?q#f'],
100+
'invalid Host with fullwith (1)' => ['http://%00.com'],
101+
'invalid host with fullwidth escaped' => ['http://%ef%bc%85%ef%bc%94%ef%bc%91.com],'],
102+
'invalid pseudo IDN to ASCII string' => ['http://xn--3/foo.'],
103+
'invalid IDN' => ['//:�@�����������������������������������������������������������������������������������������/'],
31104
];
32105
}
33106

0 commit comments

Comments
 (0)