Commit 39c5a432 authored by Oliver Bartsch's avatar Oliver Bartsch Committed by Oliver Hader
Browse files

[SECURITY] Mitigate XSS in viewpage

The `viewpage` module contains a preset selection, where
users can select different browser viewports. Since the
corresponding preset labels, configurable via TSconfig,
had not been encoded properly, is was vulnerable to XSS.

The issue is addressed by properly encoding the labels.

Resolves: #93702
Releases: master, 11.3, 10.4, 9.5
Change-Id: Ia22c5ab4332816614dd07a93d7e739d9fc1d8bac
Security-Bulletin: CORE-SA-2021-009
Security-References: CVE-2021-32667
Reviewed-on: https://review.typo3.org/c/Packages/TYPO3.CMS/+/69991

Tested-by: Oliver Hader's avatarOliver Hader <oliver.hader@typo3.org>
Reviewed-by: Oliver Hader's avatarOliver Hader <oliver.hader@typo3.org>
parent 7fbe487a
......@@ -14,6 +14,7 @@
import $ from 'jquery';
import 'jquery-ui/resizable';
import PersistentStorage = require('TYPO3/CMS/Backend/Storage/Persistent');
import SecurityUtility = require('TYPO3/CMS/Core/SecurityUtility');
enum Selectors {
resizableContainerIdentifier = '.t3js-viewpage-resizeable',
......@@ -58,7 +59,7 @@ class ViewPage {
}
private static setLabel(label: string): void {
$(Selectors.currentLabelSelector).html(label);
$(Selectors.currentLabelSelector).html((new SecurityUtility()).encodeHtml(label));
}
private static getCurrentLabel(): string {
......
......@@ -10,4 +10,4 @@
*
* The TYPO3 project - inspiring people to share!
*/
var __importDefault=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};define(["require","exports","jquery","TYPO3/CMS/Backend/Storage/Persistent","jquery-ui/resizable"],(function(e,t,i,s){"use strict";var r;i=__importDefault(i),function(e){e.resizableContainerIdentifier=".t3js-viewpage-resizeable",e.sizeIdentifier=".t3js-viewpage-size",e.moduleBodySelector=".t3js-module-body",e.customSelector=".t3js-preset-custom",e.customWidthSelector=".t3js-preset-custom",e.customHeightSelector=".t3js-preset-custom-height",e.changeOrientationSelector=".t3js-change-orientation",e.changePresetSelector=".t3js-change-preset",e.inputWidthSelector=".t3js-viewpage-input-width",e.inputHeightSelector=".t3js-viewpage-input-height",e.currentLabelSelector=".t3js-viewpage-current-label",e.topbarContainerSelector=".t3js-viewpage-topbar"}(r||(r={}));class a{constructor(){this.defaultLabel="",this.minimalHeight=300,this.minimalWidth=300,this.storagePrefix="moduleData.web_view.States.",this.queue=[],this.queueIsRunning=!1,i.default(()=>{const e=i.default(".t3js-preset-custom-label");this.defaultLabel=e.length>0?e.html().trim():"",this.$iframe=i.default("#tx_this_iframe"),this.$resizableContainer=i.default(r.resizableContainerIdentifier),this.$sizeSelector=i.default(r.sizeIdentifier),this.initialize()})}static getCurrentWidth(){return i.default(r.inputWidthSelector).val()}static getCurrentHeight(){return i.default(r.inputHeightSelector).val()}static setLabel(e){i.default(r.currentLabelSelector).html(e)}static getCurrentLabel(){return i.default(r.currentLabelSelector).html().trim()}persistQueue(){if(!1===this.queueIsRunning&&this.queue.length>=1){this.queueIsRunning=!0;let e=this.queue.shift();s.set(e.storageIdentifier,e.data).done(()=>{this.queueIsRunning=!1,this.persistQueue()})}}addToQueue(e,t){const i={storageIdentifier:e,data:t};this.queue.push(i),this.queue.length>=1&&this.persistQueue()}setSize(e,t){isNaN(t)&&(t=this.calculateContainerMaxHeight()),t<this.minimalHeight&&(t=this.minimalHeight),isNaN(e)&&(e=this.calculateContainerMaxWidth()),e<this.minimalWidth&&(e=this.minimalWidth),i.default(r.inputWidthSelector).val(e),i.default(r.inputHeightSelector).val(t),this.$resizableContainer.css({width:e,height:t,left:0})}persistCurrentPreset(){let e={width:a.getCurrentWidth(),height:a.getCurrentHeight(),label:a.getCurrentLabel()};this.addToQueue(this.storagePrefix+"current",e)}persistCustomPreset(){let e={width:a.getCurrentWidth(),height:a.getCurrentHeight()};i.default(r.customSelector).data("width",e.width),i.default(r.customSelector).data("height",e.height),i.default(r.customWidthSelector).html(e.width),i.default(r.customHeightSelector).html(e.height),this.addToQueue(this.storagePrefix+"custom",e)}persistCustomPresetAfterChange(){clearTimeout(this.queueDelayTimer),this.queueDelayTimer=window.setTimeout(()=>{this.persistCustomPreset()},1e3)}initialize(){i.default(document).on("click",r.changeOrientationSelector,()=>{const e=i.default(r.inputHeightSelector).val(),t=i.default(r.inputWidthSelector).val();this.setSize(e,t),this.persistCurrentPreset()}),i.default(document).on("change",r.inputWidthSelector,()=>{const e=i.default(r.inputWidthSelector).val(),t=i.default(r.inputHeightSelector).val();this.setSize(e,t),a.setLabel(this.defaultLabel),this.persistCustomPresetAfterChange()}),i.default(document).on("change",r.inputHeightSelector,()=>{const e=i.default(r.inputWidthSelector).val(),t=i.default(r.inputHeightSelector).val();this.setSize(e,t),a.setLabel(this.defaultLabel),this.persistCustomPresetAfterChange()}),i.default(document).on("click",r.changePresetSelector,e=>{const t=i.default(e.currentTarget).data();this.setSize(parseInt(t.width,10),parseInt(t.height,10)),a.setLabel(t.label),this.persistCurrentPreset()}),this.$resizableContainer.resizable({handles:"w, sw, s, se, e"}),this.$resizableContainer.on("resizestart",e=>{i.default(e.currentTarget).append('<div id="this-iframe-cover" style="z-index:99;position:absolute;width:100%;top:0;left:0;height:100%;"></div>')}),this.$resizableContainer.on("resize",(e,t)=>{t.size.width=t.originalSize.width+2*(t.size.width-t.originalSize.width),t.size.height<this.minimalHeight&&(t.size.height=this.minimalHeight),t.size.width<this.minimalWidth&&(t.size.width=this.minimalWidth),i.default(r.inputWidthSelector).val(t.size.width),i.default(r.inputHeightSelector).val(t.size.height),this.$resizableContainer.css({left:0}),a.setLabel(this.defaultLabel)}),this.$resizableContainer.on("resizestop",()=>{i.default("#viewpage-iframe-cover").remove(),this.persistCurrentPreset(),this.persistCustomPreset()})}calculateContainerMaxHeight(){this.$resizableContainer.hide();let e=i.default(r.moduleBodySelector),t=e.outerHeight()-e.height(),s=i.default(document).height(),a=i.default(r.topbarContainerSelector).outerHeight();return this.$resizableContainer.show(),s-t-a-8}calculateContainerMaxWidth(){this.$resizableContainer.hide();let e=i.default(r.moduleBodySelector),t=e.outerWidth()-e.width(),s=i.default(document).width();return this.$resizableContainer.show(),parseInt(s-t+"",10)}}return new a}));
\ No newline at end of file
var __importDefault=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};define(["require","exports","jquery","TYPO3/CMS/Backend/Storage/Persistent","TYPO3/CMS/Core/SecurityUtility","jquery-ui/resizable"],(function(e,t,i,s,r){"use strict";var a;i=__importDefault(i),function(e){e.resizableContainerIdentifier=".t3js-viewpage-resizeable",e.sizeIdentifier=".t3js-viewpage-size",e.moduleBodySelector=".t3js-module-body",e.customSelector=".t3js-preset-custom",e.customWidthSelector=".t3js-preset-custom",e.customHeightSelector=".t3js-preset-custom-height",e.changeOrientationSelector=".t3js-change-orientation",e.changePresetSelector=".t3js-change-preset",e.inputWidthSelector=".t3js-viewpage-input-width",e.inputHeightSelector=".t3js-viewpage-input-height",e.currentLabelSelector=".t3js-viewpage-current-label",e.topbarContainerSelector=".t3js-viewpage-topbar"}(a||(a={}));class l{constructor(){this.defaultLabel="",this.minimalHeight=300,this.minimalWidth=300,this.storagePrefix="moduleData.web_view.States.",this.queue=[],this.queueIsRunning=!1,i.default(()=>{const e=i.default(".t3js-preset-custom-label");this.defaultLabel=e.length>0?e.html().trim():"",this.$iframe=i.default("#tx_this_iframe"),this.$resizableContainer=i.default(a.resizableContainerIdentifier),this.$sizeSelector=i.default(a.sizeIdentifier),this.initialize()})}static getCurrentWidth(){return i.default(a.inputWidthSelector).val()}static getCurrentHeight(){return i.default(a.inputHeightSelector).val()}static setLabel(e){i.default(a.currentLabelSelector).html((new r).encodeHtml(e))}static getCurrentLabel(){return i.default(a.currentLabelSelector).html().trim()}persistQueue(){if(!1===this.queueIsRunning&&this.queue.length>=1){this.queueIsRunning=!0;let e=this.queue.shift();s.set(e.storageIdentifier,e.data).done(()=>{this.queueIsRunning=!1,this.persistQueue()})}}addToQueue(e,t){const i={storageIdentifier:e,data:t};this.queue.push(i),this.queue.length>=1&&this.persistQueue()}setSize(e,t){isNaN(t)&&(t=this.calculateContainerMaxHeight()),t<this.minimalHeight&&(t=this.minimalHeight),isNaN(e)&&(e=this.calculateContainerMaxWidth()),e<this.minimalWidth&&(e=this.minimalWidth),i.default(a.inputWidthSelector).val(e),i.default(a.inputHeightSelector).val(t),this.$resizableContainer.css({width:e,height:t,left:0})}persistCurrentPreset(){let e={width:l.getCurrentWidth(),height:l.getCurrentHeight(),label:l.getCurrentLabel()};this.addToQueue(this.storagePrefix+"current",e)}persistCustomPreset(){let e={width:l.getCurrentWidth(),height:l.getCurrentHeight()};i.default(a.customSelector).data("width",e.width),i.default(a.customSelector).data("height",e.height),i.default(a.customWidthSelector).html(e.width),i.default(a.customHeightSelector).html(e.height),this.addToQueue(this.storagePrefix+"custom",e)}persistCustomPresetAfterChange(){clearTimeout(this.queueDelayTimer),this.queueDelayTimer=window.setTimeout(()=>{this.persistCustomPreset()},1e3)}initialize(){i.default(document).on("click",a.changeOrientationSelector,()=>{const e=i.default(a.inputHeightSelector).val(),t=i.default(a.inputWidthSelector).val();this.setSize(e,t),this.persistCurrentPreset()}),i.default(document).on("change",a.inputWidthSelector,()=>{const e=i.default(a.inputWidthSelector).val(),t=i.default(a.inputHeightSelector).val();this.setSize(e,t),l.setLabel(this.defaultLabel),this.persistCustomPresetAfterChange()}),i.default(document).on("change",a.inputHeightSelector,()=>{const e=i.default(a.inputWidthSelector).val(),t=i.default(a.inputHeightSelector).val();this.setSize(e,t),l.setLabel(this.defaultLabel),this.persistCustomPresetAfterChange()}),i.default(document).on("click",a.changePresetSelector,e=>{const t=i.default(e.currentTarget).data();this.setSize(parseInt(t.width,10),parseInt(t.height,10)),l.setLabel(t.label),this.persistCurrentPreset()}),this.$resizableContainer.resizable({handles:"w, sw, s, se, e"}),this.$resizableContainer.on("resizestart",e=>{i.default(e.currentTarget).append('<div id="this-iframe-cover" style="z-index:99;position:absolute;width:100%;top:0;left:0;height:100%;"></div>')}),this.$resizableContainer.on("resize",(e,t)=>{t.size.width=t.originalSize.width+2*(t.size.width-t.originalSize.width),t.size.height<this.minimalHeight&&(t.size.height=this.minimalHeight),t.size.width<this.minimalWidth&&(t.size.width=this.minimalWidth),i.default(a.inputWidthSelector).val(t.size.width),i.default(a.inputHeightSelector).val(t.size.height),this.$resizableContainer.css({left:0}),l.setLabel(this.defaultLabel)}),this.$resizableContainer.on("resizestop",()=>{i.default("#viewpage-iframe-cover").remove(),this.persistCurrentPreset(),this.persistCustomPreset()})}calculateContainerMaxHeight(){this.$resizableContainer.hide();let e=i.default(a.moduleBodySelector),t=e.outerHeight()-e.height(),s=i.default(document).height(),r=i.default(a.topbarContainerSelector).outerHeight();return this.$resizableContainer.show(),s-t-r-8}calculateContainerMaxWidth(){this.$resizableContainer.hide();let e=i.default(a.moduleBodySelector),t=e.outerWidth()-e.width(),s=i.default(document).width();return this.$resizableContainer.show(),parseInt(s-t+"",10)}}return new l}));
\ No newline at end of file
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment