8000 Integrate the HtmlSanitizer component to the FrameworkBundle · symfony/symfony@e662efa · GitHub
[go: up one dir, main page]

Skip to content

Commit e662efa

Browse files
committed
Integrate the HtmlSanitizer component to the FrameworkBundle
1 parent 199e405 commit e662efa

File tree

3 files changed

+261
-0
lines changed

3 files changed

+261
-0
lines changed

src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php

Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
use Symfony\Component\DependencyInjection\ContainerBuilder;
2525
use Symfony\Component\DependencyInjection\Exception\LogicException;
2626
use Symfony\Component\Form\Form;
27+
use Symfony\Component\HtmlSanitizer\HtmlSanitizerInterface;
2728
use Symfony\Component\HttpClient\HttpClient;
2829
use Symfony\Component\HttpFoundation\Cookie;
2930
use Symfony\Component\Lock\Lock;
@@ -160,6 +161,7 @@ public function getConfigTreeBuilder(): TreeBuilder
160161
$this->addNotifierSection($rootNode, $enableIfStandalone);
161162
$this->addRateLimiterSection($rootNode, $enableIfStandalone);
162163
$this->addUidSection($rootNode, $enableIfStandalone);
164+
$this->addHtmlSanitizerSection($rootNode, $enableIfStandalone);
163165

164166
return $treeBuilder;
165167
}
@@ -2043,4 +2045,133 @@ private function addUidSection(ArrayNodeDefinition $rootNode, callable $enableIf
20432045
->end()
20442046
;
20452047
}
2048+
2049+
private function addHtmlSanitizerSection(ArrayNodeDefinition $rootNode, callable $enableIfStandalone)
2050+
{
2051+
$rootNode
2052+
->children()
2053+
->arrayNode('html_sanitizer')
2054+
->info('HtmlSanitizer configuration')
2055+
->{$enableIfStandalone('symfony/html-sanitizer', HtmlSanitizerInterface::class)}()
2056+
->fixXmlConfig('sanitizer')
2057+
->children()
2058+
->scalarNode('default')
2059+
->defaultValue('html_sanitizer')
2060+
->info('Default sanitizer to use when injecting without named binding.')
2061+
->end()
2062+
->arrayNode('sanitizers')
2063+
->normalizeKeys(false)
2064+
->useAttributeAsKey('name')
2065+
->arrayPrototype()
2066+
->children()
2067+
->scalarNode('allow_safe_elements')
2068+
->info('Allows "safe" elements and attributes.')
2069+
->end()
2070+
->scalarNode('allow_all_static_elements')
2071+
->info('Allows all static elements and attributes from the W3C Sanitizer API standard.')
2072+
->end()
2073+
->arrayNode('allow_elements')
2074+
->info('Configures elements as allowed. Allowed elements are elements the sanitizer should retain from the input.')
2075+
->normalizeKeys(false)
2076+
->defaultValue([])
2077+
->prototype('variable')
2078+
->end()
2079+
->end()
2080+
->arrayNode('block_elements')
2081+
->info('Configures elements as blocked. Blocked elements are elements the sanitizer should remove from the input, but retain their children.')
2082+
->normalizeKeys(false)
2083+
->defaultValue([])
2084+
->prototype('variable')
2085+
->end()
2086+
->end()
2087+
->arrayNode('drop_elements')
2088+
->info('Configures elements as dropped. Dropped elements are elements the sanitizer should remove from the input, including their children.')
2089+
->normalizeKeys(false)
2090+
->defaultValue([])
2091+
->prototype('variable')
2092+
->end()
2093+
->end()
2094+
->arrayNode('allow_attributes')
2095+
->info('Configures attributes as allowed. Allowed attributes are attributes the sanitizer should retain from the input.')
2096+
->normalizeKeys(false)
2097+
->defaultValue([])
2098+
->prototype('variable')
2099+
->end()
2100+
->end()
2101+
->arrayNode('drop_attributes')
2102+
->info('Configures attributes as dropped. Dropped attributes are attributes the sanitizer should remove from the input.')
2103+
->normalizeKeys(false)
2104+
->defaultValue([])
2105+
->prototype('variable')
2106+
->end()
2107+
->end()
2108+
->arrayNode('force_attributes')
2109+
->info('Forcefully set the values of certain attributes on certain elements.')
2110+
->normalizeKeys(false)
2111+
->defaultValue([])
2112+
->prototype('variable')
2113+
->end()
2114+
->end()
2115+
->booleanNode('force_https_urls')
2116+
->info('Transforms URLs using the HTTP scheme to use the HTTPS scheme instead.')
2117+
->defaultFalse()
2118+
->end()
2119+
->arrayNode('allowed_link_schemes')
2120+
->info('Allows only a given list of schemes to be used in links href attributes.')
2121+
->normalizeKeys(false)
2122+
->defaultValue([])
2123+
->prototype('variable')
2124+
->end()
2125+
->end()
2126+
->arrayNode('allowed_link_hosts')
2127+
->info('Allows only a given list of hosts to be used in links href attributes.')
2128+
->normalizeKeys(false)
2129+
->defaultValue([])
2130+
->prototype('variable')
2131+
->end()
2132+
->end()
2133+
->booleanNode('allow_relative_links')
2134+
->info('Allows relative URLs to be used in links href attributes.')
2135+
->defaultFalse()
2136+
->end()
2137+
->arrayNode('allowed_media_schemes')
2138+
->info('Allows only a given list of schemes to be used in media source attributes (img, audio, video, ...).')
2139+
->normalizeKeys(false)
2140+
->defaultValue([])
2141+
->prototype('variable')
2142+
->end()
2143+
->end()
2144+
->arrayNode('allowed_media_hosts')
2145+
->info('Allows only a given list of hosts to be used in media source attributes (img, audio, video, ...).')
2146+
->normalizeKeys(false)
2147+
->defaultValue([])
2148+
->prototype('variable')
2149+
->end()
2150+
->end()
2151+
->booleanNode('allow_relative_medias')
2152+
->info('Allows relative URLs to be used in media source attributes (img, audio, video, ...).')
2153+
->defaultFalse()
2154+
->end()
2155+
->arrayNode('with_attribute_sanitizers')
2156+
->info('Registers custom attribute sanitizers.')
2157+
->normalizeKeys(false)
2158+
->defaultValue([])
2159+
->prototype('variable')
2160+
->end()
2161+
->end()
2162+
->arrayNode('without_attribute_sanitizers')
2163+
->info('Unregisters custom attribute sanitizers.')
2164+
->normalizeKeys(false)
2165+
->defaultValue([])
2166+
->prototype('variable')
2167+
->end()
2168+
->end()
2169+
->end()
2170+
->end()
2171+
->end()
2172+
->end()
2173+
->end()
2174+
->end()
2175+
;
2176+
}
20462177
}

