Page MenuHomePhabricator

Stored XSS through a system message in Cite
Closed, ResolvedPublicSecurity

Description

The cite-ve-dialog-subreference-help-dialog-content message is inserted as raw HTML by the Cite extension, allowing for stored XSS.

Reproduction steps

  1. Install Cite and VisualEditor and make sure $wgUseXssLanguage is set to true
  2. Create a page called "CiteVETest" with the following contents using source editor: <ref name=":0">Test</ref><ref name=":0" /> (the vulnerability is also reproducible just by using VE, but this is easier)
  3. Go to /wiki/CiteVETest?veaction=edit&uselang=x-xss
  4. Wait for VE to load and left click on the second [1]
  5. Click the button at the bottom of the modal (called "Add details" normally)

image.png (431×180 px, 12 KB)

Cause

https://gerrit.wikimedia.org/r/c/mediawiki/extensions/Cite/+/1182871 (merged last week)
There even is a FIXME comment regarding the issue (but apparently the author wasn't aware that this is a vulnerability):
https://gerrit.wikimedia.org/r/plugins/gitiles/mediawiki/extensions/Cite/+blame/314bd0681799af1b40eeea096d76b7f5365846ef/modules/ve-cite/ve.ui.MWSubReferenceHelpDialog.js#58

Additional information

Details

Risk Rating
Low
Author Affiliation
Wikimedia Communities
Related Changes in Gerrit:

Event Timeline

			// FIXME: Can we use .parseDom() or something similar?
			.html( mw.msg( 'cite-ve-dialog-subreference-help-dialog-content' ) ),

You can indeed use .parseDom(), but this will require some small tweaks to the message to use simple HTML, see https://www.mediawiki.org/wiki/Manual:Messages_API#Feature_support_in_JavaScript and https://www.mediawiki.org/wiki/Manual:Messages_API#Use_of_the_messages

Alternatively, you can ship messages parsed with the normal parser as part of a ResourceLoader module, e.g. like this: extension.json and callback (as long as the message doesn't need any parameters).

(I CC'd folks who were involved in the patch, feel free to CC anyone else on the team who wants to be able to view the task)

Thanks for bringing this up. We are aware that this can be an issue. That's why we added that FIXME. 🙈️ However, we figured it currently does have a low priority, because:

  • Only interface admins can use this attack vector.
  • The element is still behind a feature flag and not deployed outside of the beta cluster.

We will prioritize this and fix it before deployment.

  • Only interface admins can use this attack vector.

The reason this is a vulnerability is that sysops can edit system messages (editinterface) but not CSS/JS (editsitecss/editsitejs). Inserting a system message as HTML allows sysops to insert arbitrary HTML, JS and CSS into the page, which they should not be able to, so this essentially results in privilege escalation.

  • The element is still behind a feature flag and not deployed outside of the beta cluster.

It will eventually land in a release branch and third party users would be affected by the vulnerability as well if they enabled it.

Here is a minimal fix. As far as I can see this needs backporting to MW-1.45-notes (1.45.0-wmf.19; 2025-09-16) and master, but that's it.

diff --git modules/ve-cite/i18n/en.json modules/ve-cite/i18n/en.json
index acc2f18d..267ed399 100644
--- modules/ve-cite/i18n/en.json
+++ modules/ve-cite/i18n/en.json
@@ -54 +54 @@
-       "cite-ve-dialog-subreference-help-dialog-content": "When re-using a reference you can now add different details such as a page number or quote without repeating the full reference.<br><br><strong>Use it when:</strong><ul><li>Citing different pages or volumes of a book</li><li>Specifiying a timestamp in a cited podcast</li><li>Adding a relevant quote from a source</li></ul>",
+       "cite-ve-dialog-subreference-help-dialog-content": "When re-using a reference you can now add different details such as a page number or quote without repeating the full reference.<p><b>Use it when:</b></p><ul><li>Citing different pages or volumes of a book</li><li>Specifiying a timestamp in a cited podcast</li><li>Adding a relevant quote from a source</li></ul>",
diff --git modules/ve-cite/ve.ui.MWSubReferenceHelpDialog.js modules/ve-cite/ve.ui.MWSubReferenceHelpDialog.js
index 3a93cd59..e2aa4631 100644
--- modules/ve-cite/ve.ui.MWSubReferenceHelpDialog.js
+++ modules/ve-cite/ve.ui.MWSubReferenceHelpDialog.js
@@ -58,2 +58 @@ ve.ui.MWSubReferenceHelpDialog.prototype.initialize = function () {
-                       // FIXME: Can we use .parseDom() or something similar?
-                       .html( mw.msg( 'cite-ve-dialog-subreference-help-dialog-content' ) ),
+                       .append( mw.message( 'cite-ve-dialog-subreference-help-dialog-content' ).parseDom() ),

Question: Is this low risk and can go directly to Gerrit?

Question: Is this low risk and can go directly to Gerrit?

Technically Cite is bundled, but I'm personally fine with pushing this through gerrit given the rationale from T404651#11183719. Unless @Reedy has any objections. But I'd prefer we merge it and then pick it to a production branch and get it deployed sooner than later. i.e. don't let it sit over the weekend if we can avoid that.

Change #1189471 had a related patch set uploaded (by Thiemo Kreuz (WMDE); author: Thiemo Kreuz (WMDE)):

[mediawiki/extensions/Cite@master] Use sanitized HTML in (undeployed) reuse dialog

https://gerrit.wikimedia.org/r/1189471

Change #1189471 merged by jenkins-bot:

[mediawiki/extensions/Cite@master] Use sanitized HTML in (undeployed) reuse dialog

https://gerrit.wikimedia.org/r/1189471

sbassett changed the visibility from "Custom Policy" to "Public (No Login Required)".Oct 3 2025, 6:09 PM
sbassett changed the edit policy from "Custom Policy" to "All Users".
sbassett changed Risk Rating from N/A to Low.