diff --git a/core/lib/Drupal/Core/Url.php b/core/lib/Drupal/Core/Url.php index 6a8c17a46e79a77e5060ea06956d435d610c5c8a..8c59abe4d8b56bd6728a5de68f00c209dd2923c8 100644 --- a/core/lib/Drupal/Core/Url.php +++ b/core/lib/Drupal/Core/Url.php @@ -353,6 +353,9 @@ protected static function fromEntityUri(array $uri_parts, array $options, $uri) * * @return \Drupal\Core\Url * A new Url object for a 'user-path:' URI. + * + * @throws \InvalidArgumentException + * Thrown when the URI's path component doesn't have a leading slash. */ protected static function fromUserPathUri(array $uri_parts, array $options) { // Both PathValidator::getUrlIfValidWithoutAccessCheck() and 'base:' URIs @@ -366,6 +369,9 @@ protected static function fromUserPathUri(array $uri_parts, array $options) { $uri_parts['path'] = '<front>'; } else { + if ($uri_parts['path'][0] !== '/') { + throw new \InvalidArgumentException(String::format('The user-path path component "@path" is invalid. Its path component must have a leading slash, e.g. user-path:/foo.', ['@path' => $uri_parts['path']])); + } // Remove the leading slash. $uri_parts['path'] = substr($uri_parts['path'], 1); } diff --git a/core/modules/link/src/Plugin/Field/FieldWidget/LinkWidget.php b/core/modules/link/src/Plugin/Field/FieldWidget/LinkWidget.php index 3203423b5edf19451356217a3dee58f94dfe62e6..e90d41686cdd1b5eb6246171035a8c4dfe79b5a7 100644 --- a/core/modules/link/src/Plugin/Field/FieldWidget/LinkWidget.php +++ b/core/modules/link/src/Plugin/Field/FieldWidget/LinkWidget.php @@ -154,6 +154,7 @@ public static function validateUriElement($element, FormStateInterface $form_sta // https://www.drupal.org/node/2421941 if (parse_url($uri, PHP_URL_SCHEME) === 'user-path' && !in_array($element['#value'][0], ['/', '?', '#'], TRUE) && substr($element['#value'], 0, 7) !== '<front>') { $form_state->setError($element, t('Manually entered paths should start with /, ? or #.')); + return; } // If the URI is empty or not well-formed, the link field type's validation diff --git a/core/tests/Drupal/Tests/Core/UrlTest.php b/core/tests/Drupal/Tests/Core/UrlTest.php index 4096e3d2658a242059dd6fb54da2fd4412564456..d57ae8b3365545f06464dde032b956eda7b3d56e 100644 --- a/core/tests/Drupal/Tests/Core/UrlTest.php +++ b/core/tests/Drupal/Tests/Core/UrlTest.php @@ -636,6 +636,78 @@ public function providerTestToUriStringForUserPath() { ]; } + /** + * Tests the fromUri() method with a valid user-path: URI. + * + * @covers ::fromUri + * @dataProvider providerFromValidUserPathUri + */ + public function testFromValidUserPathUri($path) { + $url = Url::fromUri('user-path:' . $path); + $this->assertInstanceOf('Drupal\Core\Url', $url); + } + + /** + * Data provider for testFromValidUserPathUri(). + */ + public function providerFromValidUserPathUri() { + return [ + // Normal paths with a leading slash. + ['/kittens'], + ['/kittens/bengal'], + // Fragments with and without leading slashes. + ['/#about-our-kittens'], + ['/kittens#feeding'], + ['#feeding'], + // Query strings with and without leading slashes. + ['/kittens?page=1000'], + ['/?page=1000'], + ['?page=1000'], + // Paths with various token formats but no leading slash. + ['/[duckies]'], + ['/%bunnies'], + ['/{{ puppies }}'], + // Disallowed characters in the authority (host name) that are valid + // elsewhere in the path. + ['/(:;2&+h^'], + ['/AKI@&hO@'], + ]; + } + + /** + * Tests the fromUri() method with an invalid user-path: URI. + * + * @covers ::fromUri + * @expectedException \InvalidArgumentException + * @dataProvider providerFromInvalidUserPathUri + */ + public function testFromInvalidUserPathUri($path) { + Url::fromUri('user-path:' . $path); + } + + /** + * Data provider for testFromInvalidUserPathUri(). + */ + public function providerFromInvalidUserPathUri() { + return [ + // Normal paths without a leading slash. + ['kittens'], + ['kittens/bengal'], + // Path without a leading slash containing a fragment. + ['kittens#feeding'], + // Path without a leading slash containing a query string. + ['kittens?page=1000'], + // Paths with various token formats but no leading slash. + ['[duckies]'], + ['%bunnies'], + ['{{ puppies }}'], + // Disallowed characters in the authority (host name) that are valid + // elsewhere in the path. + ['(:;2&+h^'], + ['AKI@&hO@'], + ]; + } + /** * Tests the toUriString() method with route: URIs. *