Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 069a70f

Browse files
[HtmlSanitizer] Sanitize URL attributes on <object>, <applet>, <iframe>, <img>, and the URL inside <meta http-equiv="refresh"> content
1 parent e560a58 commit 069a70f

4 files changed

Lines changed: 131 additions & 1 deletion

File tree

src/Symfony/Component/HtmlSanitizer/HtmlSanitizerConfig.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,7 @@ public function __construct()
9696
{
9797
$this->attributeSanitizers = [
9898
new Visitor\AttributeSanitizer\UrlAttributeSanitizer(),
99+
new Visitor\AttributeSanitizer\MetaRefreshAttributeSanitizer(),
99100
];
100101
}
101102

src/Symfony/Component/HtmlSanitizer/Tests/HtmlSanitizerCustomTest.php

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -503,6 +503,79 @@ public function testCiteAttributeIsSanitized()
503503
);
504504
}
505505

506+
/**
507+
* @dataProvider provideUrlAttributesSanitizedAcrossEmbeddingElements
508+
*/
509+
public function testUrlAttributesAreSanitizedAcrossEmbeddingElements(string $element, string $attribute, string $input, string $expected)
510+
{
511+
$config = (new HtmlSanitizerConfig())
512+
->allowElement($element, [$attribute])
513+
;
514+
515+
$this->assertSame($expected, $this->sanitize($config, $input));
516+
}
517+
518+
public static function provideUrlAttributesSanitizedAcrossEmbeddingElements(): iterable
519+
{
520+
yield 'object data javascript:' => [
521+
'object', 'data',
522+
'<object data="javascript:alert(1)">x</object>',
523+
'<object>x</object>',
524+
];
525+
yield 'object data http kept' => [
526+
'object', 'data',
527+
'<object data="https://symfony.com/x">x</object>',
528+
'<object data="https://symfony.com/x">x</object>',
529+
];
530+
yield 'applet codebase javascript:' => [
531+
'applet', 'codebase',
532+
'<applet codebase="javascript:alert(1)">x</applet>',
533+
'<applet>x</applet>',
534+
];
535+
yield 'applet archive javascript:' => [
536+
'applet', 'archive',
537+
'<applet archive="javascript:alert(1)">x</applet>',
538+
'<applet>x</applet>',
539+
];
540+
yield 'object codebase javascript:' => [
541+
'object', 'codebase',
542+
'<object codebase="javascript:alert(1)">x</object>',
543+
'<object>x</object>',
544+
];
545+
yield 'iframe longdesc javascript:' => [
546+
'iframe', 'longdesc',
547+
'<iframe longdesc="javascript:alert(1)">x</iframe>',
548+
'<iframe>x</iframe>',
549+
];
550+
yield 'img longdesc javascript:' => [
551+
'img', 'longdesc',
552+
'<img longdesc="javascript:alert(1)">',
553+
'<img />',
554+
];
555+
}
556+
557+
public function testMetaRefreshContentIsSanitized()
558+
{
559+
$config = (new HtmlSanitizerConfig())
560+
->allowElement('meta', ['http-equiv', 'content'])
561+
;
562+
563+
$this->assertSame(
564+
'<meta http-equiv="refresh" />',
565+
(new HtmlSanitizer($config))->sanitizeFor('head', '<meta http-equiv="refresh" content="0; url=javascript:alert(1)">')
566+
);
567+
568+
$this->assertSame(
569+
'<meta http-equiv="refresh" content="5; url&#61;https://symfony.com/" />',
570+
(new HtmlSanitizer($config))->sanitizeFor('head', '<meta http-equiv="refresh" content="5; url=https://symfony.com/">')
571+
);
572+
573+
$this->assertSame(
574+
'<meta http-equiv="refresh" content="5" />',
575+
(new HtmlSanitizer($config))->sanitizeFor('head', '<meta http-equiv="refresh" content="5">')
576+
);
577+
}
578+
506579
public function testCustomAttributeSanitizer()
507580
{
508581
$config = (new HtmlSanitizerConfig())
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <[email protected]>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\HtmlSanitizer\Visitor\AttributeSanitizer;
13+
14+
use Symfony\Component\HtmlSanitizer\HtmlSanitizerConfig;
15+
use Symfony\Component\HtmlSanitizer\TextSanitizer\UrlSanitizer;
16+
17+
/**
18+
* Sanitizes the URL embedded in the content attribute of a <meta http-equiv="refresh">
19+
* element, since the http-equiv value is not visible from a per-attribute sanitizer.
20+
*
21+
* The content attribute carries an unrelated value for other meta types (description,
22+
* keywords, generator…), which is passed through unchanged.
23+
*/
24+
final class MetaRefreshAttributeSanitizer implements AttributeSanitizerInterface
25+
{
26+
public function getSupportedElements(): ?array
27+
{
28+
return ['meta'];
29+
}
30+
31+
public function getSupportedAttributes(): ?array
32+
{
33+
return ['content'];
34+
}
35+
36+
public function sanitizeAttribute(string $element, string $attribute, string $value, HtmlSanitizerConfig $config): ?string
37+
{
38+
if (!preg_match('/^(\s*\d+\s*[;,]\s*url\s*=\s*)(["\']?)(.+?)\2(\s*)$/i', $value, $m)) {
39+
return $value;
40+
}
41+
42+
$sanitized = UrlSanitizer::sanitize(
43+
$m[3],
44+
$config->getAllowedLinkSchemes(),
45+
$config->getForceHttpsUrls(),
46+
$config->getAllowedLinkHosts(),
47+
$config->getAllowRelativeLinks(),
48+
);
49+
50+
if (null === $sanitized) {
51+
return null;
52+
}
53+
54+
return $m[1].$m[2].$sanitized.$m[2].$m[4];
55+
}
56+
}

src/Symfony/Component/HtmlSanitizer/Visitor/AttributeSanitizer/UrlAttributeSanitizer.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ public function getSupportedElements(): ?array
2727

2828
public function getSupportedAttributes(): ?array
2929
{
30-
return ['src', 'href', 'lowsrc', 'background', 'ping', 'action', 'formaction', 'poster', 'cite'];
30+
return ['src', 'href', 'lowsrc', 'background', 'ping', 'action', 'formaction', 'poster', 'cite', 'data', 'codebase', 'archive', 'longdesc'];
3131
}
3232

3333
public function sanitizeAttribute(string $element, string $attribute, string $value, HtmlSanitizerConfig $config): ?string

0 commit comments

Comments
 (0)