Page Menu
Home
Phabricator
Search
Configure Global Search
Log In
Files
F58744907
T387691.patch
matthiasmullie (Matthias Mullie)
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Authored By
matthiasmullie
Mar 10 2025, 8:37 AM
2025-03-10 08:37:06 (UTC+0)
Size
6 KB
Referenced Files
None
Subscribers
None
T387691.patch
View Options
From 30b7f71fc9a2bc7cba8d7363bffa013fa74c7f1b Mon Sep 17 00:00:00 2001
From: Matthias Mullie <git@mullie.eu>
Date: Mon, 10 Mar 2025 09:21:48 +0100
Subject: [PATCH] SECURITY: Fix XSS vulnerability
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Attributes prefixed data-mw- are covered by Sanitizer.php and should be
safe to use. Others are not safe, as they can also be used in Wikitext.
For data-(mw-)property and data-(mw-)statements, fall back to the old
attribute value for cached cases where the new ones are missing. This is
hopefully relatively safe – Wikitext with custom values for these
attributes may be able to cause confusing on-page behavior, but
hopefully not code execution. To further safeguard this, make the
selector more precise – this should hopefully cut out most
user-generated content (though discussion on the task is inconclusive,
as of this writing, whether it’s fully enough or not).
For data-(mw-)formatvalue, don’t fall back to the old values. Values of
this attribute are directly used as HTML and are thus highly unsafe; if
we can’t use them, the wbformatvalue cache should just fall back to
getting them from the API, which is slightly less efficient but safe.
Bug: T387691
Change-Id: Ie969a8cfeab0d4457417773fa884e271968e5657
---
resources/filepage/StatementPanel.js | 4 ++--
resources/filepage/init.js | 17 +++++++++++++----
.../inputs/EntityAutocompleteInputWidget.js | 2 +-
src/View/MediaInfoEntityStatementsView.php | 11 +++--------
...ityAutocompleteInputWidgetLabel.mustache+dom | 2 +-
5 files changed, 20 insertions(+), 16 deletions(-)
diff --git a/resources/filepage/StatementPanel.js b/resources/filepage/StatementPanel.js
index 4bbc1e6d..40dc1cca 100644
--- a/resources/filepage/StatementPanel.js
+++ b/resources/filepage/StatementPanel.js
@@ -36,8 +36,8 @@ const StatementPanel = function StatementPanelConstructor( config ) {
// Mixin constructors
OO.ui.mixin.PendingElement.call( this, this.config );
- if ( this.$element.attr( 'data-formatvalue' ) ) {
- this.populateFormatValueCache( JSON.parse( this.$element.attr( 'data-formatvalue' ) || '{}' ) );
+ if ( this.$element.attr( 'data-mw-formatvalue' ) ) {
+ this.populateFormatValueCache( JSON.parse( this.$element.attr( 'data-mw-formatvalue' ) || '{}' ) );
}
this.licenseDialogWidget = new LicenseDialogWidget();
diff --git a/resources/filepage/init.js b/resources/filepage/init.js
index 9bcce6c8..236523ed 100644
--- a/resources/filepage/init.js
+++ b/resources/filepage/init.js
@@ -174,7 +174,7 @@
mw.hook( 'wikipage.content' ).add( ( $content ) => {
const linkNoticeWidget = new LinkNoticeWidget();
const protectionMsgWidget = new ProtectionMsgWidget();
- const $statements = $content.find( '.wbmi-entityview-statementsGroup' );
+ const $statements = $content.find( '.wbmi-structured-data-header ~ .wbmi-entityview-statementsGroup' );
const existingProperties = defaultProperties.concat( Object.keys( mediaInfoEntity.statements || {} ) );
const deserializer = new StatementListDeserializer();
@@ -198,7 +198,7 @@
$content.find( '.wbmi-tabs-container' ).first().before( protectionMsgWidget.$element );
const captionsPanel = createCaptionsPanel();
// eslint-disable-next-line no-jquery/no-global-selector
- $( '.wbmi-entityview-captionsPanel' ).replaceWith( captionsPanel.$element );
+ $( '.wbmi-captions-header ~ .wbmi-entityview-captionsPanel' ).replaceWith( captionsPanel.$element );
// Add link notice widget and add property button if they don't exist.
if ( $statements.first().hasClass( 'wbmi-entityview-statementsGroup' ) ) {
@@ -212,8 +212,17 @@
// Set up existing statement panels
const existingStatementPanels = $statements.get().map( ( element ) => {
const $statement = $( element ),
- propId = $statement.data( 'property' ),
- statementsJson = JSON.parse( $statement.attr( 'data-statements' ) || '[]' ),
+ propId = $statement.data( 'mw-property' ) ||
+ // Fallback for when this attribute was named differently
+ // @see https://phabricator.wikimedia.org/T387691
+ $statement.data( 'property' ),
+ statementsJson = JSON.parse(
+ $statement.attr( 'data-mw-statements' ) ||
+ // Fallback for when this attribute was named differently
+ // @see https://phabricator.wikimedia.org/T387691
+ $statement.attr( 'data-statements' ) ||
+ '[]'
+ ),
data = deserializer.deserialize( statementsJson ),
statementPanel = createStatementsPanel(
$statement,
diff --git a/resources/statements/inputs/EntityAutocompleteInputWidget.js b/resources/statements/inputs/EntityAutocompleteInputWidget.js
index bc5bbce0..211b6d8b 100644
--- a/resources/statements/inputs/EntityAutocompleteInputWidget.js
+++ b/resources/statements/inputs/EntityAutocompleteInputWidget.js
@@ -211,7 +211,7 @@ EntityAutocompleteInputWidget.prototype.onMousedown = function ( e ) {
// window.open. This is a response to a mousedown event so it shouldn't
// trigger any popup blockers in modern browsers. For browsers set to
// prefer new tabs over new windows, this will open in a new tab.
- window.open( e.currentTarget.dataset.url, '_blank' );
+ window.open( e.currentTarget.dataset.mwUrl, '_blank' );
}
};
diff --git a/src/View/MediaInfoEntityStatementsView.php b/src/View/MediaInfoEntityStatementsView.php
index 24a3e224..f24322b4 100644
--- a/src/View/MediaInfoEntityStatementsView.php
+++ b/src/View/MediaInfoEntityStatementsView.php
@@ -261,9 +261,9 @@ class MediaInfoEntityStatementsView {
] );
$panel->setAttributes(
[
- 'data-property' => $propertyIdString,
- 'data-statements' => json_encode( $serializedStatements ),
- 'data-formatvalue' => json_encode( $formatValueCache ),
+ 'data-mw-property' => $propertyIdString,
+ 'data-mw-statements' => json_encode( $serializedStatements ),
+ 'data-mw-formatvalue' => json_encode( $formatValueCache ),
]
);
return $panel;
@@ -350,11 +350,6 @@ class MediaInfoEntityStatementsView {
$statementDiv = new Tag( 'div' );
$statementDiv->addClasses( [ 'wbmi-item-container' ] );
- $guid = $statement->getGuid();
- if ( $guid !== null ) {
- $statementDiv->setAttributes( [ 'data-guid' => $guid ] );
- }
-
$mainSnakDiv = new Tag( 'div' );
$mainSnakDiv->addClasses( [ 'wbmi-entity-header' ] );
$mainSnakDiv->appendContent(
diff --git a/templates/statements/inputs/EntityAutocompleteInputWidgetLabel.mustache+dom b/templates/statements/inputs/EntityAutocompleteInputWidgetLabel.mustache+dom
index 7320a630..77957477 100644
--- a/templates/statements/inputs/EntityAutocompleteInputWidgetLabel.mustache+dom
+++ b/templates/statements/inputs/EntityAutocompleteInputWidgetLabel.mustache+dom
@@ -1,4 +1,4 @@
-<span class="wbmi-autocomplete-option" data-url={{url}}>
+<span class="wbmi-autocomplete-option" data-mw-url={{url}}>
<span class="wbmi-autocomplete-option__label">
{{label}}
</span>
--
2.34.1
File Metadata
Details
Attached
Mime Type
text/x-diff
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
19441232
Default Alt Text
T387691.patch (6 KB)
Attached To
Mode
T387691: CVE-2025-32069: Wikitext stored XSS on filepages due to dangerous WBMI serialization
Attached
Detach File
Event Timeline
Log In to Comment