How to allow var(--...) in HtmlPurufier

31 Views Asked by At

How to resolve var(--...) style attribute with HtmlPurifier? I tried with this config:

<?php

return [
    'encoding'           => 'UTF-8',
    'finalize'           => true,
    'ignoreNonStrings'   => false,
    'cachePath'          => storage_path('app/purifier'),
    'cacheFileMode'      => 0755,
    'settings'      => [
        'default' => [
            'HTML.Doctype'             => 'HTML 4.01 Transitional',
            'HTML.Allowed'             => 'div,b,strong,i,em,s,u,a[class|href|title],ul,ol,li,p[style],br,span[style],img[width|height|alt|src]',
            'CSS.AllowedProperties'    => 'font,font-size,font-weight,font-style,font-family,text-decoration,padding-left,color,var,background-color,text-align',
            'AutoFormat.AutoParagraph' => true,
            'AutoFormat.RemoveEmpty'   => true,
        ],
    ],
];

$string = '<span style="color: var(--myvar);">text</span>';
$string = Purifier::clean($string);

echo $string;

But I got the result without style attribute:

<span>text</span>

Expected result:

<span style="color: var(--myvar);">text</span>

Any ideas, thanks!

1

There are 1 best solutions below

0
pinkgothic On

Why is this happening?

Fundamentally, HTML Purifier works by understanding how HTML and CSS function and are interpreted by browsers. It doesn't just let you whitelist tags or CSS, but also has knobs to define maximum image sizes to prevent imagecrash attacks and blocks attempts to put unsupported child elements into a particular place (e.g. <div> into <ul> without a <li>), which may have undefined behaviours that could, in any one browser, be an actual exploit.

To be able to do these things, it needs to understand all the elements you try to whitelist. It comes with an extensive CSS and HTML definition out of the box, but (1) even back when it was written, HTML Purifier intentionally didn't bother implementing knowledge about certain parts of HTML and CSS that the author considered inherently unsafe, (2) HTML5 has happened since HTML Purifier was written, and there have been advances in CSS.

So it can absolutely happen that you have some benign input that HTML Purifier won't allow even if it's whitelisted, because it doesn't understand the input.

What you can do.

But! You can teach it. Sadly the Customize! documentation I just linked to is for HTML elements and not for CSS, but it introduces some concepts that are valuable to understanding how HTML Purifier works under the hood. I won't paste them here because they're orthogonal to your question.

CSS doesn't have a nice corresponding doc at the moment. The add new css property thread on the HTML Purifier forum (archive.org link) had some information on how to do this. This is an example that Oliver Schonrock posted that added a CSS attribute (not a value), which might nonetheless get you started on the right track:

Thanks for this post. It helped my do exactly what the OP asked for. Here is a concrete code example which could save others time:

 $config = HTMLPurifier_Config::createDefault();

 // add some custom CSS3 properties                                                                                                                                              
 $css_definition = $config->getDefinition('CSS');

 $border_radius =
   $info['border-top-left-radius'] =
   $info['border-top-right-radius'] =
   $info['border-bottom-left-radius'] =
   $info['border-bottom-right-radius'] =
   new HTMLPurifier_AttrDef_CSS_Composite(array(
                                            new HTMLPurifier_AttrDef_CSS_Length('0'),
                                            new HTMLPurifier_AttrDef_CSS_Percentage(true)
                                            ));

 $info['border-radius'] = new HTMLPurifier_AttrDef_CSS_Multiple($border_radius);

 // wrap all new attr-defs with decorator that handles !important                                                                                                                
 $allow_important = $config->get('CSS.AllowImportant');
 foreach ($info as $k => $v) {
   $css_definition->info[$k] = new HTMLPurifier_AttrDef_CSS_ImportantDecorator($v, $allow_important);
 }

 $html_purifier = new HTMLPurifier($config);

In your case, you will want to write a custom AttrDef decorator (similar to the HTMLPurifier_AttrDef_CSS_ImportantDecorator one you can see in this example code) and assign it to all the CSS attributes that you want to support var() for.

But also, you may not want to do this. If you want to add it properly, it'll probably be hard - you'll want to make sure that no one can use var()s to bypass value checks that HTML Purifier is doing, for example! (e.g. someone may be able to bypass the imagecrash defenses this way, just as a salient example).