From eec7fff909033ebdc527c2728009b5f450651de4 Mon Sep 17 00:00:00 2001 From: Berkeley Martinez Date: Tue, 24 Nov 2015 13:48:14 -0800 Subject: [PATCH] Fix jQuery challenges --- .gitignore | 1 + .../detect-unsafe-code-stream.js | 55 ++++++++++++ client/commonFramework/end.js | 6 +- .../execute-challenge-stream.js | 88 +++++-------------- client/faux.js | 5 +- gulpfile.js | 5 +- 6 files changed, 90 insertions(+), 70 deletions(-) create mode 100644 client/commonFramework/detect-unsafe-code-stream.js diff --git a/.gitignore b/.gitignore index a59cd15b8f..8b70d1c075 100644 --- a/.gitignore +++ b/.gitignore @@ -45,6 +45,7 @@ public/js/sandbox* public/js/iFrameScripts* public/js/plugin* public/js/vendor* +public/js/faux* public/css/main* server/rev-manifest.json diff --git a/client/commonFramework/detect-unsafe-code-stream.js b/client/commonFramework/detect-unsafe-code-stream.js new file mode 100644 index 0000000000..14ed08f77b --- /dev/null +++ b/client/commonFramework/detect-unsafe-code-stream.js @@ -0,0 +1,55 @@ +window.common = (function(global) { + const { + Rx: { Observable }, + common = { init: [] } + } = global; + + const detectFunctionCall = /function\s*?\(|function\s+\w+\s*?\(/gi; + const detectUnsafeJQ = /\$\s*?\(\s*?\$\s*?\)/gi; + const detectUnsafeConsoleCall = /if\s\(null\)\sconsole\.log\(1\);/gi; + + common.detectUnsafeCode$ = function detectUnsafeCode$(code) { + const openingComments = code.match(/\/\*/gi); + const closingComments = code.match(/\*\//gi); + + // checks if the number of opening comments(/*) matches the number of + // closing comments(*/) + if ( + openingComments && + ( + !closingComments || + openingComments.length > closingComments.length + ) + ) { + + return Observable.throw( + new Error('SyntaxError: Unfinished multi-line comment') + ); + } + + if (code.match(detectUnsafeJQ)) { + return Observable.throw( + new Error('Unsafe $($)') + ); + } + + if ( + code.match(/function/g) && + !code.match(detectFunctionCall) + ) { + return Observable.throw( + new Error('SyntaxError: Unsafe or unfinished function declaration') + ); + } + + if (code.match(detectUnsafeConsoleCall)) { + return Observable.throw( + new Error('Invalid if (null) console.log(1); detected') + ); + } + + return Observable.just(code); + }; + + return common; +}(window)); diff --git a/client/commonFramework/end.js b/client/commonFramework/end.js index c0cd76129d..d2fa7f5468 100644 --- a/client/commonFramework/end.js +++ b/client/commonFramework/end.js @@ -26,7 +26,8 @@ $(document).ready(function() { code$ .flatMap(code => { if (common.hasJs(code)) { - return common.detectLoops$(code) + return common.detectUnsafeCode$(code) + .flatMap(code => common.detectLoops$(code)) .flatMap( ({ err }) => err ? Observable.throw(err) : Observable.just(code) ); @@ -101,12 +102,13 @@ $(document).ready(function() { } ); + // initial challenge run to populate tests if (challengeType === challengeTypes.HTML) { var $preview = $('#preview'); return Observable.fromCallback($preview.ready, $preview)() .delay(500) .flatMap(() => common.executeChallenge$()) - .catch(err => Observable.just(err)) + .catch(err => Observable.just({ err })) .subscribe( ({ err, tests }) => { if (err) { diff --git a/client/commonFramework/execute-challenge-stream.js b/client/commonFramework/execute-challenge-stream.js index 86541bbb70..d9a7be1c59 100644 --- a/client/commonFramework/execute-challenge-stream.js +++ b/client/commonFramework/execute-challenge-stream.js @@ -12,9 +12,6 @@ window.common = (function(global) { } = global; let attempts = 0; - const detectFunctionCall = /function\s*?\(|function\s+\w+\s*?\(/gi; - const detectUnsafeJQ = /\$\s*?\(\s*?\$\s*?\)/gi; - const detectUnsafeConsoleCall = /if\s\(null\)\sconsole\.log\(1\);/gi; common.executeChallenge$ = function executeChallenge$() { const code = common.editor.getValue(); @@ -25,101 +22,62 @@ window.common = (function(global) { ga('send', 'event', 'Challenge', 'ran-code', common.challengeName); - let openingComments = code.match(/\/\*/gi); - - // checks if the number of opening comments(/*) matches the number of - // closing comments(*/) - return Observable.just({ code }) - .flatMap(({ code }) => { - if ( - code.match(/\$\s*?\(\s*?\$\s*?\)/gi) && - openingComments && - openingComments.length > code.match(/\*\//gi).length - ) { - - return Observable.throw({ - err: 'SyntaxError: Unfinished multi-line comment', - code - }); - } - - if (code.match(detectUnsafeJQ)) { - return Observable.throw({ - err: 'Unsafe $($)', - code - }); - } - - if ( - code.match(/function/g) && - !code.match(detectFunctionCall) - ) { - return Observable.throw({ - err: 'SyntaxError: Unsafe or unfinished function declaration', - code - }); - } - - if (code.match(detectUnsafeConsoleCall)) { - return Observable.throw({ - err: 'Invalid if (null) console.log(1); detected', - code - }); - } - - // add head and tail and detect loops - return Observable.just({ code: head + code + tail, original: code }); - }) - .flatMap(data => { + // run checks for unsafe code + return common.detectUnsafeCode$(code) + // add head and tail and detect loops + .map(code => head + code + tail) + .flatMap(code => { if (common.challengeType === common.challengeTypes.HTML) { if (common.hasJs(code)) { - return common.addFaux$(data) + // html has a script code + // add faux code and test in webworker + return common.addFaux$(code) .flatMap(code => common.detectLoops$(code)) .flatMap(({ err }) => { if (err) { - return Observable.throw({ err }); + return Observable.throw(err); } - return common.runPreviewTests$({ - code: data.code, - tests: common.tests.slice() - }); + return common.updatePreview$(code) + .flatMap(() => common.runPreviewTests$({ + code, + tests: common.tests.slice() + })); }); } - return common.updatePreview$(data.code) + // no script code detected in html code + // Update preview and run tests in iframe + return common.updatePreview$(code) .flatMap(code => common.runPreviewTests$({ code, tests: common.tests.slice() })); } + // js challenge + // remove comments and add tests to string return common.addTestsToString(Object.assign( - data, { code: common.removeComments(code), tests: common.tests.slice() } )) .flatMap(common.detectLoops$) - .flatMap(({ err, code, data, userTests, original }) => { + .flatMap(({ err, code, data, userTests }) => { if (err) { - return Observable.throw({ err }); + return Observable.throw(err); } + // run tests + // for now these are running in the browser return common.runTests$({ data, code, userTests, - original, output: data.output.replace(/\\\"/gi, '') }); }); - }) - .catch(e => { - return e && e.err ? - Observable.throw(e) : - Observable.throw({ err: e }); }); }; diff --git a/client/faux.js b/client/faux.js index f1eb4e44e6..ca4563f0d8 100644 --- a/client/faux.js +++ b/client/faux.js @@ -1,8 +1,9 @@ -document = {}; +/* eslint-disable no-unused-vars */ +var document = {}; var navigator = function() { this.geolocation = function() { this.getCurrentPosition = function() { - this.coords = {latitude: "", longitude: ""}; + this.coords = {latitude: '', longitude: '' }; return this; }; return this; diff --git a/gulpfile.js b/gulpfile.js index 034491cc5b..f86b971b7a 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -103,7 +103,8 @@ var paths = { js: [ 'client/main.js', 'client/iFrameScripts.js', - 'client/plugin.js' + 'client/plugin.js', + 'client/faux.js' ], commonFramework: [ @@ -114,6 +115,7 @@ var paths = { 'code-storage', 'code-uri', 'create-editor', + 'detect-unsafe-code-stream', 'detect-loops-stream', 'display-test-results', 'execute-challenge-stream', @@ -421,6 +423,7 @@ gulp.task('js', function() { }); // commonFramework depend on iFrameScripts +// and faux.js gulp.task('dependents', ['js'], function() { var manifestName = 'dependents-manifest.json'; var dest = paths.publicJs;