src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,9 @@
7171
use Symfony\Component\Form\FormTypeExtensionInterface;
7272
use Symfony\Component\Form\FormTypeGuesserInterface;
7373
use Symfony\Component\Form\FormTypeInterface;
74+
use Symfony\Component\HtmlSanitizer\HtmlSanitizer;
75+
use Symfony\Component\HtmlSanitizer\HtmlSanitizerConfig;
76+
use Symfony\Component\HtmlSanitizer\HtmlSanitizerInterface;
7477
use Symfony\Component\HttpClient\MockHttpClient;
7578
use Symfony\Component\HttpClient\Retry\GenericRetryStrategy;
7679
use Symfony\Component\HttpClient\RetryableHttpClient;
@@ -488,6 +491,14 @@ public function load(array $configs, ContainerBuilder $container)
488491
$this->registerUidConfiguration($config['uid'], $container, $loader);
489492
}
490493

494+
if ($this->isConfigEnabled($container, $config['html_sanitizer'])) {
495+
if (!class_exists(HtmlSanitizerConfig::class)) {
496+
throw new LogicException('HtmlSanitizer support cannot be enabled as the HtmlSanitizer component is not installed. Try running "composer require symfony/html-sanitizer".');
497+
}
498+
499+
$this->registerHtmlSanitizerConfiguration($config['html_sanitizer'], $container, $loader);
500+
}
501+
491502
$this->addAnnotatedClassesToCompile([
492503
'**\\Controller\\',
493504
'**\\Entity\\',
@@ -2549,6 +2560,100 @@ private function registerUidConfiguration(array $config, ContainerBuilder $conta
25492560
}
25502561
}
25512562

2563+
private function registerHtmlSanitizerConfiguration(array $config, ContainerBuilder $container, PhpFileLoader $loader)
2564+
{
2565+
$loader->load('html_sanitizer.php');
2566+
2567+
foreach ($config['sanitizers'] as $sanitizerName => $sanitizerConfig) {
2568+
$def = $container->register($sanitizerName.'.config', HtmlSanitizerConfig::class);
2569+
2570+
// Base configuration
2571+
if ($sanitizerConfig['allow_safe_elements'] ?? false) {
2572+
$def->addMethodCall('allowSafeElements', [], true);
2573+
}
2574+
2575+
if ($sanitizerConfig['allow_all_static_elements'] ?? false) {
2576+
$def->addMethodCall('allowAllStaticElements', [], true);
2577+
}
2578+
2579+
// Configures elements
2580+
foreach ($sanitizerConfig['allow_elements'] ?? [] as $element => $attributes) {
2581+
$def->addMethodCall('allowElement', [$element, $attributes], true);
2582+
}
2583+
2584+
foreach ($sanitizerConfig['block_elements'] ?? [] as $element) {
2585+
$def->addMethodCall('blockElement', [$element], true);
2586+
}
2587+
2588+
foreach ($sanitizerConfig['drop_elements'] ?? [] as $element) {
2589+
$def->addMethodCall('dropElement', [$element], true);
2590+
}
2591+
2592+
// Configures attributes
2593+
foreach ($sanitizerConfig['allow_attributes'] ?? [] as $attribute => $elements) {
2594+
$def->addMethodCall('allowAttribute', [$attribute, $elements], true);
2595+
}
2596+
2597+
foreach ($sanitizerConfig['drop_attributes'] ?? [] as $attribute => $elements) {
2598+
$def->addMethodCall('dropAttribute', [$attribute, $elements], true);
2599+
}
2600+
2601+
// Force attributes
2602+
foreach ($sanitizerConfig['force_attributes'] ?? [] as $element => $attributes) {
2603+
foreach ($attributes as $attrName => $attrValue) {
2604+
$def->addMethodCall('forceAttribute', [$element, $attrName, $attrValue], true);
2605+
}
2606+
}
2607+
2608+
// Settings
2609+
if (isset($sanitizerConfig['force_https_urls'])) {
2610+
$def->addMethodCall('forceHttpsUrls', [$sanitizerConfig['force_https_urls']], true);
2611+
}
2612+
2613+
if (isset($sanitizerConfig['allowed_link_schemes'])) {
2614+
$def->addMethodCall('allowLinkSchemes', [$sanitizerConfig['allowed_link_schemes']], true);
2615+
}
2616+
2617+
if (isset($sanitizerConfig['allowed_link_hosts'])) {
2618+
$def->addMethodCall('allowLinkHosts', [$sanitizerConfig['allowed_link_hosts']], true);
2619+
}
2620+
2621+
if (isset($sanitizerConfig['allow_relative_links'])) {
2622+
$def->addMethodCall('allowRelativeLinks', [$sanitizerConfig['allow_relative_links']], true);
2623+
}
2624+
2625+
if (isset($sanitizerConfig['allowed_media_schemes'])) {
2626+
$def->addMethodCall('allowMediaSchemes', [$sanitizerConfig['allowed_media_schemes']], true);
2627+
}
2628+
2629+
if (isset($sanitizerConfig['allowed_media_hosts'])) {
2630+
$def->addMethodCall('allowMediaHosts', [$sanitizerConfig['allowed_media_hosts']], true);
2631+
}
2632+
2633+
if (isset($sanitizerConfig['allow_relative_medias'])) {
2634+
$def->addMethodCall('allowRelativeMedias', [$sanitizerConfig['allow_relative_medias']], true);
2635+
}
2636+
2637+
// Custom attribute sanitizers
2638+
foreach ($sanitizerConfig['with_attribute_sanitizers'] ?? [] as $serviceName) {
2639+
$def->addMethodCall('withAttributeSanitizer', [new Reference($serviceName)], true);
2640+
}
2641+
2642+
foreach ($sanitizerConfig['without_attribute_sanitizers'] ?? [] as $serviceName) {
2643+
$def->addMethodCall('withoutAttributeSanitizer', [new Reference($serviceName)], true);
2644+
}
2645+
2646+
// Create the sanitizer and link its config
2647+
$container->register($sanitizerName, HtmlSanitizer::class)
2648+
->addArgument(new Reference($sanitizerName.'.config'))
2649+
;
2650+
2651+
$container->registerAliasForArgument($sanitizerName, HtmlSanitizerInterface::class, $sanitizerName);
2652+
}
2653+
2654+
$container->setAlias(HtmlSanitizerInterface::class, new Reference($config['default']));
2655+
}
2656+
25522657
private function resolveTrustedHeaders(array $headers): int
25532658
{
25542659
$trustedHeaders = 0;
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
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\DependencyInjection\Loader\Configurator;
13+
14+
use Symfony\Component\HtmlSanitizer\HtmlSanitizer;
15+
use Symfony\Component\HtmlSanitizer\HtmlSanitizerConfig;
16+
17+
return static function (ContainerConfigurator $container) {
18+
$container->services()
19+
->set('html_sanitizer.config', HtmlSanitizerConfig::class)
20+
->call('allowSafeElements')
21+
22+
->set('html_sanitizer', HtmlSanitizer::class)
23+
->args([service('html_sanitizer.config')])
24+
;
25+
};

0 commit comments

Comments
 (0)
0