jenkins-bot has submitted this change and it was merged.
Change subject: Replace pipes in input before feeding them into a template
......................................................................
Replace pipes in input before feeding them into a template
Bug: T140901
Change-Id: I0c01db0a3d79f060173d88f10ca80c9fbc44a2fb
---
M extension.json
M resources/details/uw.DescriptionDetailsWidget.js
A resources/mw.Escaper.js
M resources/mw.UploadWizardDetails.js
4 files changed, 157 insertions(+), 3 deletions(-)
Approvals:
MarkTraceur: Looks good to me, approved
jenkins-bot: Verified
diff --git a/extension.json b/extension.json
index d6e5c60..ad51989 100644
--- a/extension.json
+++ b/extension.json
@@ -172,6 +172,7 @@
"mediawiki.api.messages",
"mediawiki.api.parse",
"mediawiki.confirmCloseWindow",
+ "mediawiki.RegExp",
"mediawiki.Title",
"mediawiki.user",
"mediawiki.feedback",
@@ -213,6 +214,7 @@
"resources/mw.units.js",
"resources/mw.canvas.js",
"resources/mw.errorDialog.js",
+ "resources/mw.Escaper.js",
"resources/mw.DestinationChecker.js",
"resources/mw.QuickTitleChecker.js",
"resources/mw.Firefogg.js",
diff --git a/resources/details/uw.DescriptionDetailsWidget.js
b/resources/details/uw.DescriptionDetailsWidget.js
index c105da6..63e7680 100644
--- a/resources/details/uw.DescriptionDetailsWidget.js
+++ b/resources/details/uw.DescriptionDetailsWidget.js
@@ -154,7 +154,7 @@
language =
mw.UploadWizard.config.languageTemplateFixups[ language ];
}
- return '{{' + language + '|1=' + description + '}}';
+ return '{{' + language + '|1=' + mw.Escaper.escapeForTemplate(
description ) + '}}';
};
/**
diff --git a/resources/mw.Escaper.js b/resources/mw.Escaper.js
new file mode 100644
index 0000000..cd7d01e
--- /dev/null
+++ b/resources/mw.Escaper.js
@@ -0,0 +1,151 @@
+( function ( mw, OO ) {
+ mw.Escaper = {
+ /**
+ * Escapes wikitext for use inside {{templates}}.
+ *
+ * @param {string} wikitext
+ * @return {string}
+ */
+ escapeForTemplate: function ( wikitext ) {
+ return this.escapePipes( wikitext );
+ },
+
+ /**
+ * Escapes pipe characters, which could be problematic when the
content is
+ * inserted in a template.
+ *
+ * @param {string} wikitext
+ * @return {string}
+ */
+ escapePipes: function ( wikitext ) {
+ var extractedTemplates, extractedLinks;
+
+ // Pipes (`|`) must be escaped because we'll be
inserting this
+ // content into a templates & pipes would mess up the
syntax.
+ // First, urlencode pipes inside links:
+ wikitext = wikitext.replace( /\bhttps?:\/\/[^\s]+/g,
function ( match ) {
+ return match.replace( /\|/g, '%7C' );
+ } );
+
+ // Second, pipes can be valid inside other templates or
links in
+ // wikitext, so we'll first extract those from the
content, then
+ // replace the pipes, then restore the original
(extracted) content:
+ extractedTemplates = this.extractTemplates( wikitext );
+ extractedLinks = this.extractLinks( extractedTemplates[
0 ] );
+ wikitext = extractedLinks[ 0 ].replace( /\|/g, '{{!}}'
);
+ return this.restoreExtracts( wikitext, $.extend(
extractedTemplates[ 1 ], extractedLinks[ 1 ] ) );
+ },
+
+ /**
+ * Extract all {{templates}} from wikitext, replacing them with
+ * placeholder content in the form of {{1}}, {{2}}.
+ *
+ * Nested templates will safely be extracted by first replacing
inner
+ * templates, then moving outwards, ensuring we don't get
closing
+ * bracket mismatches.
+ *
+ * Restoring the content is as simple as feeding the returned
content &
+ * replacements back into this.restoreExtracts.
+ *
+ * @param {string} wikitext
+ * @return {array} [{string} wikitext, {Object} replacements]
+ */
+ extractTemplates: function ( wikitext ) {
+ var extracts = {},
+ previousExtracts = {},
+ extracted = wikitext,
+ // the regex explained:
+ // * `[^\{]`: character can not be {
+ // * `\{(?!\{)`: or if it is, it can't be
followed by another {
+ // this excludes template opening brackets: {{
+ // * `\{\{[0-9]+\}\}`: unless it's a complete
{{[0-9]+}}
+ // sequence, generated by an earlier run of
this regex
+ regex =
/\{\{([^\{]|\{(?!\{)|\{\{[0-9]+\}\})*?\}\}/g,
+ callback = function ( match ) {
+ var replacement = '{{' + Object.keys(
extracts ).length + '}}';
+
+ // safeguard for not replacing
already-replaced matches
+ // this makes sure that when real
content contains something
+ // like {{1}}, it will still be
replaced, while {{1}}
+ // generated by this code can be
recognized & ignored
+ if ( match in previousExtracts ) {
+ return match;
+ }
+
+ extracts[ replacement ] = match;
+ return replacement;
+ };
+
+ do {
+ wikitext = extracted;
+ previousExtracts = OO.copy( extracts );
+ extracted = wikitext.replace( regex, callback );
+ } while ( wikitext !== extracted );
+
+ return [ wikitext, extracts ];
+ },
+
+ /**
+ * Extract all [[links]] from wikitext, replacing them with
placeholder
+ * content in the form of [[1]], [[2]].
+ *
+ * Restoring the content is as simple as feeding the returned
content &
+ * replacements back into this.restoreExtracts.
+ *
+ * @param {string} wikitext
+ * @return {array} [{string} wikitext, {Object} replacements]
+ */
+ extractLinks: function ( wikitext ) {
+ var extracts = {};
+
+ wikitext = wikitext.replace( /\[\[.*?\]\]/g, function (
match ) {
+ var replacement = '[[' + Object.keys( extracts
).length + ']]';
+ extracts[ replacement ] = match;
+ return replacement;
+ } );
+
+ return [ wikitext, extracts ];
+ },
+
+ /**
+ * Restores content that was extracted from wikitext.
+ *
+ * @param {string} wikitext
+ * @param {Object} replacements
+ * @return {string}
+ */
+ restoreExtracts: function ( wikitext, replacements ) {
+ // turn search keys into a regular expression, allowing
us to match
+ // all of them at once
+ var searchValues = Object.keys( replacements ).map(
mw.RegExp.escape ),
+ searchRegex = new RegExp( '(' +
searchValues.join( '|' ) + ')', 'g' ),
+ callback = function ( match ) {
+ var replacement = replacements[ match ];
+
+ // we matched something that has no
replacement, must be valid
+ // user input that just happens to look
like on of the
+ // replacement values
+ if ( replacement === undefined ) {
+ return match;
+ }
+
+ // if we find the replacement itself
matches a search value, we
+ // also don't want to go recursive:
nesting doesn't work like
+ // that, it's just a coincidence where
user input happens to
+ // look just like a replacement value
(e.g. `{{1}}`)
+ if ( replacement in replacements ) {
+ return replacement;
+ }
+
+ // we must not replace this one again,
to avoid getting stuck in
+ // endless recursion
+ delete replacements[ match ];
+
+ // go recursive, there may be more
replacements nested down there
+ return this.restoreExtracts(
replacement, replacements );
+ }.bind( this );
+
+ return wikitext.replace( searchRegex, callback );
+ }
+ };
+} )( mediaWiki, OO );
diff --git a/resources/mw.UploadWizardDetails.js
b/resources/mw.UploadWizardDetails.js
index 2f01f34..efc62f8 100644
--- a/resources/mw.UploadWizardDetails.js
+++ b/resources/mw.UploadWizardDetails.js
@@ -304,7 +304,7 @@
getThumbnailCaption: function () {
var descriptions =
this.descriptionsDetails.getSerialized().descriptions;
if ( descriptions.length > 0 ) {
- return descriptions[ 0 ].description.trim();
+ return mw.Escaper.escapeForTemplate(
descriptions[ 0 ].description.trim() );
} else {
return '';
}
@@ -661,7 +661,8 @@
info = '';
for ( key in information ) {
- info += '|' + key.replace( /:/g, '_' ) + '=' +
information[ key ] + '\n';
+ info += '|' + key.replace( /:/g, '_' );
+ info += '=' + mw.Escaper.escapeForTemplate(
information[ key ] ) + '\n';
}
wikiText += '=={{int:filedesc}}==\n';
--
To view, visit https://gerrit.wikimedia.org/r/307115
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings
Gerrit-MessageType: merged
Gerrit-Change-Id: I0c01db0a3d79f060173d88f10ca80c9fbc44a2fb
Gerrit-PatchSet: 12
Gerrit-Project: mediawiki/extensions/UploadWizard
Gerrit-Branch: master
Gerrit-Owner: Matthias Mullie <[email protected]>
Gerrit-Reviewer: Bartosz DziewoĆski <[email protected]>
Gerrit-Reviewer: Dereckson <[email protected]>
Gerrit-Reviewer: MarkTraceur <[email protected]>
Gerrit-Reviewer: Matthias Mullie <[email protected]>
Gerrit-Reviewer: jenkins-bot <>
_______________________________________________
MediaWiki-commits mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits