Skip to content

Potential XSS in ViewSource stripEventHandlers due to multiline event handler regex bypass #208

Description

@WiiiiillYeng

Hi, I’d like to report a potential XSS issue in the ViewSource plugin’s regex-based filtering logic.

Summary

dijit/_editor/plugins/ViewSource.js enables stripEventHandlers by default, but the regex used to remove inline event handler attributes does not handle newline characters inside quoted attribute values.

Because JavaScript regex . does not match line terminators, an event handler such as onclick="\nalert(...)" can remain in the editor content after toggling View Source mode, resulting in XSS when the rendered element is clicked.

Affected Code

File: dijit/_editor/plugins/ViewSource.js

_stripEventHandlers: function (html) {
    if(html){
        // Find all tags that contain an event handler attribute (an on* attribute).
        var matches = html.match(/<[a-z]+?\b(.*?on.*?(['"]).*?\2.*?)+>/gim);
        if(matches){
            for(var i = 0, l = matches.length; i < l; i++){
                // For each tag, remove only the event handler attributes.
                var match = matches[i];
                var replacement = match.replace(/\s+on[a-z]*\s*=\s*(['"])(.*?)\1/igm, "");
                html = html.replace(match, replacement);
            }
        }
    }
    return html;
},

The vulnerable part is:

(.*?)

The regex uses flags igm, but the m flag only changes the behavior of ^ and $. It does not make . match newline characters. Therefore, when an event handler attribute value spans multiple lines, (.*?) stops before the newline and the whole event handler attribute is not removed.

Proof of Concept

I modified the project-provided test page dijit/tests/editor/test_ViewSource.html and added a PoC editor block:

<div>
    <div id="editor_poc" data-dojo-type="dijit/Editor"
         data-dojo-props='"aria-label":"editor_poc",extraPlugins:["fullScreen", "viewSource"],
        style:"background-color: white; width: 800px;", height:"300px" '>
        <h1>ViewSource Plugin with stripEventHandlers bypass PoC</h1>
        <p>Steps to trigger the issue when stripEventHandlers is enabled:</p>
        <ol>
            <li>Toggle to View Source, ensure the payload remains unchanged, then toggle back.</li>
            <li>Click the rendered payload to observe alert execution.</li>
        </ol>
        <!-- PoC payload -->
        <button id="button3" onclick="
alert('Button clicked!')" ondblclick="alert('Button double clicked!')">
onclick button
</button>
    </div>
</div>

Reproduction Steps

  1. Open dijit/tests/editor/test_ViewSource.html in the test environment.
  2. Locate the editor_poc editor.
  3. Confirm it uses the default viewSource plugin configuration, where stripEventHandlers is enabled.
  4. Toggle View Source mode on.
  5. Confirm the multiline onclick payload remains present.
  6. Toggle View Source mode off.
  7. Click the rendered button.
  8. alert('Button clicked!') executes.

Expected Result

When stripEventHandlers is enabled, all inline event handler attributes should be removed, including multiline quoted values such as:

onclick="
alert('Button clicked!')"

Actual Result

The multiline onclick attribute is not removed because (.*?) does not match the newline. The event handler remains in the rendered editor content and JavaScript executes when the button is clicked.

Screenshots:

Image Image

Security Impact

This issue usually requires the following conditions:

  • The application uses dijit/Editor.
  • The ViewSource plugin is enabled.
  • An attacker can submit, edit, save, or otherwise influence HTML that enters the editor.
  • A victim user views or interacts with the rendered content.

If these conditions are met, an attacker can persist malicious event handler attributes in edited content, resulting in stored XSS. If the affected content is only previewed temporarily or passed into the editor for a single request/session, the impact may instead be reflected XSS.

Root Cause

The event handler stripping regex assumes that . can consume the whole quoted attribute value:

/\s+on[a-z]*\s*=\s*(['"])(.*?)\1/igm

However, . does not match newline characters in JavaScript regex without dotAll behavior or an equivalent construct such as [\s\S]. Therefore, multiline event handler attributes bypass the removal logic.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions