Description
Symfony version(s) affected
6.1, 6.2, 6.3, 6.4, 7.0, 7.1, 7.2
Description
In the HTMLSanitizer, the force_attributes
definition does not work as described in the documentation or in the class.
If an attribute is already defined in the unsanitized html, it is not replaced by the value defined in configuration.
As definied in the Force Attributes Values documentation :
Using this option, you can force an attribute with a given value on an element. For instance, use the follow config to always set rel="noopener noreferrer" on each element (even if the original one didn't contain a rel attribute)
And in the HTMLSanitizerConfig class function forceAttribute :
/**
* Forcefully set the value of a given attribute on a given element.
*
* The attribute will be created on the nodes if it didn't exist.
*/
As i understand the documentation, the attribute value should be replaced if already existing.
How to reproduce
With the following HTMLSanitizer configuration :
// config/packages/html_sanitizer.yaml
framework:
html_sanitizer:
sanitizers:
app.wysiwyg_sanitizer:
allow_safe_elements: true
force_https_urls: true
allow_relative_medias: true
allow_attributes:
border: '*'
class: '*'
src: ['img']
style: '*'
force_attributes:
img:
loading: lazy
The initial string to be sanitized :
<img title="My image" src="https://example.com/image.png" loading="eager" onerror="alert('1234')" />
Should result to :
<img title="My image" src="https://example.com/image.png" loading="lazy" />
But it actually sanitized to :
<img title="My image" src="https://example.com/image.png" loading="eager" />
And with the initial string : <img title="My image" src="https://example.com/image.png" />
The loading=lazy
attribute is correctly added : <img title="My image" src="https://example.com/image.png" loading="lazy"/>
To be sure it's replaced i need to add a drop_attributes
entry to first drop existing loading
attribute and then force it.
REPRODUCE
<?php
// $ composer require symfony/html-sanitizer
require_once __DIR__.'/vendor/autoload.php';
use Symfony\Component\HtmlSanitizer\HtmlSanitizer;
use Symfony\Component\HtmlSanitizer\HtmlSanitizerConfig;
// String containing a loading attribute (and other attributes to be sanitized)
$stringToSanitize1 = '<img title="My image" src="https://example.com/image.png" loading="eager" onerror="alert(\'1234\')" />';
// String NOT containing a loading attribute
$stringToSanitize2 = '<img title="My image" src="https://example.com/image.png" />';
// Init htmlSanitizer with a basic config forcing "loading" attribute to the value "lazy" on images
$htmlSanitizer = new HtmlSanitizer(
(new HtmlSanitizerConfig())->allowSafeElements()->forceAttribute('img', 'loading', 'lazy')
);
var_dump($htmlSanitizer->sanitize($stringToSanitize1));
// Result : string(76) "<img title="My image" src="https://example.com/image.png" loading="eager" />"
var_dump($htmlSanitizer->sanitize($stringToSanitize2));
// Result : string(75) "<img title="My image" src="https://example.com/image.png" loading="lazy" />"
Possible Solution
No response
Additional Context
Tested on :
"In Application" => Symfony 6.4.10, PHP 8.3, symfony/html-sanitizer
6.4.8
"Reproducer" => PHP8.3, symfony/html-sanitizer
7.1.1
Also a "workaround" is to add a drop_attributes
configuration before forcing it.
// config/packages/html_sanitizer.yaml
framework:
html_sanitizer:
sanitizers:
app.wysiwyg_sanitizer:
allow_safe_elements: true
force_https_urls: true
allow_relative_medias: true
allow_attributes:
border: '*'
class: '*'
src: ['img']
style: '*'
drop_attributes:
loading: ['img']
force_attributes:
img:
loading: lazy