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
- Open
dijit/tests/editor/test_ViewSource.html in the test environment.
- Locate the
editor_poc editor.
- Confirm it uses the default
viewSource plugin configuration, where stripEventHandlers is enabled.
- Toggle View Source mode on.
- Confirm the multiline
onclick payload remains present.
- Toggle View Source mode off.
- Click the rendered button.
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:
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.
Hi, I’d like to report a potential XSS issue in the ViewSource plugin’s regex-based filtering logic.
Summary
dijit/_editor/plugins/ViewSource.jsenablesstripEventHandlersby 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 asonclick="\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.jsThe vulnerable part is:
The regex uses flags
igm, but themflag 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.htmland added a PoC editor block:Reproduction Steps
dijit/tests/editor/test_ViewSource.htmlin the test environment.editor_poceditor.viewSourceplugin configuration, wherestripEventHandlersis enabled.onclickpayload remains present.alert('Button clicked!')executes.Expected Result
When
stripEventHandlersis enabled, all inline event handler attributes should be removed, including multiline quoted values such as:onclick=" alert('Button clicked!')"Actual Result
The multiline
onclickattribute 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:
Security Impact
This issue usually requires the following conditions:
dijit/Editor.ViewSourceplugin is enabled.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/igmHowever,
.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.