dojo / dojo/dijit

Potential XSS in ViewSource stripScripts due to malformed javascript: URL filtering regex

Open
#209 1 comment 0 reactions 0 assignees View on GitHub
Dominant language
HTML
Stars
172
Forks
180
PR merge metrics
No merged PRs in 30d

Description

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`

```js
html = html.replace(/<[^>]*=(\s|)*[("|')]javascript:[^$1][(\s|.)]*[$1][^>]*>/ig, "");
```

There are four key regex mistakes:

1. `[("|')]` 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 `)`.
2. `[^$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`.
3. `[(\s|.)]` is a character class. It does not mean "match whitespace or any character". It only matches one character from the set `(`, `\s`, `|`, `.`, or `)`.
4. `[$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:

```html



ViewSource Plugin with stripScripts javascript URL bypass PoC


Steps to trigger the issue when stripScripts is enabled:



  1. Toggle to View Source, ensure the payload remains unchanged, then toggle back.

  2. Submit the rendered form to observe alert execution.






```

## 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 `stripScripts` is enabled.
4. Toggle View Source mode on.
5. Confirm the `` payload remains present.
6. Toggle View Source mode off.
7. Submit the rendered form.
8. `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:

```html

```

## 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:

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, 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:

```js
/<[^>]*=(\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.

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.