Search code examples
javascriptmustachedompurify

DOMPurify changing Mustache-Template-Structure


I am trying to sanitize an user-submitted template by using DOMPurify. This template is using the mustache-syntax to embed variables etc. The sanitizing itself works without issue, but Mustache-Sections are being moved to different parts within the template and DOMPurify seems to try to fix the HTML structure itself.

I am assuming this "autofix" is messing with the template. Is there a way to prevent this?

The following code is being used for the sanitizing:

DOMPurify.sanitize(model.html_template);

Template before sanitizing:

<table>
    <tr>
        <td>{{value1}}</td>
    </tr>
    
    {{#section}}
    <tr>
        <td>{{value2}}</td>
    </tr>
    {{/section}}
</table>

Template after sanitizing:

{{#section}}
    
{{/section}}
<table>
    <tbody>
        <tr>
            <td>{{value1}}</td>
        </tr>
        <tr>
            <td>{{value2}}</td>
        </tr>
    </tbody>
</table>


Solution

  • I think your safest option is to first render and then sanitize, rather than the other way round. I realize this is less efficient; of course, it would be ideal if you could sanitize just the template once and then use it repeatedly without any further processing. However, the DOMPurify documentation explicitly warns against doing any more HTML processing after sanitization:

    Is there any foot-gun potential?
    Well, please note, if you first sanitize HTML and then modify it afterwards, you might easily void the effects of sanitization. If you feed the sanitized markup to another library after sanitization, please be certain that the library doesn't mess around with the HTML on its own.

    You might argue that Mustache does not "mess around with" the HTML, but this is obviously not true. Consider the following problematic example:

    <table>
        {{#rows}}
        <tr><td>{{value}}</td></tr>
    </table>
    {{/rows}}
    

    The HTML looks valid if you ignore the Mustache syntax, but the generated code will be invalid unless rows resolves to something of exactly length 1.

    Another telltale sign is that DOMPurify's options rather cater to the opposite situation: SAFE_FOR_TEMPLATES: true will strip out all template syntax.

    In case you need more reasons: DOMPurify defers much of its sanitization logic to the browser. The moved section problem might very well be specific to the browser in which you are testing, so it is not even something that DOMPurify can control. Moreover, other browsers might mangle your templates in other ways that you are not even aware of yet. By rendering first and making sanitization the very last step before DOM insertion, you avoid a large class of potential problems.