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 attempts to remove tags containing javascript: URLs in _stripScripts, but the regular expression is malformed. The expression appears to intend to match quoted javascript: attribute values and then use the opening quote as a backreference, but it does not do that.
As a result, a dangerous form action such as action="javascript:alert('XSS')" can remain in the editor content and execute when the form is submitted.
Affected Code
File: dijit/_editor/plugins/ViewSource.js
html = html.replace(/<[^>]*=(\s|)*[("|')]javascript:[^$1][(\s|.)]*[$1][^>]*>/ig, "");
There are four key regex mistakes:
[("|')] is a character class. It does not mean "match either a double quote or a single quote". It matches one character from the set (, ", |, ', or ).
[^$1] is a negated character class. It is not related to capture group 1. It only means "match one character that is not $ and not 1.
[(\s|.)] is a character class. It does not mean "match whitespace or any character". It only matches one character from the set (, \s, |, ., or ).
[$1] is also a character class. It is not a backreference to capture group 1. It only matches either $ or 1.
Because of these mistakes, the regex does not reliably match normal quoted javascript: attribute values.
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 stripScripts javascript URL bypass PoC</h1>
<p>Steps to trigger the issue when stripScripts is enabled:</p>
<ol>
<li>Toggle to View Source, ensure the payload remains unchanged, then toggle back.</li>
<li>Submit the rendered form to observe alert execution.</li>
</ol>
<!-- PoC payload -->
<form action="javascript:alert('XSS')"> <input type="submit" value="submit" /> </form>
</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 stripScripts is enabled.
- Toggle View Source mode on.
- Confirm the
<form action="javascript:alert('XSS')"> payload remains present.
- Toggle View Source mode off.
- Submit the rendered form.
alert('XSS') executes.
Expected Result
When stripScripts is enabled, dangerous javascript: URL attributes should be removed or neutralized. The following payload should not remain executable:
<form action="javascript:alert('XSS')">
<input type="submit" value="submit" />
</form>
Actual Result
The payload is not removed by the _stripScripts regex. The javascript: form action remains in the editor content and executes when the form is submitted.
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, such as submitting a form or clicking a link.
If these conditions are met, an attacker can persist malicious javascript: URL 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 regex is written as:
/<[^>]*=(\s|)*[("|')]javascript:[^$1][(\s|.)]*[$1][^>]*>/ig
The intended behavior appears to be:
- match an attribute assignment,
- match a quote character,
- match
javascript:,
- consume the rest of the quoted JavaScript URL,
- match the same closing quote.
But the actual behavior is different:
[("|')] is a character class. It matches one character among (, ", |, ', or ). It is not a grouped quote choice in the way the pattern suggests.
[(\s|.)]* is a character class repeated zero or more times. It can only consume the literal characters represented inside the class; it does not mean (\s|.)*.
[$1] is a character class matching $ or 1. It is not equivalent to \1 and does not refer back to the previously matched quote.
Therefore, normal javascript: attributes such as action="javascript:alert('XSS')" can bypass the filter.
Hi, I’d like to report a potential XSS issue in the ViewSource plugin’s regex-based filtering logic.
Summary
dijit/_editor/plugins/ViewSource.jsattempts to remove tags containingjavascript:URLs in_stripScripts, but the regular expression is malformed. The expression appears to intend to match quotedjavascript:attribute values and then use the opening quote as a backreference, but it does not do that.As a result, a dangerous form action such as
action="javascript:alert('XSS')"can remain in the editor content and execute when the form is submitted.Affected Code
File:
dijit/_editor/plugins/ViewSource.jsThere are four key regex mistakes:
[("|')]is a character class. It does not mean "match either a double quote or a single quote". It matches one character from the set(,",|,', or).[^$1]is a negated character class. It is not related to capture group 1. It only means "match one character that is not$and not1.[(\s|.)]is a character class. It does not mean "match whitespace or any character". It only matches one character from the set(,\s,|,., or).[$1]is also a character class. It is not a backreference to capture group 1. It only matches either$or1.Because of these mistakes, the regex does not reliably match normal quoted
javascript:attribute values.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, wherestripScriptsis enabled.<form action="javascript:alert('XSS')">payload remains present.alert('XSS')executes.Expected Result
When
stripScriptsis enabled, dangerousjavascript:URL attributes should be removed or neutralized. The following payload should not remain executable:Actual Result
The payload is not removed by the
_stripScriptsregex. Thejavascript:form action remains in the editor content and executes when the form is submitted.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
javascript:URL 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 regex is written as:
/<[^>]*=(\s|)*[("|')]javascript:[^$1][(\s|.)]*[$1][^>]*>/igThe intended behavior appears to be:
javascript:,But the actual behavior is different:
[("|')]is a character class. It matches one character among(,",|,', or). It is not a grouped quote choice in the way the pattern suggests.[(\s|.)]*is a character class repeated zero or more times. It can only consume the literal characters represented inside the class; it does not mean(\s|.)*.[$1]is a character class matching$or1. It is not equivalent to\1and does not refer back to the previously matched quote.Therefore, normal
javascript:attributes such asaction="javascript:alert('XSS')"can bypass the filter.