From 81028fceacccd186b39d216594052a5e572d15c6 Mon Sep 17 00:00:00 2001 From: Berkeley Martinez Date: Thu, 3 Dec 2015 14:07:39 -0800 Subject: [PATCH 1/4] Add ability to disable user code on page load Adding `run=disabled` to the uri will disable the page from running user code. This is useful for pages that have frozen to to infinite loops or untrusted links that may be malicious --- client/commonFramework/code-uri.js | 44 +++++++++++++++++------- client/commonFramework/update-preview.js | 2 +- 2 files changed, 33 insertions(+), 13 deletions(-) diff --git a/client/commonFramework/code-uri.js b/client/commonFramework/code-uri.js index a0694b892b..bbb84239b2 100644 --- a/client/commonFramework/code-uri.js +++ b/client/commonFramework/code-uri.js @@ -42,6 +42,8 @@ window.common = (function(global) { return decoded .split('?') .splice(1) + .pop() + .split('&') .reduce(function(found, param) { var key = param.split('=')[0]; if (key === 'solution') { @@ -55,6 +57,23 @@ window.common = (function(global) { codeUri.isInQuery(location.search) || codeUri.isInQuery(location.hash); }, + getKeyInQuery(query, keyToFind = '') { + return query + .split('&') + .reduce(function(oldValue, param) { + var key = param.split('=')[0]; + var value = param.split('=')[1]; + if (key === keyToFind) { + return value; + } + return oldValue; + }, null); + }, + getSolutionFromQuery(query = '') { + return decodeFcc( + codeUri.decode(codeUri.getKeyInQuery(query, 'solution')) + ); + }, parse: function() { if (!codeUri.enabled) { return null; @@ -62,6 +81,7 @@ window.common = (function(global) { var query; if (location.search && codeUri.isInQuery(location.search)) { query = location.search.replace(/^\?/, ''); + if (history && typeof history.replaceState === 'function') { history.replaceState( history.state, @@ -73,20 +93,12 @@ window.common = (function(global) { } else { query = location.hash.replace(/^\#\?/, ''); } + if (!query) { return null; } - return query - .split('&') - .reduce(function(solution, param) { - var key = param.split('=')[0]; - var value = param.split('=')[1]; - if (key === 'solution') { - return decodeFcc(codeUri.decode(value || '')); - } - return solution; - }, null); + return this.getSolutionFromQuery(query); }, querify: function(solution) { if (!codeUri.enabled) { @@ -96,7 +108,9 @@ window.common = (function(global) { history.replaceState( history.state, null, - '?solution=' + codeUri.encode(encodeFcc(solution)) + '#?solution=' + + codeUri.encode(encodeFcc(solution)) + + (codeUri.shouldRun() ? '&run=disabled' : '' ) ); } else { location.hash = '?solution=' + @@ -105,7 +119,13 @@ window.common = (function(global) { return solution; }, - enabled: true + enabled: true, + shouldRun() { + return !this.getKeyInQuery( + (location.search || location.hash).replace(/^(\?|#\?)/, ''), + 'run' + ); + } }; common.init.push(function() { diff --git a/client/commonFramework/update-preview.js b/client/commonFramework/update-preview.js index 0f483a6a9c..9ba986d6a0 100644 --- a/client/commonFramework/update-preview.js +++ b/client/commonFramework/update-preview.js @@ -74,7 +74,7 @@ window.common = (function(global) { preview.write( libraryIncludes + jQuery + - code + + (common.codeUri.shouldRun() ? code : '' ) + '' + iframeScript ); From b7530253aa69fc75495c5dcaf240afc70cf9d977 Mon Sep 17 00:00:00 2001 From: Berkeley Martinez Date: Thu, 3 Dec 2015 15:09:52 -0800 Subject: [PATCH 2/4] Also disable output and tests --- client/commonFramework/code-uri.js | 1 + client/commonFramework/update-preview.js | 7 ++++++- client/iFrameScripts.js | 3 +++ 3 files changed, 10 insertions(+), 1 deletion(-) diff --git a/client/commonFramework/code-uri.js b/client/commonFramework/code-uri.js index bbb84239b2..e676f3ff11 100644 --- a/client/commonFramework/code-uri.js +++ b/client/commonFramework/code-uri.js @@ -133,6 +133,7 @@ window.common = (function(global) { }); common.codeUri = codeUri; + common.shouldRun = () => codeUri.shouldRun(); return common; }(window)); diff --git a/client/commonFramework/update-preview.js b/client/commonFramework/update-preview.js index 9ba986d6a0..ab99e46a6f 100644 --- a/client/commonFramework/update-preview.js +++ b/client/commonFramework/update-preview.js @@ -34,6 +34,11 @@ window.common = (function(global) { body { padding: 0px 3px 0px 3px; } `; + const codeDisabledError = ` + + `; const iFrameScript$ = common.getScriptContent$('/js/iFrameScripts.js').shareReplay(); @@ -74,7 +79,7 @@ window.common = (function(global) { preview.write( libraryIncludes + jQuery + - (common.codeUri.shouldRun() ? code : '' ) + + (common.shouldRun() ? code : codeDisabledError) + '' + iframeScript ); diff --git a/client/iFrameScripts.js b/client/iFrameScripts.js index 2fc6ac8b69..e9d1456f88 100644 --- a/client/iFrameScripts.js +++ b/client/iFrameScripts.js @@ -11,6 +11,9 @@ window.$(document).ready(function() { var common = parent.common; common.getJsOutput = function evalJs(code = '') { + if (window.__err || !common.shouldRun()) { + return window.__err || 'code disabled'; + } let output; try { /* eslint-disable no-eval */ From d6508ac080c6f482ad07c3f71abb0602c84637c7 Mon Sep 17 00:00:00 2001 From: Berkeley Martinez Date: Thu, 3 Dec 2015 17:13:04 -0800 Subject: [PATCH 3/4] Increase loop protect timeout --- public/js/lib/loop-protect/loop-protect.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/public/js/lib/loop-protect/loop-protect.js b/public/js/lib/loop-protect/loop-protect.js index 5050605b9e..1452d9525d 100644 --- a/public/js/lib/loop-protect/loop-protect.js +++ b/public/js/lib/loop-protect/loop-protect.js @@ -20,6 +20,7 @@ if (typeof DEBUG === 'undefined') { DEBUG = true; } var reSingle = /\b(for|while|do)\b/; var labelRe = /\b([a-z_]{1}\w+:)/i; var comments = /(?:\/\*(?:[\s\S]*?)\*\/)|(?:([\s;])+\/\/(?:.*)$)/gm; + var loopTimeout = 500; var loopProtect = rewriteLoops; @@ -404,8 +405,7 @@ if (typeof DEBUG === 'undefined') { DEBUG = true; } } line.hit++; - if ((now - line.time) > 100) {//} && line.hit !== line.last+1) { - // We've spent over 100ms on this loop... smells infinite. + if ((now - line.time) > loopTimeout) {//} && line.hit !== line.last+1) { loopProtect.hit(state.line); // Returning true prevents the loop running again return true; From 0b42712a1f0fb0612fd2d111af5279c39f932ba2 Mon Sep 17 00:00:00 2001 From: Berkeley Martinez Date: Thu, 3 Dec 2015 17:37:00 -0800 Subject: [PATCH 4/4] Fix moving of query behind hash --- client/commonFramework/code-uri.js | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/client/commonFramework/code-uri.js b/client/commonFramework/code-uri.js index e676f3ff11..3051c12bd2 100644 --- a/client/commonFramework/code-uri.js +++ b/client/commonFramework/code-uri.js @@ -105,12 +105,17 @@ window.common = (function(global) { return null; } if (history && typeof history.replaceState === 'function') { + // grab the url up to the query + // destroy any hash symbols still clinging to life + const url = (location.href.split('?')[0]).replace(/(#*)$/, ''); history.replaceState( history.state, null, - '#?solution=' + - codeUri.encode(encodeFcc(solution)) + - (codeUri.shouldRun() ? '&run=disabled' : '' ) + url + + '#?' + + (codeUri.shouldRun() ? '' : 'run=disabled&') + + 'solution=' + + codeUri.encode(encodeFcc(solution)) ); } else { location.hash = '?solution=' +