Page Menu
Home
c4science
Search
Configure Global Search
Log In
Files
F102664067
PhutilURITestCase.php
No One
Temporary
Actions
Download File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Subscribers
None
File Metadata
Details
File Info
Storage
Attached
Created
Sun, Feb 23, 00:35
Size
13 KB
Mime Type
text/x-php
Expires
Tue, Feb 25, 00:35 (2 d)
Engine
blob
Format
Raw Data
Handle
24385738
Attached To
rPHU libphutil
PhutilURITestCase.php
View Options
<?php
/**
* Test cases for @{class:PhutilURI} parser.
*/
final
class
PhutilURITestCase
extends
PhutilTestCase
{
public
function
testURIParsing
()
{
$uri
=
new
PhutilURI
(
'http://user:pass@host:99/path/?query=value#fragment'
);
$this
->
assertEqual
(
'http'
,
$uri
->
getProtocol
(),
pht
(
'protocol'
));
$this
->
assertEqual
(
'user'
,
$uri
->
getUser
(),
pht
(
'user'
));
$this
->
assertEqual
(
'pass'
,
$uri
->
getPass
(),
pht
(
'password'
));
$this
->
assertEqual
(
'host'
,
$uri
->
getDomain
(),
pht
(
'domain'
));
$this
->
assertEqual
(
'99'
,
$uri
->
getPort
(),
pht
(
'port'
));
$this
->
assertEqual
(
'/path/'
,
$uri
->
getPath
(),
pht
(
'path'
));
$this
->
assertEqual
(
array
(
array
(
'query'
,
'value'
,
),
),
$uri
->
getQueryParamsAsPairList
(),
'query params'
);
$this
->
assertEqual
(
'fragment'
,
$uri
->
getFragment
(),
pht
(
'fragment'
));
$this
->
assertEqual
(
'http://user:pass@host:99/path/?query=value#fragment'
,
(
string
)
$uri
,
'uri'
);
$uri
=
new
PhutilURI
(
'ssh://git@example.com/example/example.git'
);
$this
->
assertEqual
(
'ssh'
,
$uri
->
getProtocol
(),
pht
(
'protocol'
));
$this
->
assertEqual
(
'git'
,
$uri
->
getUser
(),
pht
(
'user'
));
$this
->
assertEqual
(
''
,
$uri
->
getPass
(),
pht
(
'password'
));
$this
->
assertEqual
(
'example.com'
,
$uri
->
getDomain
(),
pht
(
'domain'
));
$this
->
assertEqual
(
''
,
$uri
->
getPort
(),
'port'
);
$this
->
assertEqual
(
'/example/example.git'
,
$uri
->
getPath
(),
pht
(
'path'
));
$this
->
assertEqual
(
array
(),
$uri
->
getQueryParamsAsPairList
(),
pht
(
'query parameters'
));
$this
->
assertEqual
(
''
,
$uri
->
getFragment
(),
pht
(
'fragment'
));
$this
->
assertEqual
(
'ssh://git@example.com/example/example.git'
,
(
string
)
$uri
,
'uri'
);
$uri
=
new
PhutilURI
(
'http://0@domain.com/'
);
$this
->
assertEqual
(
'0'
,
$uri
->
getUser
());
$this
->
assertEqual
(
'http://0@domain.com/'
,
(
string
)
$uri
);
$uri
=
new
PhutilURI
(
'http://0:0@domain.com/'
);
$this
->
assertEqual
(
'0'
,
$uri
->
getUser
());
$this
->
assertEqual
(
'0'
,
$uri
->
getPass
());
$this
->
assertEqual
(
'http://0:0@domain.com/'
,
(
string
)
$uri
);
$uri
=
new
PhutilURI
(
'http://%20:%20@domain.com/'
);
$this
->
assertEqual
(
' '
,
$uri
->
getUser
());
$this
->
assertEqual
(
' '
,
$uri
->
getPass
());
$this
->
assertEqual
(
'http://%20:%20@domain.com/'
,
(
string
)
$uri
);
$uri
=
new
PhutilURI
(
'http://%40:%40@domain.com/'
);
$this
->
assertEqual
(
'@'
,
$uri
->
getUser
());
$this
->
assertEqual
(
'@'
,
$uri
->
getPass
());
$this
->
assertEqual
(
'http://%40:%40@domain.com/'
,
(
string
)
$uri
);
$uri
=
new
PhutilURI
(
'http://%2F:%2F@domain.com/'
);
$this
->
assertEqual
(
'/'
,
$uri
->
getUser
());
$this
->
assertEqual
(
'/'
,
$uri
->
getPass
());
$this
->
assertEqual
(
'http://%2F:%2F@domain.com/'
,
(
string
)
$uri
);
// These tests are covering cases where cURL and parse_url() behavior
// may differ in potentially dangerous ways. See T6755 for discussion.
// In general, we defuse these attacks by emitting URIs which escape
// special characters so that they are interpreted unambiguously by
// cURL in the same way that parse_url() interpreted them.
$uri
=
new
PhutilURI
(
'http://u:p@evil.com?@good.com'
);
$this
->
assertEqual
(
'u'
,
$uri
->
getUser
());
$this
->
assertEqual
(
'p'
,
$uri
->
getPass
());
$this
->
assertEqual
(
'evil.com'
,
$uri
->
getDomain
());
$this
->
assertEqual
(
'http://u:p@evil.com?%40good.com='
,
(
string
)
$uri
);
// The behavior of URLs in these forms differs for different versions
// of cURL, PHP, and other software. Because safe parsing is a tricky
// proposition and these URIs are almost certainly malicious, we just
// reject them. See T12526 for discussion.
$dangerous
=
array
(
// Ambiguous encoding.
'http://good.com#u:p@evil.com/'
=>
true
,
'http://good.com?u:p@evil.com/'
=>
true
,
// Unambiguous encoding: with a trailing slash.
'http://good.com/#u:p@evil.com/'
=>
false
,
'http://good.com/?u:p@evil.com/'
=>
false
,
// Unambiguous encoding: with escaping.
'http://good.com%23u:p@evil.com/'
=>
false
,
'http://good.com%40u:p@evil.com/'
=>
false
,
);
foreach
(
$dangerous
as
$input
=>
$expect
)
{
$caught
=
null
;
try
{
new
PhutilURI
(
$input
);
}
catch
(
Exception
$ex
)
{
$caught
=
$ex
;
}
$this
->
assertEqual
(
$expect
,
(
$caught
instanceof
$ex
),
pht
(
'Unexpected parse result for dangerous URI "%s".'
,
$input
));
}
$uri
=
new
PhutilURI
(
'www.example.com'
);
$this
->
assertEqual
(
''
,
$uri
->
getProtocol
());
$this
->
assertEqual
(
'www.example.com'
,
(
string
)
$uri
);
}
public
function
testURIGeneration
()
{
$uri
=
new
PhutilURI
(
'http://example.com'
);
$uri
->
setPath
(
'bar'
);
$this
->
assertEqual
(
'http://example.com/bar'
,
$uri
->
__toString
());
}
public
function
testStrictURIParsingOfHosts
()
{
$uri
=
new
PhutilURI
(
'http://&/'
);
$this
->
assertEqual
(
''
,
$uri
->
getDomain
());
// See T12961 for more discussion of these hosts which begin with "-".
$uri
=
new
PhutilURI
(
'ssh://-oProxyCommand/'
);
$this
->
assertEqual
(
''
,
$uri
->
getDomain
());
$uri
=
new
PhutilURI
(
'ssh://-oProxyCommand=curl/'
);
$this
->
assertEqual
(
''
,
$uri
->
getDomain
());
$uri
=
new
PhutilURI
(
'ssh://.com/'
);
$this
->
assertEqual
(
''
,
$uri
->
getDomain
());
// Make sure newlines are rejected.
$uri
=
new
PhutilURI
(
"ssh://example.com
\n
.domain.us/"
);
$this
->
assertEqual
(
''
,
$uri
->
getDomain
());
$uri
=
new
PhutilURI
(
"ssh://example.com
\n
"
);
$this
->
assertEqual
(
''
,
$uri
->
getDomain
());
}
public
function
testStrictURIParsingOfLeadingWhitespace
()
{
$uri
=
new
PhutilURI
(
' http://example.com/'
);
$this
->
assertEqual
(
''
,
$uri
->
getDomain
());
}
public
function
testAppendPath
()
{
$uri
=
new
PhutilURI
(
'http://example.com'
);
$uri
->
appendPath
(
'foo'
);
$this
->
assertEqual
(
'http://example.com/foo'
,
$uri
->
__toString
());
$uri
->
appendPath
(
'bar'
);
$this
->
assertEqual
(
'http://example.com/foo/bar'
,
$uri
->
__toString
());
$uri
=
new
PhutilURI
(
'http://example.com'
);
$uri
->
appendPath
(
'/foo/'
);
$this
->
assertEqual
(
'http://example.com/foo/'
,
$uri
->
__toString
());
$uri
->
appendPath
(
'/bar/'
);
$this
->
assertEqual
(
'http://example.com/foo/bar/'
,
$uri
->
__toString
());
$uri
=
new
PhutilURI
(
'http://example.com'
);
$uri
->
appendPath
(
'foo'
);
$this
->
assertEqual
(
'http://example.com/foo'
,
$uri
->
__toString
());
$uri
->
appendPath
(
'/bar/'
);
$this
->
assertEqual
(
'http://example.com/foo/bar/'
,
$uri
->
__toString
());
}
public
function
testUnusualURIs
()
{
$uri
=
new
PhutilURI
(
'file:///path/to/file'
);
$this
->
assertEqual
(
'file'
,
$uri
->
getProtocol
(),
pht
(
'protocol'
));
$this
->
assertEqual
(
''
,
$uri
->
getDomain
(),
pht
(
'domain'
));
$this
->
assertEqual
(
'/path/to/file'
,
$uri
->
getPath
(),
pht
(
'path'
));
$uri
=
new
PhutilURI
(
'idea://open?x=/'
);
$this
->
assertEqual
(
'idea'
,
$uri
->
getProtocol
(),
pht
(
'protocol'
));
$this
->
assertEqual
(
'open'
,
$uri
->
getDomain
(),
pht
(
'domain'
));
$this
->
assertEqual
(
''
,
$uri
->
getPath
(),
pht
(
'path'
));
$this
->
assertEqual
(
array
(
array
(
'x'
,
'/'
,
),
),
$uri
->
getQueryParamsAsPairList
());
// This is not a legitimate URI and should not parse as one.
$uri
=
new
PhutilURI
(
'fruit.list: apple banana cherry'
);
$this
->
assertEqual
(
''
,
$uri
->
getDomain
());
}
public
function
testAmbiguousURIs
()
{
// It's important that this be detected as a Javascript URI, because that
// is how browsers will treat it.
$uri
=
new
PhutilURI
(
'javascript:evil'
);
$this
->
assertEqual
(
'javascript'
,
$uri
->
getProtocol
());
// This is "wrong", in that the user probably intends for this to be a
// Git-style URI, but we can not easily parse it as one without making the
// "javascript" case above unsafe.
$uri
=
new
PhutilURI
(
'localhost:todo.txt'
);
$this
->
assertEqual
(
'localhost'
,
$uri
->
getProtocol
());
// These variants are unambiguous and safe.
$uri
=
new
PhutilURI
(
'localhost.com:todo.txt'
);
$this
->
assertEqual
(
'localhost.com'
,
$uri
->
getDomain
());
$uri
=
new
PhutilURI
(
'user@localhost:todo.txt'
);
$this
->
assertEqual
(
'localhost'
,
$uri
->
getDomain
());
// This could either be a Git URI with relative path "22", or a normal URI
// with port "22". We should assume it is a port number because this is
// relatively common, while relative Git URIs pointing at numeric filenames
// are bizarre.
$uri
=
new
PhutilURI
(
'domain.com:22'
);
$this
->
assertEqual
(
'domain.com'
,
$uri
->
getDomain
());
$this
->
assertEqual
(
'22'
,
$uri
->
getPort
());
}
public
function
testDefaultPorts
()
{
$uri
=
new
PhutilURI
(
'http://www.example.com'
);
$this
->
assertEqual
(
'80'
,
$uri
->
getPortWithProtocolDefault
());
$uri
=
new
PhutilURI
(
'https://www.example.com'
);
$this
->
assertEqual
(
'443'
,
$uri
->
getPortWithProtocolDefault
());
$uri
=
new
PhutilURI
(
'ssh://git@example.com/example/example.git'
);
$this
->
assertEqual
(
'22'
,
$uri
->
getPortWithProtocolDefault
());
$uri
=
new
PhutilURI
(
'unknown://www.example.com'
);
$this
->
assertEqual
(
''
,
$uri
->
getPortWithProtocolDefault
());
}
public
function
testGitURIParsing
()
{
$uri
=
new
PhutilURI
(
'git@host.com:path/to/something'
);
$this
->
assertEqual
(
'ssh'
,
$uri
->
getProtocol
());
$this
->
assertEqual
(
'git'
,
$uri
->
getUser
());
$this
->
assertEqual
(
'host.com'
,
$uri
->
getDomain
());
$this
->
assertEqual
(
'path/to/something'
,
$uri
->
getPath
());
$this
->
assertEqual
(
'git@host.com:path/to/something'
,
(
string
)
$uri
);
$uri
=
new
PhutilURI
(
'host.com:path/to/something'
);
$this
->
assertEqual
(
'ssh'
,
$uri
->
getProtocol
());
$this
->
assertEqual
(
''
,
$uri
->
getUser
());
$this
->
assertEqual
(
'host.com'
,
$uri
->
getDomain
());
$this
->
assertEqual
(
'path/to/something'
,
$uri
->
getPath
());
$this
->
assertEqual
(
'host.com:path/to/something'
,
(
string
)
$uri
);
$uri_1
=
new
PhutilURI
(
'host.com:path/to/something'
);
$uri_2
=
new
PhutilURI
(
$uri_1
);
$this
->
assertEqual
((
string
)
$uri_1
,
(
string
)
$uri_2
);
}
public
function
testStrictGitURIParsingOfLeadingWhitespace
()
{
$uri
=
new
PhutilURI
(
' user@example.com:path'
);
$this
->
assertEqual
(
''
,
$uri
->
getDomain
());
}
public
function
testNoRelativeURIPaths
()
{
$uri
=
new
PhutilURI
(
'user@example.com:relative_path'
);
$caught
=
null
;
try
{
$uri
->
setType
(
PhutilURI
::
TYPE_URI
);
}
catch
(
Exception
$ex
)
{
$caught
=
$ex
;
}
$this
->
assertTrue
(
$caught
instanceof
Exception
);
}
public
function
testDuplicateKeys
()
{
$uri
=
new
PhutilURI
(
'http://www.example.com/?x=1&x=2'
);
$this
->
assertEqual
(
'http://www.example.com/?x=1&x=2'
,
(
string
)
$uri
);
$uri
->
appendQueryParam
(
'x'
,
'3'
);
$this
->
assertEqual
(
'http://www.example.com/?x=1&x=2&x=3'
,
(
string
)
$uri
);
$uri
->
replaceQueryParam
(
'x'
,
'4'
);
$this
->
assertEqual
(
'http://www.example.com/?x=4'
,
(
string
)
$uri
);
$uri
->
removeQueryParam
(
'x'
);
$this
->
assertEqual
(
'http://www.example.com/'
,
(
string
)
$uri
);
$uri
->
appendQueryParam
(
'a'
,
'a'
);
$uri
->
appendQueryParam
(
'b'
,
'b'
);
$uri
->
appendQueryParam
(
'c'
,
'c'
);
$uri
->
appendQueryParam
(
'b'
,
'd'
);
$this
->
assertEqual
(
'http://www.example.com/?a=a&b=b&c=c&b=d'
,
(
string
)
$uri
);
$uri
->
replaceQueryParam
(
'b'
,
'e'
);
$this
->
assertEqual
(
'http://www.example.com/?a=a&c=c&b=e'
,
(
string
)
$uri
,
pht
(
'Replacing a parameter should overwrite other instances of the key.'
));
}
public
function
testBadHTTPParameters
()
{
$uri
=
new
PhutilURI
(
'http://www.example.com/'
);
$caught
=
null
;
try
{
$uri
->
replaceQueryParam
(
array
(),
'x'
);
}
catch
(
Exception
$ex
)
{
$caught
=
$ex
;
}
$this
->
assertTrue
(
(
bool
)
$caught
,
pht
(
'Nonscalar HTTP keys should throw.'
));
$caught
=
null
;
try
{
$uri
->
replaceQueryParam
(
'x'
,
array
());
}
catch
(
Exception
$ex
)
{
$caught
=
$ex
;
}
$this
->
assertTrue
(
(
bool
)
$caught
,
pht
(
'Nonscalar HTTP values should throw.'
));
}
public
function
testHTTPParameterTypes
()
{
// Whether you pass an integer or string, "0" should always be the same
// query parameter.
$uri
=
new
PhutilURI
(
'http://www.example.com/'
);
$uri
->
appendQueryParam
(
0
,
'a'
);
$uri
->
appendQueryParam
(
'0'
,
'b'
);
$this
->
assertEqual
(
'http://www.example.com/?0=a&0=b'
,
(
string
)
$uri
);
$uri
->
replaceQueryParam
(
0
,
'c'
);
$this
->
assertEqual
(
'http://www.example.com/?0=c'
,
(
string
)
$uri
);
$uri
->
replaceQueryParam
(
0
,
'a'
);
$uri
->
appendQueryParam
(
'0'
,
'b'
);
$this
->
assertEqual
(
'http://www.example.com/?0=a&0=b'
,
(
string
)
$uri
);
$uri
->
replaceQueryParam
(
'0'
,
'c'
);
$this
->
assertEqual
(
'http://www.example.com/?0=c'
,
(
string
)
$uri
);
}
public
function
testGetQueryParamsAsMap
()
{
$uri
=
new
PhutilURI
(
'http://www.example.com/?x=1&x=2'
);
$caught
=
null
;
try
{
$map
=
$uri
->
getQueryParamsAsMap
();
}
catch
(
Exception
$ex
)
{
$caught
=
$ex
;
}
$this
->
assertTrue
((
bool
)
$caught
);
}
public
function
testQueryURIConstruction
()
{
$uri
=
new
PhutilURI
(
'http://example.com/'
,
array
(
'y'
=>
'1'
));
$this
->
assertEqual
(
'http://example.com/?y=1'
,
(
string
)
$uri
);
$uri
=
new
PhutilURI
(
'http://example.com/?x=2'
,
array
(
'y'
=>
'1'
));
$this
->
assertEqual
(
'http://example.com/?x=2&y=1'
,
(
string
)
$uri
);
$caught
=
null
;
try
{
$uri
=
new
PhutilURI
(
'http://example.com/?y=3'
,
array
(
'y'
=>
'1'
));
}
catch
(
InvalidArgumentException
$ex
)
{
$caught
=
$ex
;
}
$this
->
assertTrue
((
bool
)
$caught
);
$uri
=
new
PhutilURI
(
'http://example.com/?a=1'
,
array
(
'b'
=>
'2'
));
$uri
=
new
PhutilURI
(
$uri
,
array
(
'c'
=>
'3'
));
$this
->
assertEqual
(
'http://example.com/?a=1&b=2&c=3'
,
(
string
)
$uri
);
}
}
Event Timeline
Log In to Comment