diff --git a/core/lib/Drupal/Core/Utility/LinkGenerator.php b/core/lib/Drupal/Core/Utility/LinkGenerator.php index ab2d232..78a4af1 100644 --- a/core/lib/Drupal/Core/Utility/LinkGenerator.php +++ b/core/lib/Drupal/Core/Utility/LinkGenerator.php @@ -125,26 +125,26 @@ public function generate($text, Url $url, $collect_cacheability_metadata = FALSE // Allow other modules to modify the structure of the link. $this->moduleHandler->alter('link', $variables); - // Move attributes out of options. generateFromRoute(() doesn't need them. - $attributes = new Attribute($variables['options']['attributes']); + // Move attributes out of options. generateFromRoute(() doesn't need them, + // including a placeholder for the href. + $attributes = array('href' => '') + $variables['options']['attributes']; unset($variables['options']['attributes']); $url->setOptions($variables['options']); - // The result of the url generator is a plain-text URL. Because we are using - // it here in an HTML argument context, we need to encode it properly. + // The result of the url generator is a plain-text URL. We format it with + //SafeMarkup::format() because we are using it an HTML argument context. if (!$collect_cacheability_metadata) { - $url = SafeMarkup::checkPlain($url->toString($collect_cacheability_metadata)); + $url_string = $url->toString($collect_cacheability_metadata); } else { $generated_url = $url->toString($collect_cacheability_metadata); - $url = SafeMarkup::checkPlain($generated_url->getGeneratedUrl()); + $url_string = $generated_url->getGeneratedUrl(); $generated_link = GeneratedLink::createFromObject($generated_url); } + $attributes['href'] = $url_string; - // Make sure the link text is sanitized. - $safe_text = SafeMarkup::escape($variables['text']); + $result = SafeMarkup::format('@text', array('@attributes' => new Attribute($attributes), '@text' => $variables['text'])); - $result = SafeMarkup::set('' . $safe_text . ''); return $collect_cacheability_metadata ? $generated_link->setGeneratedLink($result) : $result; } diff --git a/core/tests/Drupal/Tests/Core/Utility/LinkGeneratorTest.php b/core/tests/Drupal/Tests/Core/Utility/LinkGeneratorTest.php index 5bf2b94..d9130e0 100644 --- a/core/tests/Drupal/Tests/Core/Utility/LinkGeneratorTest.php +++ b/core/tests/Drupal/Tests/Core/Utility/LinkGeneratorTest.php @@ -14,6 +14,7 @@ use Drupal\Core\Url; use Drupal\Core\Utility\LinkGenerator; use Drupal\Tests\UnitTestCase; +use Drupal\Core\DependencyInjection\ContainerBuilder; /** * @coversDefaultClass \Drupal\Core\Utility\LinkGenerator @@ -181,6 +182,37 @@ public function testGenerateExternal() { } /** + * Tests the generate() method with a url containing double quotes. + * + * @covers ::generate + */ + public function testGenerateUrlWithQuotes() { + $this->urlAssembler->expects($this->once()) + ->method('assemble') + ->with('base:example', array('query' => array('foo' => '"bar"', 'zoo' => 'baz')) + $this->defaultOptions) + ->will($this->returnValue('/example?foo=%22bar%22&zoo=baz')); + + $path_validator = $this->getMock('Drupal\Core\Path\PathValidatorInterface'); + $container_builder = new ContainerBuilder(); + $container_builder->set('path.validator', $path_validator); + \Drupal::setContainer($container_builder); + + $path = '/example?foo="bar"&zoo=baz'; + $url = Url::fromUserInput($path); + $url->setUrlGenerator($this->urlGenerator); + $url->setUnroutedUrlAssembler($this->urlAssembler); + + $result = $this->linkGenerator->generate('Drupal', $url); + + $this->assertLink(array( + 'attributes' => array( + 'href' => '/example?foo=%22bar%22&zoo=baz', + ), + 'content' => 'Drupal', + ), $result, 1); + } + + /** * Tests the link method with additional attributes. * * @see \Drupal\Core\Utility\LinkGenerator::generate() @@ -335,7 +367,7 @@ public function testGenerateWithHtml() { // Test that safe HTML is output inside the anchor tag unescaped. $url = new Url('test_route_5', array()); $url->setUrlGenerator($this->urlGenerator); - $result = $this->linkGenerator->generate(SafeMarkup::set('HTML output'), $url); + $result = $this->linkGenerator->generate(SafeMarkup::checkAdminXss('HTML output'), $url); $this->assertLink(array( 'attributes' => array('href' => '/test-route-5'), 'child' => array( diff --git a/core/tests/Drupal/Tests/Core/Utility/UnroutedUrlAssemblerTest.php b/core/tests/Drupal/Tests/Core/Utility/UnroutedUrlAssemblerTest.php index 0433561..98fab07 100644 --- a/core/tests/Drupal/Tests/Core/Utility/UnroutedUrlAssemblerTest.php +++ b/core/tests/Drupal/Tests/Core/Utility/UnroutedUrlAssemblerTest.php @@ -125,6 +125,8 @@ public function providerTestAssembleWithLocalUri() { return [ ['base:example', [], FALSE, '/example'], ['base:example', ['query' => ['foo' => 'bar']], FALSE, '/example?foo=bar'], + ['base:example', ['query' => ['foo' => '"bar"']], FALSE, '/example?foo=%22bar%22'], + ['base:example', ['query' => ['foo' => '"bar"', 'zoo' => 'baz']], FALSE, '/example?foo=%22bar%22&zoo=baz'], ['base:example', ['fragment' => 'example', ], FALSE, '/example#example'], ['base:example', [], TRUE, '/subdir/example'], ['base:example', ['query' => ['foo' => 'bar']], TRUE, '/subdir/example?foo=bar'],