diff --git a/composer.json b/composer.json index 6a96c107..0a2f1db8 100644 --- a/composer.json +++ b/composer.json @@ -45,7 +45,7 @@ "ext-libxml": "*", "http-interop/http-factory-tests": "^0.9.0", "laminas/laminas-coding-standard": "^2.4.0", - "php-http/psr7-integration-tests": "^1.1.1", + "php-http/psr7-integration-tests": "^1.2", "phpunit/phpunit": "^9.5.26", "psalm/plugin-phpunit": "^0.18.0", "vimeo/psalm": "^5.0.0" diff --git a/composer.lock b/composer.lock index a28a4d52..fb4966be 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "ea3023a656a7fe64ada7d14e983774a1", + "content-hash": "e648a7ae1f89270f45676c296966494f", "packages": [ { "name": "psr/http-factory", @@ -856,6 +856,67 @@ }, "time": "2022-03-02T22:36:06+00:00" }, + { + "name": "fidry/cpu-core-counter", + "version": "0.4.0", + "source": { + "type": "git", + "url": "https://github.com/theofidry/cpu-core-counter.git", + "reference": "666cb04a02f2801f3b19955fc23c824f9018bf64" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/theofidry/cpu-core-counter/zipball/666cb04a02f2801f3b19955fc23c824f9018bf64", + "reference": "666cb04a02f2801f3b19955fc23c824f9018bf64", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "require-dev": { + "fidry/makefile": "^0.2.0", + "phpstan/extension-installer": "^1.2.0", + "phpstan/phpstan": "^1.9.2", + "phpstan/phpstan-deprecation-rules": "^1.0.0", + "phpstan/phpstan-phpunit": "^1.2.2", + "phpstan/phpstan-strict-rules": "^1.4.4", + "phpunit/phpunit": "^9.5.26 || ^8.5.31", + "theofidry/php-cs-fixer-config": "^1.0", + "webmozarts/strict-phpunit": "^7.5" + }, + "type": "library", + "autoload": { + "psr-4": { + "Fidry\\CpuCoreCounter\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Théo FIDRY", + "email": "theo.fidry@gmail.com" + } + ], + "description": "Tiny utility to get the number of CPU cores.", + "keywords": [ + "CPU", + "core" + ], + "support": { + "issues": "https://github.com/theofidry/cpu-core-counter/issues", + "source": "https://github.com/theofidry/cpu-core-counter/tree/0.4.0" + }, + "funding": [ + { + "url": "https://github.com/theofidry", + "type": "github" + } + ], + "time": "2022-12-10T21:26:31+00:00" + }, { "name": "http-interop/http-factory-tests", "version": "0.9.0", @@ -1022,16 +1083,16 @@ }, { "name": "netresearch/jsonmapper", - "version": "v4.0.0", + "version": "v4.1.0", "source": { "type": "git", "url": "https://github.com/cweiske/jsonmapper.git", - "reference": "8bbc021a8edb2e4a7ea2f8ad4fa9ec9dce2fcb8d" + "reference": "cfa81ea1d35294d64adb9c68aa4cb9e92400e53f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/cweiske/jsonmapper/zipball/8bbc021a8edb2e4a7ea2f8ad4fa9ec9dce2fcb8d", - "reference": "8bbc021a8edb2e4a7ea2f8ad4fa9ec9dce2fcb8d", + "url": "https://api.github.com/repos/cweiske/jsonmapper/zipball/cfa81ea1d35294d64adb9c68aa4cb9e92400e53f", + "reference": "cfa81ea1d35294d64adb9c68aa4cb9e92400e53f", "shasum": "" }, "require": { @@ -1067,9 +1128,9 @@ "support": { "email": "cweiske@cweiske.de", "issues": "https://github.com/cweiske/jsonmapper/issues", - "source": "https://github.com/cweiske/jsonmapper/tree/v4.0.0" + "source": "https://github.com/cweiske/jsonmapper/tree/v4.1.0" }, - "time": "2020-12-01T19:48:11+00:00" + "time": "2022-12-08T20:46:14+00:00" }, { "name": "nikic/php-parser", @@ -1293,16 +1354,16 @@ }, { "name": "php-http/psr7-integration-tests", - "version": "1.1.1", + "version": "1.2.0", "source": { "type": "git", "url": "https://github.com/php-http/psr7-integration-tests.git", - "reference": "dbc81e59655c3d927ba62b2cd38be9af334590fc" + "reference": "d11c83205562b7a4701873e496ec8d0b71946e36" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-http/psr7-integration-tests/zipball/dbc81e59655c3d927ba62b2cd38be9af334590fc", - "reference": "dbc81e59655c3d927ba62b2cd38be9af334590fc", + "url": "https://api.github.com/repos/php-http/psr7-integration-tests/zipball/d11c83205562b7a4701873e496ec8d0b71946e36", + "reference": "d11c83205562b7a4701873e496ec8d0b71946e36", "shasum": "" }, "require": { @@ -1311,7 +1372,7 @@ "psr/http-message": "^1.0" }, "require-dev": { - "guzzlehttp/psr7": "^1.4", + "guzzlehttp/psr7": "^1.7 || ^2.0", "laminas/laminas-diactoros": "^2.1", "nyholm/psr7": "^1.0", "ringcentral/psr7": "^1.2", @@ -1346,9 +1407,9 @@ ], "support": { "issues": "https://github.com/php-http/psr7-integration-tests/issues", - "source": "https://github.com/php-http/psr7-integration-tests/tree/1.1.1" + "source": "https://github.com/php-http/psr7-integration-tests/tree/1.2.0" }, - "time": "2020-11-02T14:08:03+00:00" + "time": "2022-12-01T07:49:31+00:00" }, { "name": "phpdocumentor/reflection-common", @@ -1561,16 +1622,16 @@ }, { "name": "phpunit/php-code-coverage", - "version": "9.2.19", + "version": "9.2.21", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "c77b56b63e3d2031bd8997fcec43c1925ae46559" + "reference": "3f893e19712bb0c8bc86665d1562e9fd509c4ef0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/c77b56b63e3d2031bd8997fcec43c1925ae46559", - "reference": "c77b56b63e3d2031bd8997fcec43c1925ae46559", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/3f893e19712bb0c8bc86665d1562e9fd509c4ef0", + "reference": "3f893e19712bb0c8bc86665d1562e9fd509c4ef0", "shasum": "" }, "require": { @@ -1626,7 +1687,7 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", - "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.19" + "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.21" }, "funding": [ { @@ -1634,7 +1695,7 @@ "type": "github" } ], - "time": "2022-11-18T07:47:47+00:00" + "time": "2022-12-14T13:26:54+00:00" }, { "name": "phpunit/php-file-iterator", @@ -1879,16 +1940,16 @@ }, { "name": "phpunit/phpunit", - "version": "9.5.26", + "version": "9.5.27", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "851867efcbb6a1b992ec515c71cdcf20d895e9d2" + "reference": "a2bc7ffdca99f92d959b3f2270529334030bba38" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/851867efcbb6a1b992ec515c71cdcf20d895e9d2", - "reference": "851867efcbb6a1b992ec515c71cdcf20d895e9d2", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/a2bc7ffdca99f92d959b3f2270529334030bba38", + "reference": "a2bc7ffdca99f92d959b3f2270529334030bba38", "shasum": "" }, "require": { @@ -1961,7 +2022,7 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/phpunit/issues", - "source": "https://github.com/sebastianbergmann/phpunit/tree/9.5.26" + "source": "https://github.com/sebastianbergmann/phpunit/tree/9.5.27" }, "funding": [ { @@ -1977,20 +2038,20 @@ "type": "tidelift" } ], - "time": "2022-10-28T06:00:21+00:00" + "time": "2022-12-09T07:31:23+00:00" }, { "name": "psalm/plugin-phpunit", - "version": "0.18.3", + "version": "0.18.4", "source": { "type": "git", "url": "https://github.com/psalm/psalm-plugin-phpunit.git", - "reference": "057c1cdf7546c1e427f6fd83b635d0cc18c252bf" + "reference": "e4ab3096653d9eb6f6d0ea5f4461898d59ae4dbc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/psalm/psalm-plugin-phpunit/zipball/057c1cdf7546c1e427f6fd83b635d0cc18c252bf", - "reference": "057c1cdf7546c1e427f6fd83b635d0cc18c252bf", + "url": "https://api.github.com/repos/psalm/psalm-plugin-phpunit/zipball/e4ab3096653d9eb6f6d0ea5f4461898d59ae4dbc", + "reference": "e4ab3096653d9eb6f6d0ea5f4461898d59ae4dbc", "shasum": "" }, "require": { @@ -1998,7 +2059,7 @@ "composer/semver": "^1.4 || ^2.0 || ^3.0", "ext-simplexml": "*", "php": "^7.1 || ^8.0", - "vimeo/psalm": "dev-master || dev-4.x || ^4.5 || ^5@beta" + "vimeo/psalm": "dev-master || dev-4.x || ^4.7.1 || ^5@beta || ^5.0" }, "conflict": { "phpunit/phpunit": "<7.5" @@ -2035,9 +2096,9 @@ "description": "Psalm plugin for PHPUnit", "support": { "issues": "https://github.com/psalm/psalm-plugin-phpunit/issues", - "source": "https://github.com/psalm/psalm-plugin-phpunit/tree/0.18.3" + "source": "https://github.com/psalm/psalm-plugin-phpunit/tree/0.18.4" }, - "time": "2022-11-03T18:17:28+00:00" + "time": "2022-12-03T07:47:07+00:00" }, { "name": "psr/container", @@ -4013,16 +4074,16 @@ }, { "name": "vimeo/psalm", - "version": "5.0.0", + "version": "5.2.0", "source": { "type": "git", "url": "https://github.com/vimeo/psalm.git", - "reference": "4e177bf0c9f03c17d2fbfd83b7cc9c47605274d8" + "reference": "fb685a16df3050d4c18d8a4100fe83abe6458cba" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/vimeo/psalm/zipball/4e177bf0c9f03c17d2fbfd83b7cc9c47605274d8", - "reference": "4e177bf0c9f03c17d2fbfd83b7cc9c47605274d8", + "url": "https://api.github.com/repos/vimeo/psalm/zipball/fb685a16df3050d4c18d8a4100fe83abe6458cba", + "reference": "fb685a16df3050d4c18d8a4100fe83abe6458cba", "shasum": "" }, "require": { @@ -4041,6 +4102,7 @@ "ext-tokenizer": "*", "felixfbecker/advanced-json-rpc": "^3.1", "felixfbecker/language-server-protocol": "^1.5.2", + "fidry/cpu-core-counter": "^0.4.0", "netresearch/jsonmapper": "^1.0 || ^2.0 || ^3.0 || ^4.0", "nikic/php-parser": "^4.13", "openlss/lib-array2xml": "^1.0", @@ -4111,9 +4173,9 @@ ], "support": { "issues": "https://github.com/vimeo/psalm/issues", - "source": "https://github.com/vimeo/psalm/tree/5.0.0" + "source": "https://github.com/vimeo/psalm/tree/5.2.0" }, - "time": "2022-11-30T06:06:01+00:00" + "time": "2022-12-12T08:18:56+00:00" }, { "name": "webimpress/coding-standard", diff --git a/src/Uri.php b/src/Uri.php index b2183187..1e70d1aa 100644 --- a/src/Uri.php +++ b/src/Uri.php @@ -113,7 +113,7 @@ public function __toString(): string $this->uriString = static::createUriString( $this->scheme, $this->getAuthority(), - $this->getPath(), // Absolute URIs should use a "/" for an empty path + $this->path, // Absolute URIs should use a "/" for an empty path $this->query, $this->fragment ); @@ -185,7 +185,18 @@ public function getPort(): ?int */ public function getPath(): string { - return $this->path; + if ('' === $this->path) { + // No path + return $this->path; + } + + if ($this->path[0] !== '/') { + // Relative path + return $this->path; + } + + // Ensure only one leading slash, to prevent XSS attempts. + return '/' . ltrim($this->path, '/'); } /** @@ -557,7 +568,7 @@ private function filterPath(string $path): string { $path = $this->filterInvalidUtf8($path); - $path = preg_replace_callback( + return preg_replace_callback( '/(?:[^' . self::CHAR_UNRESERVED . ')(:@&=\+\$,\/;%]+|%(?![A-Fa-f0-9]{2}))/u', [$this, 'urlEncodeChar'], $path diff --git a/test/UriTest.php b/test/UriTest.php index d342a344..848241af 100644 --- a/test/UriTest.php +++ b/test/UriTest.php @@ -575,13 +575,6 @@ public function testFragmentIsNotDoubleEncoded(): void $this->assertSame($expected, $uri->getFragment()); } - public function testProperlyTrimsLeadingSlashesToPreventXSS(): void - { - $url = 'http://example.org//zend.com'; - $uri = new Uri($url); - $this->assertSame('http://example.org/zend.com', (string) $uri); - } - /** @return non-empty-array */ public function invalidStringComponentValues(): array {