From 8efc8793ea00eaf3acea237bbe68ad4daf831045 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20Gr=C5=88o?= Date: Mon, 7 May 2018 17:09:23 +0200 Subject: [PATCH 1/2] fixed security holes --- filtrex.js | 44 +++++++++++++++++++++++++++++++++--------- test/filtrex-test.html | 16 +++++++++++++++ 2 files changed, 51 insertions(+), 9 deletions(-) diff --git a/filtrex.js b/filtrex.js index 3efd916..a1c2c0e 100644 --- a/filtrex.js +++ b/filtrex.js @@ -49,9 +49,17 @@ function compileExpression(expression, extraFunctions /* optional */) { function unknown(funcName) { throw 'Unknown function: ' + funcName + '()'; } - var func = new Function('functions', 'data', 'unknown', js.join('')); + + function prop(obj, name) { + return Object.prototype.hasOwnProperty.call(obj||{}, name) ? obj[name] : undefined; + } + + console.log(js.join('')); + + var func = new Function('functions', 'data', 'unknown', 'prop', js.join('')); + return function(data) { - return func(functions, data, unknown); + return func(functions, data, unknown, prop); }; } @@ -103,9 +111,25 @@ function filtrexParser() { ['\\s+', ''], // skip whitespace ['[0-9]+(?:\\.[0-9]+)?\\b', 'return "NUMBER";'], // 212.321 - ['[a-zA-Z][\\.a-zA-Z0-9_]*', 'return "SYMBOL";'], // some.Symbol22 - ['\'(?:[^\'])*\'', 'yytext = yytext.substr(1, yyleng-2); return "SYMBOL";'], // 'some-symbol' - ['"(?:[^"])*"', 'yytext = yytext.substr(1, yyleng-2); return "STRING";'], // "foo" + + ['[a-zA-Z][\\.a-zA-Z0-9_]*', + `yytext = JSON.stringify(yytext); + return "SYMBOL";` + ], // some.Symbol22 + + [`'(?:[^\'])*'`, + `yytext = JSON.stringify( + yytext.substr(1, yyleng-2) + ); + return "SYMBOL";` + ], // 'some-symbol' + + ['"(?:[^"])*"', + `yytext = JSON.stringify( + yytext.substr(1, yyleng-2) + ); + return "STRING";` + ], // "foo" // End ['$', 'return "EOF";'], @@ -154,11 +178,13 @@ function filtrexParser() { ['e >= e' , code(['Number(', 1, '>=', 3, ')'])], ['e ? e : e', code([1, '?', 3, ':', 5])], ['( e )' , code([2])], + ['( array , e )', code(['[', 2, ',', 4, ']'])], ['NUMBER' , code([1])], - ['STRING' , code(['"', 1, '"'])], - ['SYMBOL' , code(['data["', 1, '"]'])], - ['SYMBOL ( )', code(['(functions.hasOwnProperty("', 1, '") ? functions.', 1, '() : unknown("', 1, '"))'])], - ['SYMBOL ( argsList )', code(['(functions.hasOwnProperty("', 1, '") ? functions.', 1, '(', 3, ') : unknown("', 1, '"))'])], + ['STRING' , code([1])], + ['SYMBOL' , code(['prop(data, ', 1, ')'])], + ['SYMBOL of e', code(['prop(', 3, ',', 1, ')'])], + ['SYMBOL ( )', code(['(functions.hasOwnProperty(', 1, ') ? functions[', 1, ']() : unknown(', 1, '))'])], + ['SYMBOL ( argsList )', code(['(functions.hasOwnProperty(', 1, ') ? functions[', 1, '](', 3, ') : unknown(', 1, '))'])], ['e in ( inSet )', code([1, ' in (function(o) { ', 4, 'return o; })({})'])], ['e not in ( inSet )', code(['!(', 1, ' in (function(o) { ', 5, 'return o; })({}))'])], ], diff --git a/test/filtrex-test.html b/test/filtrex-test.html index d0ba6ed..4e1da3a 100644 --- a/test/filtrex-test.html +++ b/test/filtrex-test.html @@ -148,6 +148,22 @@ eq(false, window.p0wned); }, + 'cannot access properties of the data prototype': function() { + eq(undefined, compileExpression('a')(Object.create({a: 42}))); + }, + + 'cannot inject single-quoted names with double quotes': function() { + window.p0wned = false; + let evil = compileExpression(`'"+(window.p0wned = true)+"'`); + + eq(31, evil({'"+(window.p0wned = true)+"': 31})); + eq(false, window.p0wned); + }, + + 'backslash escaping': function() { + eq('\\good', compileExpression(`"\\" + '\\'`)({'\\':'good'})); + }, + }); From b93f3ddf269f81bd37a16d8407917dc4d84ab552 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20Gr=C5=88o?= Date: Mon, 7 May 2018 17:24:56 +0200 Subject: [PATCH 2/2] test for joewalnes/filtrex#17 --- test/filtrex-test.html | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/test/filtrex-test.html b/test/filtrex-test.html index 4e1da3a..6b58a85 100644 --- a/test/filtrex-test.html +++ b/test/filtrex-test.html @@ -158,6 +158,12 @@ eq(31, evil({'"+(window.p0wned = true)+"': 31})); eq(false, window.p0wned); + + eq(42, compileExpression( + "'undefined:(window.p0wned=true)));((true?(x=>x)'()", + {'undefined:(window.p0wned=true)));((true?(x=>x)': ()=>42} + )()); + eq(false, window.p0wned); }, 'backslash escaping': function() {