Page MenuHomePhabricator

0001-SECURITY-Fix-XSS-vulnerability.patch

Authored By
Lucas_Werkmeister_WMDE
Mar 7 2025, 3:28 PM
Size
7 KB
Referenced Files
None
Subscribers
None

0001-SECURITY-Fix-XSS-vulnerability.patch

From 9cc4b19340783d4f5b4f0cd09bc3cdc340616c21 Mon Sep 17 00:00:00 2001
From: Matthias Mullie <git@mullie.eu>
Date: Fri, 7 Mar 2025 11:39:46 +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: I18122bf29a53b963f0919051abe0e9254a4c4db2
---
resources/filepage/StatementPanel.js | 4 ++--
resources/filepage/init.js | 21 +++++++++++++------
.../inputs/EntityAutocompleteInputWidget.js | 2 +-
src/View/MediaInfoEntityStatementsView.php | 11 +++-------
...yAutocompleteInputWidgetLabel.mustache+dom | 2 +-
5 files changed, 22 insertions(+), 18 deletions(-)
diff --git a/resources/filepage/StatementPanel.js b/resources/filepage/StatementPanel.js
index 07445fbf3b..cac0bdb49b 100644
--- a/resources/filepage/StatementPanel.js
+++ b/resources/filepage/StatementPanel.js
@@ -37,8 +37,8 @@ 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 3eed8a5f65..ec4d44de8a 100644
--- a/resources/filepage/init.js
+++ b/resources/filepage/init.js
@@ -175,7 +175,7 @@
mw.hook( 'wikipage.content' ).add( function ( $content ) {
var linkNoticeWidget = new LinkNoticeWidget(),
protectionMsgWidget = new ProtectionMsgWidget(),
- $statements = $content.find( '.wbmi-entityview-statementsGroup' ),
+ $statements = $content.find( '.wbmi-structured-data-header ~ .wbmi-entityview-statementsGroup' ),
existingProperties = defaultProperties.concat( Object.keys( mediaInfoEntity.statements || {} ) ),
deserializer = new StatementListDeserializer(),
tabs,
@@ -200,7 +200,7 @@
$content.find( '.wbmi-tabs-container' ).first().before( protectionMsgWidget.$element );
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,10 +212,19 @@
}
// Set up existing statement panels
- existingStatementPanels = $statements.get().map( function ( element ) {
- var $statement = $( element ),
- propId = $statement.data( 'property' ),
- statementsJson = JSON.parse( $statement.attr( 'data-statements' ) || '[]' ),
+ existingStatementPanels = $statements.get().map( ( element ) => {
+ const $statement = $( element ),
+ 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 a09c865b39..7b76bd59de 100644
--- a/resources/statements/inputs/EntityAutocompleteInputWidget.js
+++ b/resources/statements/inputs/EntityAutocompleteInputWidget.js
@@ -216,7 +216,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 0e6df996da..2ca522ef69 100644
--- a/src/View/MediaInfoEntityStatementsView.php
+++ b/src/View/MediaInfoEntityStatementsView.php
@@ -261,9 +261,9 @@ private function getLayoutForProperty( $propertyIdString, array $statements ) {
] );
$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 @@ private function innerStatementDiv( Statement $statement ): Tag {
$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 7320a630f0..7795747737 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.48.1

File Metadata

Mime Type
text/x-diff
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
19410025
Default Alt Text
0001-SECURITY-Fix-XSS-vulnerability.patch (7 KB)

Event Timeline