add escaping for inline styles and more targeted escaping for inline scripts

This commit is contained in:
Michael Rawlings 2017-02-09 12:48:41 -08:00
parent 741fde06ce
commit 42ef3fcb45
7 changed files with 48 additions and 4 deletions

View File

@ -68,6 +68,7 @@ const helpers = {
'escapeXml': 'x',
'escapeXmlAttr': 'xa',
'escapeScript': 'xs',
'escapeStyle': 'xc',
'forEach': 'f',
'forEachProp': { module: 'marko/runtime/helper-forEachProperty' },
'forEachPropStatusVar': { module: 'marko/runtime/helper-forEachPropStatusVar' },

View File

@ -11,12 +11,18 @@ function beforeGenerateCode(event) {
if (event.node.tagName === 'script') {
event.context.pushFlag('SCRIPT_BODY');
}
if (event.node.tagName === 'style') {
event.context.pushFlag('STYLE_BODY');
}
}
function afterGenerateCode(event) {
if (event.node.tagName === 'script') {
event.context.popFlag('SCRIPT_BODY');
}
if (event.node.tagName === 'style') {
event.context.popFlag('STYLE_BODY');
}
}
class HtmlElement extends Node {

View File

@ -1,7 +1,7 @@
'use strict';
var escapeXml = require('../../../../runtime/html/helpers').x;
var Literal = require('../..//Literal');
var Literal = require('../../Literal');
module.exports = function(node, codegen) {
var context = codegen.context;
@ -16,7 +16,7 @@ module.exports = function(node, codegen) {
return;
}
if (context.isFlagSet('SCRIPT_BODY')) {
if (context.isFlagSet('SCRIPT_BODY') || context.isFlagSet('STYLE_BODY')) {
escape = false;
}
@ -35,6 +35,10 @@ module.exports = function(node, codegen) {
escapeIdentifier = context.helper('escapeScript');
}
if (context.isFlagSet('STYLE_BODY')) {
escapeIdentifier = context.helper('escapeStyle');
}
// TODO Only escape the parts that need to be escaped if it is a compound expression with static
// text parts
argument = builder.functionCall(

View File

@ -3,7 +3,6 @@ var extend = require('raptor-util/extend');
var STYLE_ATTR = 'style';
var CLASS_ATTR = 'class';
var escapeEndingScriptTagRegExp = /<\//g;
var escape = require('./escape');
var escapeXml = escape.escapeXml;
@ -44,8 +43,27 @@ exports.xa = escapeXmlAttr;
* prematurely ended and a new script tag could then be started that could then execute
* arbitrary code.
*/
var escapeEndingScriptTagRegExp = /<\/script/g;
exports.xs = function escapeScriptHelper(val) {
return (typeof val === 'string') ? val.replace(escapeEndingScriptTagRegExp, '\\u003C/') : val;
return (typeof val === 'string') ? val.replace(escapeEndingScriptTagRegExp, '\\u003C/script') : val;
};
/**
* Escapes the '</' sequence in the body of a <style> body to avoid the `<style>` being
* ended prematurely.
*
* For example:
* var color = '</style><script>alert(1)</script>';
*
* <style>#foo { background-color:${color} }</style>
*
* Without escaping the ending '</style>' sequence the opening <style> tag would be
* prematurely ended and a script tag could then be started that could then execute
* arbitrary code.
*/
var escapeEndingStyleTagRegExp = /<\/style/g;
exports.xc = function escapeScriptHelper(val) {
return (typeof val === 'string') ? val.replace(escapeEndingStyleTagRegExp, '\\003C/style') : val;
};
/**

View File

@ -0,0 +1,5 @@
<style>
#foo {
background-color:\003C/style><script>alert("evil");</script>;
}
</style>

View File

@ -0,0 +1,5 @@
<style>
#foo {
background-color:${input.color};
}
</style>

View File

@ -0,0 +1,5 @@
exports.templateData = {
color: '</style><script>alert("evil");</script>'
};
exports.vdomSkip = true;