Skip to content

Commit

Permalink
[SECURITY] Prevent XSS in modal component
Browse files Browse the repository at this point in the history
Resolves: #84190
Releases: master, 8.7, 7.6
Security-Commit: e991d9ac10b78f360bff386d9a822f0caa7c781d
Security-Bulletin: TYPO3-CORE-SA-2018-007
Change-Id: I41f0d6bdb5e06b6f08b19feaf59ea47e3a197549
Reviewed-on: https://review.typo3.org/59093
Reviewed-by: Oliver Hader <oliver.hader@typo3.org>
Tested-by: Oliver Hader <oliver.hader@typo3.org>
  • Loading branch information
NeoBlack authored and ohader committed Dec 11, 2018
1 parent c917493 commit c35646c
Show file tree
Hide file tree
Showing 3 changed files with 97 additions and 6 deletions.
20 changes: 14 additions & 6 deletions typo3/sysext/backend/Resources/Public/JavaScript/Modal.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,13 @@
define(['jquery',
'TYPO3/CMS/Backend/Severity',
'TYPO3/CMS/Backend/Icons',
'TYPO3/CMS/Core/SecurityUtility',
'bootstrap'
], function($, Severity, Icons) {
], function($, Severity, Icons, SecurityUtility) {
'use strict';

var securityUtility = new SecurityUtility();

try {
// fetch from parent
if (parent && parent.window.TYPO3 && parent.window.TYPO3.Modal) {
Expand Down Expand Up @@ -233,7 +236,15 @@ define(['jquery',
// Validation of configuration
configuration.type = typeof configuration.type === 'string' && configuration.type in Modal.types ? configuration.type : Modal.defaultConfiguration.type;
configuration.title = typeof configuration.title === 'string' ? configuration.title : Modal.defaultConfiguration.title;
configuration.content = typeof configuration.content === 'string' || typeof configuration.content === 'object' ? configuration.content : Modal.defaultConfiguration.content;
if (typeof configuration.content === 'string') {
// A string means, no markup allowed, let's ensure this
configuration.content = securityUtility.encodeHtml(configuration.content);
} else if (typeof configuration.content === 'object') {
// An object means, a valid jQuery object with markup, let's get the markup
configuration.content = configuration.content.html();
} else {
configuration.content = Modal.defaultConfiguration.content;
}
configuration.severity = typeof configuration.severity !== 'undefined' ? configuration.severity : Modal.defaultConfiguration.severity;
configuration.buttons = configuration.buttons || Modal.defaultConfiguration.buttons;
configuration.size = typeof configuration.size === 'string' && configuration.size in Modal.sizes ? configuration.size : Modal.defaultConfiguration.size;
Expand Down Expand Up @@ -318,10 +329,7 @@ define(['jquery',
if (typeof content === 'object') {
currentModal.find(Modal.identifiers.body).append(content);
} else {
// we need html, check if we have to wrap content in <p>
if (!/^<[a-z][\s\S]*>/i.test(content)) {
content = $('<p />').html(content);
}
content = $('<p />').html(content);
currentModal.find(Modal.identifiers.body).html(content);
}
}
Expand Down
70 changes: 70 additions & 0 deletions typo3/sysext/core/Resources/Private/TypeScript/SecurityUtility.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/

/**
* Module: TYPO3/CMS/Core/SecurityUtility
* contains method to escape input to prevent XSS and other security related things
* @exports TYPO3/CMS/Core/SecurityUtility
*/
class SecurityUtility {
private readonly documentRef: Document;

/**
* @param {Document} documentRef
*/
constructor(documentRef: Document = document) {
this.documentRef = documentRef;
}

/**
* Encodes HTML to use according entities. Behavior is similar to PHP's
* htmlspecialchars. Input might contain XSS, output has it encoded.
*
* @param {string} value Input value to be encoded
* @param {boolean} doubleEncode (default `true`)
* @return {string}
*/
public encodeHtml(value: string, doubleEncode: boolean = true): string {
let anvil: HTMLSpanElement = this.createAnvil();
if (!doubleEncode) {
// decode HTML entities step-by-step
// but NEVER(!) as a whole, since that would allow XSS
value = value.replace(/&[#A-Za-z0-9]+;/g, (html: string) => {
anvil.innerHTML = html;
return anvil.innerText;
});
}
// apply arbitrary data a text node
// thus browser is capable of properly encoding
anvil.innerText = value;
return anvil.innerHTML;
}

/**
* @param {string} value
*/
public debug(value: string): void {
if (value !== this.encodeHtml(value)) {
console.warn('XSS?!', value);
}
}

/**
* @return {HTMLSpanElement}
*/
private createAnvil(): HTMLSpanElement {
return this.documentRef.createElement('span');
}
}

export = SecurityUtility;
13 changes: 13 additions & 0 deletions typo3/sysext/core/Resources/Public/JavaScript/SecurityUtility.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
define(["require","exports"],function(e,n){"use strict";return function(){function e(e){void 0===e&&(e=document),this.documentRef=e}return e.prototype.encodeHtml=function(e,n){void 0===n&&(n=!0);var t=this.createAnvil();return n||(e=e.replace(/&[#A-Za-z0-9]+;/g,function(e){return t.innerHTML=e,t.innerText})),t.innerText=e,t.innerHTML},e.prototype.createAnvil=function(){return this.documentRef.createElement("span")},e.prototype.debug=function(e){e!==this.encodeHtml(e)&&console.warn("XSS?!",e)},e}()});

0 comments on commit c35646c

Please sign in to comment.