/** * Tests the caption filter. */ function testCaptionFilter() { /** @var \Drupal\Core\Render\RendererInterface $renderer */ $renderer = \Drupal::service('renderer'); $filter = $this->filters['filter_caption']; $test = function ($input) use($filter, $renderer) { return $renderer->executeInRenderContext(new RenderContext(), function () use($input, $filter) { return $filter->process($input, 'und'); }); }; $attached_library = array('library' => array('filter/caption')); // No data-caption attribute. $input = '<img src="llama.jpg" />'; $expected = $input; $this->assertIdentical($expected, $test($input)->getProcessedText()); // Data-caption attribute. $input = '<img src="llama.jpg" data-caption="Loquacious llama!" />'; $expected = '<figure role="group"><img src="llama.jpg" /><figcaption>Loquacious llama!</figcaption></figure>'; $output = $test($input); $this->assertIdentical($expected, $output->getProcessedText()); $this->assertIdentical($attached_library, $output->getAttachments()); // Empty data-caption attribute. $input = '<img src="llama.jpg" data-caption="" />'; $expected = '<img src="llama.jpg" />'; $this->assertIdentical($expected, $test($input)->getProcessedText()); // HTML entities in the caption. $input = '<img src="llama.jpg" data-caption="“Loquacious llama!”" />'; $expected = '<figure role="group"><img src="llama.jpg" /><figcaption>“Loquacious llama!”</figcaption></figure>'; $output = $test($input); $this->assertIdentical($expected, $output->getProcessedText()); $this->assertIdentical($attached_library, $output->getAttachments()); // HTML encoded as HTML entities in data-caption attribute. $input = '<img src="llama.jpg" data-caption="<em>Loquacious llama!</em>" />'; $expected = '<figure role="group"><img src="llama.jpg" /><figcaption><em>Loquacious llama!</em></figcaption></figure>'; $output = $test($input); $this->assertIdentical($expected, $output->getProcessedText()); $this->assertIdentical($attached_library, $output->getAttachments()); // HTML (not encoded as HTML entities) in data-caption attribute, which is // not allowed by the HTML spec, but may happen when people manually write // HTML, so we explicitly support it. $input = '<img src="llama.jpg" data-caption="<em>Loquacious llama!</em>" />'; $expected = '<figure role="group"><img src="llama.jpg" /><figcaption><em>Loquacious llama!</em></figcaption></figure>'; $output = $test($input); $this->assertIdentical($expected, $output->getProcessedText()); $this->assertIdentical($attached_library, $output->getAttachments()); // Security test: attempt an XSS. $input = '<img src="llama.jpg" data-caption="<script>alert(\'Loquacious llama!\')</script>" />'; $expected = '<figure role="group"><img src="llama.jpg" /><figcaption>alert(\'Loquacious llama!\')</figcaption></figure>'; $output = $test($input); $this->assertIdentical($expected, $output->getProcessedText()); $this->assertIdentical($attached_library, $output->getAttachments()); // Ensure the filter also works with uncommon yet valid attribute quoting. $input = '<img src=llama.jpg data-caption=\'Loquacious llama!\' />'; $expected = '<figure role="group"><img src="llama.jpg" /><figcaption>Loquacious llama!</figcaption></figure>'; $output = $test($input); $this->assertIdentical($expected, $output->getProcessedText()); $this->assertIdentical($attached_library, $output->getAttachments()); // Finally, ensure that this also works on any other tag. $input = '<video src="llama.jpg" data-caption="Loquacious llama!" />'; $expected = '<figure role="group"><video src="llama.jpg"></video><figcaption>Loquacious llama!</figcaption></figure>'; $output = $test($input); $this->assertIdentical($expected, $output->getProcessedText()); $this->assertIdentical($attached_library, $output->getAttachments()); $input = '<foobar data-caption="Loquacious llama!">baz</foobar>'; $expected = '<figure role="group"><foobar>baz</foobar><figcaption>Loquacious llama!</figcaption></figure>'; $output = $test($input); $this->assertIdentical($expected, $output->getProcessedText()); $this->assertIdentical($attached_library, $output->getAttachments()); // Ensure the caption filter works for linked images. $input = '<a href="http://example.com/llamas/are/awesome/but/kittens/are/cool/too"><img src="llama.jpg" data-caption="Loquacious llama!" /></a>'; $expected = '<figure role="group"><a href="http://example.com/llamas/are/awesome/but/kittens/are/cool/too"><img src="llama.jpg" /></a>' . "\n" . '<figcaption>Loquacious llama!</figcaption></figure>'; $output = $test($input); $this->assertIdentical($expected, $output->getProcessedText()); $this->assertIdentical($attached_library, $output->getAttachments()); // So far we've tested that the caption filter works correctly. But we also // want to make sure that it works well in tandem with the "Limit allowed // HTML tags" filter, which it is typically used with. $html_filter = $this->filters['filter_html']; $html_filter->setConfiguration(array('settings' => array('allowed_html' => '<img src data-align data-caption>', 'filter_html_help' => 1, 'filter_html_nofollow' => 0))); $test_with_html_filter = function ($input) use($filter, $html_filter, $renderer) { return $renderer->executeInRenderContext(new RenderContext(), function () use($input, $filter, $html_filter) { // 1. Apply HTML filter's processing step. $output = $html_filter->process($input, 'und'); // 2. Apply caption filter's processing step. $output = $filter->process($output, 'und'); return $output->getProcessedText(); }); }; // Editor XSS filter. $test_editor_xss_filter = function ($input) { $dummy_filter_format = FilterFormat::create(); return Standard::filterXss($input, $dummy_filter_format); }; // All the tricky cases encountered at https://www.drupal.org/node/2105841. // A plain URL preceded by text. $input = '<img data-caption="See https://www.drupal.org" src="llama.jpg" />'; $expected = '<figure role="group"><img src="llama.jpg" /><figcaption>See https://www.drupal.org</figcaption></figure>'; $this->assertIdentical($expected, $test_with_html_filter($input)); $this->assertIdentical($input, $test_editor_xss_filter($input)); // An anchor. $input = '<img data-caption="This is a <a href="https://www.drupal.org">quick</a> test…" src="llama.jpg" />'; $expected = '<figure role="group"><img src="llama.jpg" /><figcaption>This is a <a href="https://www.drupal.org">quick</a> test…</figcaption></figure>'; $this->assertIdentical($expected, $test_with_html_filter($input)); $this->assertIdentical($input, $test_editor_xss_filter($input)); // A plain URL surrounded by parentheses. $input = '<img data-caption="(https://www.drupal.org)" src="llama.jpg" />'; $expected = '<figure role="group"><img src="llama.jpg" /><figcaption>(https://www.drupal.org)</figcaption></figure>'; $this->assertIdentical($expected, $test_with_html_filter($input)); $this->assertIdentical($input, $test_editor_xss_filter($input)); // A source being credited. $input = '<img data-caption="Source: Wikipedia" src="llama.jpg" />'; $expected = '<figure role="group"><img src="llama.jpg" /><figcaption>Source: Wikipedia</figcaption></figure>'; $this->assertIdentical($expected, $test_with_html_filter($input)); $this->assertIdentical($input, $test_editor_xss_filter($input)); // A source being credited, without a space after the colon. $input = '<img data-caption="Source:Wikipedia" src="llama.jpg" />'; $expected = '<figure role="group"><img src="llama.jpg" /><figcaption>Source:Wikipedia</figcaption></figure>'; $this->assertIdentical($expected, $test_with_html_filter($input)); $this->assertIdentical($input, $test_editor_xss_filter($input)); // A pretty crazy edge case where we have two colons. $input = '<img data-caption="Interesting (Scope resolution operator ::)" src="llama.jpg" />'; $expected = '<figure role="group"><img src="llama.jpg" /><figcaption>Interesting (Scope resolution operator ::)</figcaption></figure>'; $this->assertIdentical($expected, $test_with_html_filter($input)); $this->assertIdentical($input, $test_editor_xss_filter($input)); // An evil anchor (to ensure XSS filtering is applied to the caption also). $input = '<img data-caption="This is an <a href="javascript:alert();">evil</a> test…" src="llama.jpg" />'; $expected = '<figure role="group"><img src="llama.jpg" /><figcaption>This is an <a href="alert();">evil</a> test…</figcaption></figure>'; $this->assertIdentical($expected, $test_with_html_filter($input)); $expected_xss_filtered = '<img data-caption="This is an <a href="alert();">evil</a> test…" src="llama.jpg" />'; $this->assertIdentical($expected_xss_filtered, $test_editor_xss_filter($input)); }
/** * Tests removing disallowed tags and XSS prevention. * * \Drupal\Component\Utility\Xss::filter() has the ability to run in blacklist * mode, in which it still applies the exact same filtering, with one * exception: it no longer works with a list of allowed tags, but with a list * of disallowed tags. * * @param string $value * The value to filter. * @param string $expected * The string that is expected to be missing. * @param string $message * The assertion message to display upon failure. * @param array $disallowed_tags * (optional) The disallowed HTML tags to be passed to \Drupal\Component\Utility\Xss::filter(). * * @dataProvider providerTestBlackListMode */ public function testBlacklistMode($value, $expected, $message, array $disallowed_tags) { $value = Standard::filter($value, $disallowed_tags); $this->assertSame($expected, $value, $message); }