Skip to content

Commit

Permalink
Adding UriInfo::isCrossOrigin method
Browse files Browse the repository at this point in the history
  • Loading branch information
nyamsprod committed Jun 28, 2022
1 parent 8723973 commit 7de8cda
Show file tree
Hide file tree
Showing 5 changed files with 75 additions and 13 deletions.
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ All Notable changes to `League\Uri` will be documented in this file

### Added

- None
- `UriInfo::isCrossOrigin` method

### Fixed

Expand Down
2 changes: 1 addition & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@
"phpcs": "php-cs-fixer fix -v --diff --dry-run --allow-risky=yes --ansi",
"phpcs:fix": "php-cs-fixer fix -vvv --allow-risky=yes --ansi",
"phpstan": "phpstan analyse -l max -c phpstan.neon src --ansi --memory-limit=256M",
"phpunit": "phpunit --coverage-text",
"phpunit": "XDEBUG_MODE=coverage phpunit --coverage-text",
"test": [
"@phpunit",
"@phpstan",
Expand Down
24 changes: 23 additions & 1 deletion docs/uri/6.0/info.md
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ UriInfo::getOrigin(Http::createFromString('file:///usr/bin/php')); //returns nul
UriInfo::getOrigin(Uri::createFromString('data:text/plain,Bonjour%20le%20monde%21')); //returns null
~~~

<p class="message-info">For absolute URI with the `file` scheme the method will return <code>null</code> (as this is left to the implementation decision)</p>
<p class="message-info">For absolute URI with the <code>file</code> scheme the method will return <code>null</code> (as this is left to the implementation decision)</p>

Because the origin property does not exists in the RFC3986 specification this additional steps is implemented:

Expand All @@ -92,3 +92,25 @@ use League\Uri\UriInfo;

UriInfo::getOrigin(Http::createFromString('/path/to/endpoint')); //returns null
~~~

## UriInfo::isCrossOrigin

This public static method tells whether the given URI object represents different origins.
According to [RFC9110](https://www.rfc-editor.org/rfc/rfc9110#section-4.3.1) The "origin" for a given URI is the triple of scheme, host, and port
after normalizing the scheme and host to lowercase and normalizing the port to remove any leading
zeros.

~~~php
<?php

use League\Uri\Http;
use League\Uri\Uri;
use League\Uri\UriInfo;

UriInfo::isCrossOrigin('http://example.com:80/123', 'http://example.com/'); //returns false
UriInfo::isCrossOrigin('https://xn--bb-bjab.be./path', 'https://Bébé.BE./path'); //returns false
UriInfo::isCrossOrigin(Http::createFromString('https://example.com/123'), Uri::createFromString('https://www.example.com/')); //returns true
~~~

The method expects URI objects (implementing the League or PSR `UriInterface`).
The method takes into account i18n while comparing both URI.
29 changes: 19 additions & 10 deletions src/UriInfo.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,9 @@

final class UriInfo
{
private const REGEXP_ENCODED_CHARS = ',%(2[D|E]|3[0-9]|4[1-9|A-F]|5[0-9|A|F]|6[1-9|A-F]|7[0-9|E]),i';
private const REGEXP_ENCODED_CHARS = ',%(2[D|E]|3[0-9]|4[1-9|A-F]|5[0-9|AF]|6[1-9|A-F]|7[0-9|E]),i';

private const WHATWG_SPECIAL_SCHEMES = ['ftp', 'http', 'https', 'ws', 'wss'];
private const WHATWG_SPECIAL_SCHEMES = ['ftp' => 21, 'http' => 80, 'https' => 443, 'ws' => 80, 'wss' => 443];

/**
* @codeCoverageIgnore
Expand Down Expand Up @@ -88,9 +88,7 @@ private static function normalize($uri)
$pairs = null === $query ? [] : explode('&', $query);
sort($pairs, SORT_REGULAR);

$replace = static function (array $matches): string {
return rawurldecode($matches[0]);
};
$replace = static fn (array $matches): string => rawurldecode($matches[0]);

$retval = preg_replace_callback(self::REGEXP_ENCODED_CHARS, $replace, [$path, implode('&', $pairs), $fragment]);
if (null !== $retval) {
Expand Down Expand Up @@ -195,12 +193,23 @@ public static function getOrigin($uri): ?string
$scheme = $uri->getScheme();
}

if (in_array($scheme, self::WHATWG_SPECIAL_SCHEMES, true)) {
$null = self::emptyComponentValue($uri);

return (string) $uri->withFragment($null)->withQuery($null)->withPath('')->withUserInfo($null, null);
if (null === $scheme || !array_key_exists($scheme, self::WHATWG_SPECIAL_SCHEMES)) {
return null;
}

return null;
$null = self::emptyComponentValue($uri);

return (string) $uri->withFragment($null)->withQuery($null)->withPath('')->withUserInfo($null);
}

/**
* @param Psr7UriInterface|UriInterface $uri
* @param Psr7UriInterface|UriInterface $base_uri
*/
public static function isCrossOrigin($uri, $base_uri): bool
{
return null === ($uriString = self::getOrigin($uri))
|| null === ($baseUriString = self::getOrigin($base_uri))
|| $uriString !== $baseUriString;
}
}
31 changes: 31 additions & 0 deletions src/UriInfoTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -215,4 +215,35 @@ public function getOriginProvider(): array
],
];
}

/**
* @dataProvider getCrossOriginExamples
*/
public function testIsCrossOrigin(string $original, string $modified, bool $expected): void
{
self::assertSame($expected, UriInfo::isCrossOrigin(Uri::createFromString($original), Http::createFromString($modified)));
}

/**
* @return array<string, array{0:string, 1:string, 2:bool}>
*/
public function getCrossOriginExamples(): array
{
return [
'different path' => ['http://example.com/123', 'http://example.com/', false],
'same port with default value (1)' => ['https://example.com/123', 'https://example.com:443/', false],
'same port with default value (2)' => ['ws://example.com:80/123', 'ws://example.com/', false],
'same explicit port' => ['wss://example.com:443/123', 'wss://example.com:443/', false],
'same origin with i18n host' => ['https://xn--bb-bjab.be./path', 'https://Bébé.BE./path', false],
'same origin using a blob' => ['blob:https://mozilla.org:443/', 'https://mozilla.org/123', false],
'different scheme' => ['https://example.com/123', 'ftp://example.com/', true],
'different host' => ['ftp://example.com/123', 'ftp://www.example.com/123', true],
'different port implicit' => ['https://example.com/123', 'https://example.com:81/', true],
'different port explicit' => ['https://example.com:80/123', 'https://example.com:81/', true],
'same scheme different port' => ['https://example.com:443/123', 'https://example.com:444/', true],
'comparing two opaque URI' => ['ldap://ldap.example.net', 'ldap://ldap.example.net', true],
'comparing a URI with an origin and one with an opaque origin' => ['https://example.com:443/123', 'ldap://ldap.example.net', true],
'cross origin using a blob' => ['blob:http://mozilla.org:443/', 'https://mozilla.org/123', true],
];
}
}

0 comments on commit 7de8cda

Please sign in to comment.