diff --git a/core/lib/Drupal/Core/Field/WidgetBase.php b/core/lib/Drupal/Core/Field/WidgetBase.php index 5a4740b..575daf9 100644 --- a/core/lib/Drupal/Core/Field/WidgetBase.php +++ b/core/lib/Drupal/Core/Field/WidgetBase.php @@ -85,7 +85,7 @@ public function form(FieldItemListInterface $items, array &$form, FormStateInter $delta = isset($get_delta) ? $get_delta : 0; $element = array( '#title' => $this->fieldDefinition->getLabel(), - '#description' => FieldFilteredString::create(\Drupal::token()->replace($this->fieldDefinition->getDescription())->renderToHtml()), + '#description' => FieldFilteredString::create(\Drupal::token()->replace($this->fieldDefinition->getDescription())), ); $element = $this->formSingleElement($items, $delta, $element, $form, $form_state); @@ -164,9 +164,7 @@ protected function formMultipleElements(FieldItemListInterface $items, array &$f } $title = $this->fieldDefinition->getLabel(); - debug($this->fieldDefinition->getDescription()); - debug(\Drupal::token()->replace($this->fieldDefinition->getDescription())); - $description = FieldFilteredString::create(\Drupal::token()->replace($this->fieldDefinition->getDescription())->renderToHtml()); + $description = FieldFilteredString::create(\Drupal::token()->replace($this->fieldDefinition->getDescription())); $elements = array(); diff --git a/core/lib/Drupal/Core/Render/Renderer.php b/core/lib/Drupal/Core/Render/Renderer.php index 5b3bbe4..c2e03c9 100644 --- a/core/lib/Drupal/Core/Render/Renderer.php +++ b/core/lib/Drupal/Core/Render/Renderer.php @@ -9,7 +9,6 @@ use Drupal\Component\Utility\Html; use Drupal\Component\Utility\SafeMarkup; -use Drupal\Component\Utility\SafeStringInterface; use Drupal\Component\Utility\Xss; use Drupal\Core\Access\AccessResultInterface; use Drupal\Core\Cache\Cache; @@ -733,11 +732,7 @@ protected function ensureMarkupIsSafe(array $elements) { } if (!empty($elements['#plain_text'])) { - $string = $elements['#plain_text']; - if (!SafeMarkup::isSafe($string)) { - $string = Html::escape($string) - } - $elements['#markup'] = SafeString::create($string); + $elements['#markup'] = SafeString::create(Html::escape($elements['#plain_text'])); } elseif (!SafeMarkup::isSafe($elements['#markup'])) { // The default behaviour is to XSS filter using the admin tag list. diff --git a/core/lib/Drupal/Core/Utility/Token.php b/core/lib/Drupal/Core/Utility/Token.php index c7a67ca..d41102b 100644 --- a/core/lib/Drupal/Core/Utility/Token.php +++ b/core/lib/Drupal/Core/Utility/Token.php @@ -8,8 +8,7 @@ namespace Drupal\Core\Utility; use Drupal\Component\Utility\Html; -use Drupal\Component\Utility\PlainTextOutput; -use Drupal\Component\Utility\SafeMarkup; +use Drupal\Component\Utility\SafeStringInterface; use Drupal\Core\Cache\Cache; use Drupal\Core\Cache\CacheableDependencyInterface; use Drupal\Core\Cache\CacheBackendInterface; @@ -144,7 +143,9 @@ public function __construct(ModuleHandlerInterface $module_handler, CacheBackend * Replaces all tokens in a given string with appropriate values. * * @param string $text - * A plain text string potentially containing replaceable tokens. + * A HTML string containing replaceable tokens. The caller is responsible + * for calling \Drupal\Component\Utility\Html::escape() in case the expected + * input was plain text. * @param array $data * (optional) An array of keyed objects. For simple replacement scenarios * 'node', 'user', and others are common keys, with an accompanying node or @@ -178,15 +179,19 @@ public function __construct(ModuleHandlerInterface $module_handler, CacheBackend * method creates a local one, and applies the collected metadata to the * Renderer's currently active render context. * - * @return \Drupal\Core\Utility\TokenString - * The token result string which is an instance of SafeStringInterface, but - * it casts to plain text by default. For usages of this API as HTML, read - * more on \Drupal\Core\Utility\TokenString itself. + * @return string + * The token result is the entered HTML text with tokens replaced. The + * caller is responsible for choosing the right escaping / sanitization. If + * its intended to be used as plain text, the usage of + * PlainTextOutput::renderFromHtml() is suggested. If the result is just + * printed as part of a template relying on twig autoescaping is possible, + * otherwise for example the result can be put into #markup, in which case + * it would be xss admin filtered. */ public function replace($text, array $data = array(), array $options = array(), BubbleableMetadata $bubbleable_metadata = NULL) { $text_tokens = $this->scan($text); if (empty($text_tokens)) { - return TokenString::create($text); + return $text; } $bubbleable_metadata_is_passed_in = (bool) $bubbleable_metadata; @@ -211,7 +216,7 @@ public function replace($text, array $data = array(), array $options = array(), $values = []; foreach ($replacements as $token => $value) { $tokens[] = $token; - $values[] = SafeMarkup::isSafe($value) ? $value : Html::escape($value); + $values[] = $value instanceof SafeStringInterface ? $value : Html::escape($value); } // If a local $bubbleable_metadata object was created, apply the metadata @@ -222,7 +227,7 @@ public function replace($text, array $data = array(), array $options = array(), $this->renderer->render($build); } - return TokenString::create(str_replace($tokens, $values, $text)); + return str_replace($tokens, $values, $text); } /** diff --git a/core/lib/Drupal/Core/Utility/TokenString.php b/core/lib/Drupal/Core/Utility/TokenString.php deleted file mode 100644 index bb81867..0000000 --- a/core/lib/Drupal/Core/Utility/TokenString.php +++ /dev/null @@ -1,58 +0,0 @@ -string = $string; - return $safe_string; - } - - /** - * {@inheritdoc} - */ - public function __toString() { - return Html::escape($this->string); - } - - /** - * Returns the XSS filtered raw string. - * - * Use this method just in case you output a full HTML fragment. Do not pass - * this - * - * @param array $html_tags - * An array of HTML tags. - * - * @return string - */ - public function renderToHtml(array $html_tags = NULL) { - return Xss::filter($this->string, $html_tags); - } - -} diff --git a/core/modules/action/src/Plugin/Action/EmailAction.php b/core/modules/action/src/Plugin/Action/EmailAction.php index 252428c..4c68e1d 100644 --- a/core/modules/action/src/Plugin/Action/EmailAction.php +++ b/core/modules/action/src/Plugin/Action/EmailAction.php @@ -7,6 +7,7 @@ namespace Drupal\action\Plugin\Action; +use Drupal\Component\Utility\PlainTextOutput; use Drupal\Core\Access\AccessResult; use Drupal\Core\Action\ConfigurableActionBase; use Drupal\Core\Entity\EntityManagerInterface; @@ -127,7 +128,7 @@ public function execute($entity = NULL) { $this->configuration['node'] = $entity; } - $recipient = $this->token->replace($this->configuration['recipient'], $this->configuration); + $recipient = PlainTextOutput::renderFromHtml($this->token->replace($this->configuration['recipient'], $this->configuration)); // If the recipient is a registered user with a language preference, use // the recipient's preferred language. Otherwise, use the system default diff --git a/core/modules/comment/src/Tests/CommentTokenReplaceTest.php b/core/modules/comment/src/Tests/CommentTokenReplaceTest.php index 9d530ba..85f09dc 100644 --- a/core/modules/comment/src/Tests/CommentTokenReplaceTest.php +++ b/core/modules/comment/src/Tests/CommentTokenReplaceTest.php @@ -117,7 +117,7 @@ function testCommentTokenReplacement() { foreach ($tests as $input => $expected) { $bubbleable_metadata = new BubbleableMetadata(); $output = $token_service->replace($input, array('comment' => $comment), array('langcode' => $language_interface->getId()), $bubbleable_metadata); - $this->assertEqual($output->renderToHtml(), $expected, format_string('Comment token %token replaced.', array('%token' => $input))); + $this->assertEqual($output, $expected, format_string('Comment token %token replaced.', array('%token' => $input))); $this->assertEqual($bubbleable_metadata, $metadata_tests[$input]); } @@ -126,7 +126,7 @@ function testCommentTokenReplacement() { $comment->setOwnerId(0)->setAuthorName($author_name); $input = '[comment:author]'; $output = $token_service->replace($input, array('comment' => $comment), array('langcode' => $language_interface->getId())); - $this->assertEqual($output->renderToHtml(), Html::escape($author_name), format_string('Comment author token %token replaced.', array('%token' => $input))); + $this->assertEqual($output, Html::escape($author_name), format_string('Comment author token %token replaced.', array('%token' => $input))); // Load node so comment_count gets computed. $node = Node::load($node->id()); @@ -138,7 +138,7 @@ function testCommentTokenReplacement() { foreach ($tests as $input => $expected) { $output = $token_service->replace($input, array('entity' => $node, 'node' => $node), array('langcode' => $language_interface->getId())); - $this->assertEqual($output->renderToHtml(), $expected, format_string('Node comment token %token replaced.', array('%token' => $input))); + $this->assertEqual($output, $expected, format_string('Node comment token %token replaced.', array('%token' => $input))); } } diff --git a/core/modules/node/src/Tests/NodeTokenReplaceTest.php b/core/modules/node/src/Tests/NodeTokenReplaceTest.php index 78d6531..d8c9ddf 100644 --- a/core/modules/node/src/Tests/NodeTokenReplaceTest.php +++ b/core/modules/node/src/Tests/NodeTokenReplaceTest.php @@ -105,7 +105,7 @@ function testNodeTokenReplacement() { foreach ($tests as $input => $expected) { $bubbleable_metadata = new BubbleableMetadata(); $output = $this->tokenService->replace($input, array('node' => $node), array('langcode' => $this->interfaceLanguage->getId()), $bubbleable_metadata); - $this->assertEqual($output->renderToHtml(), $expected, format_string('Node token %token replaced.', array('%token' => $input))); + $this->assertEqual($output, $expected, format_string('Node token %token replaced.', array('%token' => $input))); $this->assertEqual($bubbleable_metadata, $metadata_tests[$input]); } @@ -127,7 +127,7 @@ function testNodeTokenReplacement() { foreach ($tests as $input => $expected) { $output = $this->tokenService->replace($input, array('node' => $node), array('language' => $this->interfaceLanguage)); - $this->assertEqual($output->renderToHtml(), $expected, format_string('Node token %token replaced for node without a summary.', array('%token' => $input))); + $this->assertEqual($output, $expected, format_string('Node token %token replaced for node without a summary.', array('%token' => $input))); } } diff --git a/core/modules/system/src/Tests/System/TokenReplaceUnitTest.php b/core/modules/system/src/Tests/System/TokenReplaceUnitTest.php index 0af566a..8330e67 100644 --- a/core/modules/system/src/Tests/System/TokenReplaceUnitTest.php +++ b/core/modules/system/src/Tests/System/TokenReplaceUnitTest.php @@ -130,7 +130,7 @@ public function testSystemSiteTokenReplacement() { foreach ($tests as $input => $expected) { $bubbleable_metadata = new BubbleableMetadata(); $output = $this->tokenService->replace($input, array(), array('langcode' => $this->interfaceLanguage->getId()), $bubbleable_metadata); - $this->assertEqual($output->renderToHtml(), $expected, format_string('System site information token %token replaced.', array('%token' => $input))); + $this->assertEqual($output, $expected, format_string('System site information token %token replaced.', array('%token' => $input))); $this->assertEqual($bubbleable_metadata, $metadata_tests[$input]); } } diff --git a/core/modules/system/system.module b/core/modules/system/system.module index a8477d3..e21c2c8 100644 --- a/core/modules/system/system.module +++ b/core/modules/system/system.module @@ -5,6 +5,7 @@ * Configuration system that lets administrators modify the workings of the site. */ +use Drupal\Component\Utility\PlainTextOutput; use Drupal\Component\Utility\UrlHelper; use Drupal\Core\Asset\AttachedAssetsInterface; use Drupal\Core\Cache\Cache; @@ -1290,7 +1291,7 @@ function system_mail($key, &$message, $params) { $context = $params['context']; - $subject = $token_service->replace($context['subject'], $context); + $subject = PlainTextOutput::renderFromHtml($token_service->replace($context['subject'], $context)); $body = $token_service->replace($context['message'], $context); $message['subject'] .= str_replace(array("\r", "\n"), '', $subject); diff --git a/core/modules/taxonomy/src/Tests/TokenReplaceTest.php b/core/modules/taxonomy/src/Tests/TokenReplaceTest.php index 709fb43..c1b0795 100644 --- a/core/modules/taxonomy/src/Tests/TokenReplaceTest.php +++ b/core/modules/taxonomy/src/Tests/TokenReplaceTest.php @@ -110,7 +110,7 @@ function testTaxonomyTokenReplacement() { foreach ($tests as $input => $expected) { $bubbleable_metadata = new BubbleableMetadata(); $output = $token_service->replace($input, array('term' => $term1), array('langcode' => $language_interface->getId()), $bubbleable_metadata); - $this->assertEqual($output->renderToHtml(), $expected, format_string('Sanitized taxonomy term token %token replaced.', array('%token' => $input))); + $this->assertEqual($output, $expected, format_string('Sanitized taxonomy term token %token replaced.', array('%token' => $input))); $this->assertEqual($bubbleable_metadata, $metadata_tests[$input]); } @@ -131,7 +131,7 @@ function testTaxonomyTokenReplacement() { foreach ($tests as $input => $expected) { $output = $token_service->replace($input, array('term' => $term2), array('langcode' => $language_interface->getId())); - $this->assertEqual($output->renderToHtml(), $expected, format_string('Sanitized taxonomy term token %token replaced.', array('%token' => $input))); + $this->assertEqual($output, $expected, format_string('Sanitized taxonomy term token %token replaced.', array('%token' => $input))); } // Generate and test sanitized tokens. @@ -147,7 +147,7 @@ function testTaxonomyTokenReplacement() { foreach ($tests as $input => $expected) { $output = $token_service->replace($input, array('vocabulary' => $this->vocabulary), array('langcode' => $language_interface->getId())); - $this->assertEqual($output->renderToHtml(), $expected, format_string('Sanitized taxonomy vocabulary token %token replaced.', array('%token' => $input))); + $this->assertEqual($output, $expected, format_string('Sanitized taxonomy vocabulary token %token replaced.', array('%token' => $input))); } } } diff --git a/core/modules/tour/src/Plugin/tour/tip/TipPluginText.php b/core/modules/tour/src/Plugin/tour/tip/TipPluginText.php index d3f8fb6..6bb2338 100644 --- a/core/modules/tour/src/Plugin/tour/tip/TipPluginText.php +++ b/core/modules/tour/src/Plugin/tour/tip/TipPluginText.php @@ -122,7 +122,7 @@ public function getAttributes() { public function getOutput() { $output = '

' . Html::escape($this->getLabel()) . '

'; $tags = Xss::getHtmlTagList(); - $output .= '

' . $this->token->replace($this->getBody())->renderToHtml() . '

'; + $output .= '

' . $this->token->replace($this->getBody()) . '

'; return array('#markup' => $output); } diff --git a/core/modules/user/src/Tests/UserTokenReplaceTest.php b/core/modules/user/src/Tests/UserTokenReplaceTest.php index 1840035..142c6fe 100644 --- a/core/modules/user/src/Tests/UserTokenReplaceTest.php +++ b/core/modules/user/src/Tests/UserTokenReplaceTest.php @@ -108,7 +108,7 @@ function testUserTokenReplacement() { foreach ($tests as $input => $expected) { $bubbleable_metadata = new BubbleableMetadata(); $output = $token_service->replace($input, array('user' => $account), array('langcode' => $language_interface->getId()), $bubbleable_metadata); - $this->assertEqual($output->renderToHtml(), $expected, format_string('User token %token replaced.', array('%token' => $input))); + $this->assertEqual($output, $expected, format_string('User token %token replaced.', array('%token' => $input))); $this->assertEqual($bubbleable_metadata, $metadata_tests[$input]); } @@ -128,7 +128,7 @@ function testUserTokenReplacement() { foreach ($tests as $input => $expected) { $bubbleable_metadata = new BubbleableMetadata(); $output = $token_service->replace($input, array('user' => $anonymous_user), array('langcode' => $language_interface->getId()), $bubbleable_metadata); - $this->assertEqual($output->renderToHtml(), $expected, format_string('Sanitized user token %token replaced.', array('%token' => $input))); + $this->assertEqual($output, $expected, format_string('Sanitized user token %token replaced.', array('%token' => $input))); $this->assertEqual($bubbleable_metadata, $metadata_tests[$input]); } @@ -141,7 +141,7 @@ function testUserTokenReplacement() { $link = \Drupal::url('user.page', [], array('absolute' => TRUE)); foreach ($tests as $input => $expected) { $output = $token_service->replace($input, array('user' => $account), array('langcode' => $language_interface->getId(), 'callback' => 'user_mail_tokens', 'clear' => TRUE)); - $this->assertTrue(strpos($output->renderToHtml(), $link) === 0, 'Generated URL is in interface language.'); + $this->assertTrue(strpos($output, $link) === 0, 'Generated URL is in interface language.'); } // Generate tokens with the user's preferred language. @@ -150,7 +150,7 @@ function testUserTokenReplacement() { $link = \Drupal::url('user.page', [], array('language' => \Drupal::languageManager()->getLanguage($account->getPreferredLangcode()), 'absolute' => TRUE)); foreach ($tests as $input => $expected) { $output = $token_service->replace($input, array('user' => $account), array('callback' => 'user_mail_tokens', 'clear' => TRUE)); - $this->assertTrue(strpos($output->renderToHtml(), $link) === 0, "Generated URL is in the user's preferred language."); + $this->assertTrue(strpos($output, $link) === 0, "Generated URL is in the user's preferred language."); } // Generate tokens with one specific language. @@ -158,7 +158,7 @@ function testUserTokenReplacement() { foreach ($tests as $input => $expected) { foreach (array($user1, $user2) as $account) { $output = $token_service->replace($input, array('user' => $account), array('langcode' => 'de', 'callback' => 'user_mail_tokens', 'clear' => TRUE)); - $this->assertTrue(strpos($output->renderToHtml(), $link) === 0, "Generated URL in in the requested language."); + $this->assertTrue(strpos($output, $link) === 0, "Generated URL in in the requested language."); } } @@ -168,6 +168,6 @@ function testUserTokenReplacement() { $input = '[user:display-name] [current-user:display-name]'; $expected = "{$user1->id()} {$user2->id()}"; $output = $token_service->replace($input, ['user' => $user1]); - $this->assertEqual($output->renderToHtml(), $expected, SafeMarkup::format('User token %token does not escape safe markup.', ['%token' => 'display-name'])); + $this->assertEqual($output, $expected, SafeMarkup::format('User token %token does not escape safe markup.', ['%token' => 'display-name'])); } } diff --git a/core/modules/user/user.module b/core/modules/user/user.module index 0247597..ba4e6bd 100644 --- a/core/modules/user/user.module +++ b/core/modules/user/user.module @@ -1,6 +1,7 @@ $langcode, 'callback' => 'user_mail_tokens', 'clear' => TRUE); - $message['subject'] .= $token_service->replace($mail_config->get($key . '.subject'), $variables, $token_options); + $message['subject'] .= PlainTextOutput::renderFromHtml($token_service->replace($mail_config->get($key . '.subject'), $variables, $token_options)); $message['body'][] = $token_service->replace($mail_config->get($key . '.body'), $variables, $token_options); $language_manager->setConfigOverrideLanguage($original_language); diff --git a/core/tests/Drupal/Tests/Core/Utility/TokenStringTest.php b/core/tests/Drupal/Tests/Core/Utility/TokenStringTest.php deleted file mode 100644 index c2c7b41..0000000 --- a/core/tests/Drupal/Tests/Core/Utility/TokenStringTest.php +++ /dev/null @@ -1,88 +0,0 @@ -assertEquals($expected, (string) $token_string); - } - - /** - * @covers ::create - */ - public function testCreateWithEmptyString() { - $token_string = TokenString::create(''); - $this->assertInstanceOf(TokenString::class, $token_string); - $this->assertEquals('', $token_string->__toString()); - } - - /** - * @covers ::create - */ - public function testCreateWithSafeString() { - $string = 'foo'; - $safe_string = $this->prophesize(SafeStringInterface::class); - $safe_string->__toString()->willReturn($string); - $safe_string = $safe_string->reveal(); - - $token_string = TokenString::create($safe_string); - $this->assertInstanceOf(TokenString::class, $token_string); - $this->assertEquals(Html::escape($string), $token_string->__toString()); - $this->assertEquals($string, $token_string->renderToHtml()); - } - - public function providerTestToString() { - $data = []; - $data['simple-text'] = ['Foo bar', 'Foo bar']; - $string = '

This is html

'; - $data['html-text'] = [Html::escape($string), $string]; - $string = ''; - $data['xss-test'] = [Html::escape($string), $string]; - - return $data; - } - - /** - * @covers ::renderToHtml - * @dataProvider providerTestRenderToHtml - */ - public function testRenderToHtml($expected, $input) { - /** @var \Drupal\Core\Utility\TokenString $token_string */ - $token_string = TokenString::create($input); - $this->assertEquals($expected, $token_string->renderToHtml()); - } - - public function providerTestRenderToHtml() { - $data = []; - $data['simple-text'] = ['Foo bar', 'Foo bar']; - $string = 'This is html'; - $data['html-text'] = [$string, $string]; - $string = ''; - $data['xss-test'] = ['alert(123)', $string]; - $data['xss-test2'] = ['Giraffe', 'Giraffe']; - - return $data; - } - -} diff --git a/core/tests/Drupal/Tests/Core/Utility/TokenTest.php b/core/tests/Drupal/Tests/Core/Utility/TokenTest.php index a083471..7fd8324 100644 --- a/core/tests/Drupal/Tests/Core/Utility/TokenTest.php +++ b/core/tests/Drupal/Tests/Core/Utility/TokenTest.php @@ -278,7 +278,9 @@ public function testReplaceEscaping($string, array $tokens, $expected) { return $args[2]['tokens']; }); - $this->assertEquals($expected, $this->token->replace($string, ['tokens' => $tokens])); + $result = $this->token->replace($string, ['tokens' => $tokens]); + $this->assertInternalType('string', $result); + $this->assertEquals($expected, $result); } public function providerTestReplaceEscaping() { @@ -286,74 +288,15 @@ public function providerTestReplaceEscaping() { // No tokens. The first argument to Token::replace() should not be escaped. $data['no-tokens'] = ['muh', [], 'muh']; - $data['html-in-string'] = ['

Giraffe

', [], TokenString::create('

Giraffe

')]; - $data['html-in-string-quote'] = ['

Giraffe"

', [], TokenString::create('

Giraffe"

')]; - - // Token escaping. - $data['simple-placeholder'] = [ - '[token:meh]', - ['[token:meh]' => 'muh'], - TokenString::create('muh') - ]; - $data['simple-placeholder-with-xss'] = [ - '

[token:meh]

', - ['[token:meh]' => ""], - TokenString::create('

' . Html::escape('') . '

') - ]; + $data['html-in-string'] = ['

Giraffe

', [], '

Giraffe

']; + $data['html-in-string-quote'] = ['

Giraffe"

', [], '

Giraffe"

']; + + $data['simple-placeholder-with-plain-text'] = ['

[token:meh]

', ['[token:meh]' => 'Giraffe"'], '

' . Html::escape('Giraffe"') . '

']; $data['simple-placeholder-with-safe-html'] = [ '

[token:meh]

', - ['[token:meh]' => SafeString::create("Emphasized")], - TokenString::create('

Emphasized

') - ]; - - return $data; - } - - /** - * Tests that Token::replace() does not mark its output as safe. - * - * There are many ways for XSS to be injected into Token::replace(). This - * tests some of them and ensures that the result is therefore not a - * SafeStringInterface object. - * - * @covers ::replace - * @dataProvider providerTestReplaceUnsafe - */ - public function testReplaceUnsafe($string, array $tokens, $expected, $expected_raw) { - $this->moduleHandler->expects($this->any()) - ->method('invokeAll') - ->willReturnCallback(function ($type, $args) { - return $args[2]['tokens']; - }); - - $output = $this->token->replace($string, ['tokens' => $tokens]); - $this->assertInstanceOf(TokenString::class, $output); - $this->assertEquals($expected, $output); - $this->assertEquals($output->renderToHtml(), $expected_raw); - } - - public function providerTestReplaceUnsafe() { - $data = []; - - // XSS can be injected directly into the first argument. - $data['xss-via-string'] = ['', [], TokenString::create(''), 'alert(1)']; - - // XSS can be injected via "safe" markup injected into an attribute value. - $data['xss-via-attribute'] = [ - 'Giraffe', - ['[token:meh]' => SafeString::create(Xss::filter('" onclick="alert(1)'))], - TokenString::create('Giraffe'), - 'Giraffe', - ]; - - // XSS can be injected via a URL protocol (especially via tokens that don't - // filter for URL, such as [comment:title]). - $data['xss-via-protocol'] = [ - 'Giraffe', - ['[token:meh]' => "javascript:alert(1)"], - TokenString::create('Giraffe'), - 'Giraffe', + ['[token:meh]' => SafeString::create('Emphasized')], + '

Emphasized

', ]; return $data;