From 71bc5d6fd6f24495a9a5723d8152e7a561e3d013 Mon Sep 17 00:00:00 2001 From: Adrien Roches Date: Thu, 4 Jul 2024 13:02:35 +0200 Subject: [PATCH 1/6] feat(HtmlSanitizer::Config): allow default action from semantic configuration --- .../FrameworkBundle/DependencyInjection/Configuration.php | 5 +++++ .../DependencyInjection/FrameworkExtension.php | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php index d5137dc2ba805..930e0faaeaaee 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php @@ -26,6 +26,7 @@ use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Exception\LogicException; use Symfony\Component\Form\Form; +use Symfony\Component\HtmlSanitizer\HtmlSanitizerAction; use Symfony\Component\HtmlSanitizer\HtmlSanitizerInterface; use Symfony\Component\HttpClient\HttpClient; use Symfony\Component\HttpFoundation\Cookie; @@ -2382,6 +2383,10 @@ private function addHtmlSanitizerSection(ArrayNodeDefinition $rootNode, callable ->fixXmlConfig('with_attribute_sanitizer') ->fixXmlConfig('without_attribute_sanitizer') ->children() + ->enumNode('default_action') + ->info('Defines how the sanitizer must behave by default.') + ->values(array_map(static fn (HtmlSanitizerAction $action): string => $action->value, HtmlSanitizerAction::cases())) + ->end() ->booleanNode('allow_safe_elements') ->info('Allows "safe" elements and attributes.') ->defaultFalse() diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php index 7cc67725ec461..b02876c6b873c 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php @@ -80,6 +80,7 @@ use Symfony\Component\Form\FormTypeGuesserInterface; use Symfony\Component\Form\FormTypeInterface; use Symfony\Component\HtmlSanitizer\HtmlSanitizer; +use Symfony\Component\HtmlSanitizer\HtmlSanitizerAction; use Symfony\Component\HtmlSanitizer\HtmlSanitizerConfig; use Symfony\Component\HtmlSanitizer\HtmlSanitizerInterface; use Symfony\Component\HttpClient\Messenger\PingWebhookMessageHandler; @@ -3006,6 +3007,10 @@ private function registerHtmlSanitizerConfiguration(array $config, ContainerBuil $def = $container->register($configId, HtmlSanitizerConfig::class); // Base + if ($sanitizerConfig['default_action']) { + $def->addMethodCall('defaultAction', [HtmlSanitizerAction::from($sanitizerConfig['default_action'])], true); + } + if ($sanitizerConfig['allow_safe_elements']) { $def->addMethodCall('allowSafeElements', [], true); } From 8b9f9f4e278a94467393213b077ef3103e896763 Mon Sep 17 00:00:00 2001 From: Adrien Roches Date: Thu, 4 Jul 2024 18:04:43 +0200 Subject: [PATCH 2/6] [UPDATE] Check for version installed of sanitizer --- .../DependencyInjection/FrameworkExtension.php | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php index b02876c6b873c..12e1b0bc1e7a7 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php @@ -12,6 +12,7 @@ namespace Symfony\Bundle\FrameworkBundle\DependencyInjection; use Composer\InstalledVersions; +use Composer\Semver\VersionParser; use Http\Client\HttpAsyncClient; use Http\Client\HttpClient; use phpDocumentor\Reflection\DocBlockFactoryInterface; @@ -3008,6 +3009,13 @@ private function registerHtmlSanitizerConfiguration(array $config, ContainerBuil // Base if ($sanitizerConfig['default_action']) { + if (InstalledVersions::satisfies(new VersionParser(), 'symfony/html-sanitizer', '>=7.2') === false) { + throw new LogicException(\sprintf( + 'Default action requires the HtmlSanitizer component to be installed in version >=7.2 (%s currently installed). Try running "composer require symfony/html-sanitizer".', + InstalledVersions::getVersion('symfony/html-sanitizer') + )); + } + $def->addMethodCall('defaultAction', [HtmlSanitizerAction::from($sanitizerConfig['default_action'])], true); } From 29f97495ae41ea52acc4523ec8e271c596ac1e43 Mon Sep 17 00:00:00 2001 From: Adrien Roches Date: Thu, 4 Jul 2024 18:12:04 +0200 Subject: [PATCH 3/6] [FIX] Key might be missing --- .../FrameworkBundle/DependencyInjection/FrameworkExtension.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php index 12e1b0bc1e7a7..3aa273f358283 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php @@ -3008,7 +3008,7 @@ private function registerHtmlSanitizerConfiguration(array $config, ContainerBuil $def = $container->register($configId, HtmlSanitizerConfig::class); // Base - if ($sanitizerConfig['default_action']) { + if ($sanitizerConfig['default_action'] ?? false) { if (InstalledVersions::satisfies(new VersionParser(), 'symfony/html-sanitizer', '>=7.2') === false) { throw new LogicException(\sprintf( 'Default action requires the HtmlSanitizer component to be installed in version >=7.2 (%s currently installed). Try running "composer require symfony/html-sanitizer".', From d63e583acf5cd633fd7052192ed30586a698c31e Mon Sep 17 00:00:00 2001 From: Adrien Roches Date: Fri, 5 Jul 2024 11:04:12 +0200 Subject: [PATCH 4/6] [UPDATE] Add some tests for default_action --- .../FrameworkExtension.php | 3 +- .../Fixtures/php/html_sanitizer.php | 1 + .../html_sanitizer_without_default_action.php | 50 +++++++++++++ .../Fixtures/xml/html_sanitizer.xml | 1 + .../html_sanitizer_without_default_action.xml | 65 +++++++++++++++++ .../Fixtures/yml/html_sanitizer.yml | 1 + .../html_sanitizer_without_default_action.yml | 44 ++++++++++++ .../FrameworkExtensionTestCase.php | 71 ++++++++++++++++++- 8 files changed, 233 insertions(+), 3 deletions(-) create mode 100644 src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/html_sanitizer_without_default_action.php create mode 100644 src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/html_sanitizer_without_default_action.xml create mode 100644 src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/html_sanitizer_without_default_action.yml diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php index 3aa273f358283..5cc7ee8fc9c9c 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php @@ -12,7 +12,6 @@ namespace Symfony\Bundle\FrameworkBundle\DependencyInjection; use Composer\InstalledVersions; -use Composer\Semver\VersionParser; use Http\Client\HttpAsyncClient; use Http\Client\HttpClient; use phpDocumentor\Reflection\DocBlockFactoryInterface; @@ -3009,7 +3008,7 @@ private function registerHtmlSanitizerConfiguration(array $config, ContainerBuil // Base if ($sanitizerConfig['default_action'] ?? false) { - if (InstalledVersions::satisfies(new VersionParser(), 'symfony/html-sanitizer', '>=7.2') === false) { + if (!class_exists(HtmlSanitizerAction::class)) { throw new LogicException(\sprintf( 'Default action requires the HtmlSanitizer component to be installed in version >=7.2 (%s currently installed). Try running "composer require symfony/html-sanitizer".', InstalledVersions::getVersion('symfony/html-sanitizer') diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/html_sanitizer.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/html_sanitizer.php index 1b64cd1b3e21e..81ae3c337cec0 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/html_sanitizer.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/html_sanitizer.php @@ -8,6 +8,7 @@ 'html_sanitizer' => [ 'sanitizers' => [ 'custom' => [ + 'default_action' => 'allow', 'allow_safe_elements' => true, 'allow_static_elements' => true, 'allow_elements' => [ diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/html_sanitizer_without_default_action.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/html_sanitizer_without_default_action.php new file mode 100644 index 0000000000000..1b64cd1b3e21e --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/html_sanitizer_without_default_action.php @@ -0,0 +1,50 @@ +loadFromExtension('framework', [ + 'annotations' => false, + 'http_method_override' => false, + 'handle_all_throwables' => true, + 'php_errors' => ['log' => true], + 'html_sanitizer' => [ + 'sanitizers' => [ + 'custom' => [ + 'allow_safe_elements' => true, + 'allow_static_elements' => true, + 'allow_elements' => [ + 'iframe' => 'src', + 'custom-tag' => ['data-attr', 'data-attr-1'], + 'custom-tag-2' => '*', + ], + 'block_elements' => ['section'], + 'drop_elements' => ['video'], + 'allow_attributes' => [ + 'src' => ['iframe'], + 'data-attr' => '*', + ], + 'drop_attributes' => [ + 'data-attr' => ['custom-tag'], + 'data-attr-1' => [], + 'data-attr-2' => '*', + ], + 'force_attributes' => [ + 'a' => ['rel' => 'noopener noreferrer'], + 'h1' => ['class' => 'bp4-heading'], + ], + 'force_https_urls' => true, + 'allowed_link_schemes' => ['http', 'https', 'mailto'], + 'allowed_link_hosts' => ['symfony.com'], + 'allow_relative_links' => true, + 'allowed_media_schemes' => ['http', 'https', 'data'], + 'allowed_media_hosts' => ['symfony.com'], + 'allow_relative_medias' => true, + 'with_attribute_sanitizers' => [ + 'App\\Sanitizer\\CustomAttributeSanitizer', + ], + 'without_attribute_sanitizers' => [ + 'App\\Sanitizer\\OtherCustomAttributeSanitizer', + ], + ], + 'all.sanitizer' => null, + ], + ], +]); diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/html_sanitizer.xml b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/html_sanitizer.xml index 7cb6758d93bfe..ce97226fc3fd3 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/html_sanitizer.xml +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/html_sanitizer.xml @@ -10,6 +10,7 @@ + + + + + + + + + + src + + + data-attr + data-attr-1 + + + * + + section + video + + iframe + + + * + + + custom-tag + + + + * + + + noopener noreferrer + + + bp4-heading + + http + https + mailto + symfony.com + http + https + data + symfony.com + App\Sanitizer\CustomAttributeSanitizer + App\Sanitizer\OtherCustomAttributeSanitizer + + + + + + diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/html_sanitizer.yml b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/html_sanitizer.yml index f0d515e418d86..77aae692a03fc 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/html_sanitizer.yml +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/html_sanitizer.yml @@ -7,6 +7,7 @@ framework: html_sanitizer: sanitizers: custom: + default_action: 'allow' allow_safe_elements: true allow_static_elements: true allow_elements: diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/html_sanitizer_without_default_action.yml b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/html_sanitizer_without_default_action.yml new file mode 100644 index 0000000000000..f0d515e418d86 --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/html_sanitizer_without_default_action.yml @@ -0,0 +1,44 @@ +framework: + annotations: false + http_method_override: false + handle_all_throwables: true + php_errors: + log: true + html_sanitizer: + sanitizers: + custom: + allow_safe_elements: true + allow_static_elements: true + allow_elements: + iframe: 'src' + custom-tag: ['data-attr', 'data-attr-1'] + custom-tag-2: '*' + block_elements: + - section + drop_elements: + - video + allow_attributes: + src: ['iframe'] + data-attr: '*' + drop_attributes: + data-attr: [custom-tag] + data-attr-1: [] + data-attr-2: '*' + force_attributes: + a: + rel: noopener noreferrer + h1: + class: bp4-heading + force_https_urls: true + allowed_link_schemes: ['http', 'https', 'mailto'] + allowed_link_hosts: ['symfony.com'] + allow_relative_links: true + allowed_media_schemes: ['http', 'https', 'data'] + allowed_media_hosts: ['symfony.com'] + allow_relative_medias: true + with_attribute_sanitizers: + - App\Sanitizer\CustomAttributeSanitizer + without_attribute_sanitizers: + - App\Sanitizer\OtherCustomAttributeSanitizer + + all.sanitizer: null diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTestCase.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTestCase.php index b37d2e910ec45..d5f91e19e166a 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTestCase.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTestCase.php @@ -47,6 +47,7 @@ use Symfony\Component\Finder\Finder; use Symfony\Component\Form\Form; use Symfony\Component\HtmlSanitizer\HtmlSanitizer; +use Symfony\Component\HtmlSanitizer\HtmlSanitizerAction; use Symfony\Component\HtmlSanitizer\HtmlSanitizerConfig; use Symfony\Component\HtmlSanitizer\HtmlSanitizerInterface; use Symfony\Component\HttpClient\MockHttpClient; @@ -2219,8 +2220,74 @@ public function testLocaleSwitcherServiceRegistered() $this->assertNotContains('translation.locale_switcher', $localeAwareServices); } + public function testHtmlSanitizerBefore72() + { + if (class_exists(HtmlSanitizerAction::class)) { + $this->markTestSkipped('HtmlSanitizer version is <7.2'); + } + + $container = $this->createContainerFromFile('html_sanitizer_without_default_action'); + + // html_sanitizer service + $this->assertSame(HtmlSanitizer::class, $container->getDefinition('html_sanitizer.sanitizer.custom')->getClass()); + $this->assertCount(1, $args = $container->getDefinition('html_sanitizer.sanitizer.custom')->getArguments()); + $this->assertSame('html_sanitizer.config.custom', (string) $args[0]); + + // config + $this->assertTrue($container->hasDefinition('html_sanitizer.config.custom'), '->registerHtmlSanitizerConfiguration() loads custom sanitizer'); + $this->assertSame(HtmlSanitizerConfig::class, $container->getDefinition('html_sanitizer.config.custom')->getClass()); + $this->assertCount(23, $calls = $container->getDefinition('html_sanitizer.config.custom')->getMethodCalls()); + $this->assertSame( + [ + ['allowSafeElements', [], true], + ['allowStaticElements', [], true], + ['allowElement', ['iframe', 'src'], true], + ['allowElement', ['custom-tag', ['data-attr', 'data-attr-1']], true], + ['allowElement', ['custom-tag-2', '*'], true], + ['blockElement', ['section'], true], + ['dropElement', ['video'], true], + ['allowAttribute', ['src', $this instanceof XmlFrameworkExtensionTest ? 'iframe' : ['iframe']], true], + ['allowAttribute', ['data-attr', '*'], true], + ['dropAttribute', ['data-attr', $this instanceof XmlFrameworkExtensionTest ? 'custom-tag' : ['custom-tag']], true], + ['dropAttribute', ['data-attr-1', []], true], + ['dropAttribute', ['data-attr-2', '*'], true], + ['forceAttribute', ['a', 'rel', 'noopener noreferrer'], true], + ['forceAttribute', ['h1', 'class', 'bp4-heading'], true], + ['forceHttpsUrls', [true], true], + ['allowLinkSchemes', [['http', 'https', 'mailto']], true], + ['allowLinkHosts', [['symfony.com']], true], + ['allowRelativeLinks', [true], true], + ['allowMediaSchemes', [['http', 'https', 'data']], true], + ['allowMediaHosts', [['symfony.com']], true], + ['allowRelativeMedias', [true], true], + ['withAttributeSanitizer', ['@App\\Sanitizer\\CustomAttributeSanitizer'], true], + ['withoutAttributeSanitizer', ['@App\\Sanitizer\\OtherCustomAttributeSanitizer'], true], + ], + + // Convert references to their names for easier assertion + array_map( + static function ($call) { + foreach ($call[1] as $k => $arg) { + $call[1][$k] = $arg instanceof Reference ? '@'.$arg : $arg; + } + + return $call; + }, + $calls + ) + ); + + // Named alias + $this->assertSame('html_sanitizer.sanitizer.all.sanitizer', (string) $container->getAlias(HtmlSanitizerInterface::class.' $allSanitizer')); + $this->assertFalse($container->hasAlias(HtmlSanitizerInterface::class.' $default')); + } + public function testHtmlSanitizer() { + if (!class_exists(HtmlSanitizerAction::class)) { + $this->markTestSkipped('HtmlSanitizer version must be >=7.2'); + } + $container = $this->createContainerFromFile('html_sanitizer'); // html_sanitizer service @@ -2231,9 +2298,11 @@ public function testHtmlSanitizer() // config $this->assertTrue($container->hasDefinition('html_sanitizer.config.custom'), '->registerHtmlSanitizerConfiguration() loads custom sanitizer'); $this->assertSame(HtmlSanitizerConfig::class, $container->getDefinition('html_sanitizer.config.custom')->getClass()); - $this->assertCount(23, $calls = $container->getDefinition('html_sanitizer.config.custom')->getMethodCalls()); + $calls = $container->getDefinition('html_sanitizer.config.custom')->getMethodCalls(); + $this->assertCount(24, $calls = $container->getDefinition('html_sanitizer.config.custom')->getMethodCalls()); $this->assertSame( [ + ['defaultAction', [HtmlSanitizerAction::Allow], true], ['allowSafeElements', [], true], ['allowStaticElements', [], true], ['allowElement', ['iframe', 'src'], true], From 4744fe20bb4aa49f5871e2ce109d220f03ee6f1b Mon Sep 17 00:00:00 2001 From: Adrien Roches Date: Fri, 5 Jul 2024 11:05:52 +0200 Subject: [PATCH 5/6] [UPDATE] Remove duplicate code test --- .../Tests/DependencyInjection/FrameworkExtensionTestCase.php | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTestCase.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTestCase.php index d5f91e19e166a..72a8bbf0b0508 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTestCase.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTestCase.php @@ -2298,7 +2298,6 @@ public function testHtmlSanitizer() // config $this->assertTrue($container->hasDefinition('html_sanitizer.config.custom'), '->registerHtmlSanitizerConfiguration() loads custom sanitizer'); $this->assertSame(HtmlSanitizerConfig::class, $container->getDefinition('html_sanitizer.config.custom')->getClass()); - $calls = $container->getDefinition('html_sanitizer.config.custom')->getMethodCalls(); $this->assertCount(24, $calls = $container->getDefinition('html_sanitizer.config.custom')->getMethodCalls()); $this->assertSame( [ From 6a05b1b72fdc7d01065f1c34854f75dd8114f157 Mon Sep 17 00:00:00 2001 From: Adrien Roches Date: Tue, 9 Jul 2024 20:46:03 +0200 Subject: [PATCH 6/6] [UPDATE] Fix xml config --- .../FrameworkBundle/Resources/config/schema/symfony-1.0.xsd | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/schema/symfony-1.0.xsd b/src/Symfony/Bundle/FrameworkBundle/Resources/config/schema/symfony-1.0.xsd index d8d23168d1887..55935d2d5fbbd 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/schema/symfony-1.0.xsd +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/schema/symfony-1.0.xsd @@ -918,6 +918,7 @@ + pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy