mirror of
https://github.com/chinchang/web-maker.git
synced 2025-07-09 16:06:21 +02:00
Protect webmaker users from INFINITE LOOPS! fixes #18
This commit is contained in:
@ -676,6 +676,7 @@
|
|||||||
|
|
||||||
<script src="lib/split.js"></script>
|
<script src="lib/split.js"></script>
|
||||||
<script src="lib/esprima.js"></script>
|
<script src="lib/esprima.js"></script>
|
||||||
|
<script src="lib/escodegen.browser.min.js"></script>
|
||||||
|
|
||||||
<script src="utils.js"></script>
|
<script src="utils.js"></script>
|
||||||
<script src="deferred.js"></script>
|
<script src="deferred.js"></script>
|
||||||
|
@ -334,16 +334,18 @@
|
|||||||
var code = editur.cm.js.getValue();
|
var code = editur.cm.js.getValue();
|
||||||
|
|
||||||
cleanupErrors('js');
|
cleanupErrors('js');
|
||||||
|
var ast;
|
||||||
|
|
||||||
if (jsMode === JsModes.JS) {
|
if (jsMode === JsModes.JS) {
|
||||||
try {
|
try {
|
||||||
esprima.parse(code, {
|
ast = esprima.parse(code, {
|
||||||
tolerant: true
|
tolerant: true
|
||||||
});
|
});
|
||||||
} catch(e) {
|
} catch(e) {
|
||||||
showErrors('js', [ { lineNumber: e.lineNumber-1, message: e.description } ]);
|
showErrors('js', [ { lineNumber: e.lineNumber-1, message: e.description } ]);
|
||||||
} finally {
|
} finally {
|
||||||
d.resolve(code);
|
utils.addInfiniteLoopProtection(ast);
|
||||||
|
d.resolve(escodegen.generate(ast));
|
||||||
}
|
}
|
||||||
} else if (jsMode === JsModes.COFFEESCRIPT) {
|
} else if (jsMode === JsModes.COFFEESCRIPT) {
|
||||||
var coffeeCode;
|
var coffeeCode;
|
||||||
@ -352,23 +354,31 @@
|
|||||||
} catch (e) {
|
} catch (e) {
|
||||||
showErrors('js', [ { lineNumber: e.location.first_line, message: e.message } ]);
|
showErrors('js', [ { lineNumber: e.location.first_line, message: e.message } ]);
|
||||||
} finally {
|
} finally {
|
||||||
d.resolve(coffeeCode);
|
ast = esprima.parse(coffeeCode, {
|
||||||
|
tolerant: true
|
||||||
|
});
|
||||||
|
utils.addInfiniteLoopProtection(ast);
|
||||||
|
d.resolve(escodegen.generate(ast));
|
||||||
}
|
}
|
||||||
} else if (jsMode === JsModes.ES6) {
|
} else if (jsMode === JsModes.ES6) {
|
||||||
try {
|
try {
|
||||||
esprima.parse(code, {
|
ast = esprima.parse(code, {
|
||||||
tolerant: true
|
tolerant: true
|
||||||
});
|
});
|
||||||
} catch(e) {
|
} catch(e) {
|
||||||
showErrors('js', [ { lineNumber: e.lineNumber-1, message: e.description } ]);
|
showErrors('js', [ { lineNumber: e.lineNumber-1, message: e.description } ]);
|
||||||
} finally {
|
} finally {
|
||||||
d.resolve(Babel.transform(code, { presets: ['es2015'] }).code);
|
utils.addInfiniteLoopProtection(ast);
|
||||||
|
d.resolve(Babel.transform(escodegen.generate(ast), { presets: ['es2015'] }).code);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return d.promise;
|
return d.promise;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
window.previewException = function (error) {
|
||||||
|
console.error('Possible infinite loop detected.', error.stack)
|
||||||
|
}
|
||||||
window.onunload = function () {
|
window.onunload = function () {
|
||||||
saveCode('code');
|
saveCode('code');
|
||||||
};
|
};
|
||||||
@ -391,7 +401,7 @@
|
|||||||
var contents = '<html>\n<head>\n' +
|
var contents = '<html>\n<head>\n' +
|
||||||
'<style>\n' + css + '\n</style>\n' +
|
'<style>\n' + css + '\n</style>\n' +
|
||||||
'</head>\n' +
|
'</head>\n' +
|
||||||
'<body>\n' + html + '\n<script>\n' + js + '\n</script></body>\n</html>';
|
'<body>\n' + html + '\n<script>\n' + js + '\n//# sourceURL=userscript.js</script></body>\n</html>';
|
||||||
|
|
||||||
var fileWritten = false;
|
var fileWritten = false;
|
||||||
|
|
||||||
|
50
src/utils.js
50
src/utils.js
@ -1,6 +1,7 @@
|
|||||||
(function () {
|
(function () {
|
||||||
window.$ = document.querySelector.bind(document);
|
window.$ = document.querySelector.bind(document);
|
||||||
window.$all = document.querySelectorAll.bind(document);
|
window.$all = document.querySelectorAll.bind(document);
|
||||||
|
var alphaNum = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
|
||||||
|
|
||||||
// https://github.com/substack/semver-compare/blob/master/index.js
|
// https://github.com/substack/semver-compare/blob/master/index.js
|
||||||
function semverCompare(a, b) {
|
function semverCompare(a, b) {
|
||||||
@ -21,7 +22,7 @@
|
|||||||
len = len || 10;
|
len = len || 10;
|
||||||
var id = '';
|
var id = '';
|
||||||
for (var i = len; i--;) {
|
for (var i = len; i--;) {
|
||||||
id += String.fromCharCode(~~(Math.random() * 52) + 65);
|
id += alphaNum[~~(Math.random() * alphaNum.length)];
|
||||||
}
|
}
|
||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
@ -39,10 +40,57 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Generate 2 ASTs for the code to be inserted in loops for infinite run protection.
|
||||||
|
// The `myVar` variable names would be changed later for every insertion.
|
||||||
|
function getLoopProtectorBlocks() {
|
||||||
|
var ast1 = esprima.parse('var myvar = Date.now();');
|
||||||
|
var ast2 = esprima.parse('while(a){if (Date.now() - a787897 > 1000) { window.top.previewException(new Error("Infinite loop")); break;}}');
|
||||||
|
return {
|
||||||
|
before: ast1.body[0],
|
||||||
|
inside: ast2.body[0].body.body[0]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Add timed limit on the loops found in the passed AST body
|
||||||
|
* @param {ASTBody} Body of an AST generated by esprima or any ES compliant AST
|
||||||
|
*/
|
||||||
|
function addInfiniteLoopProtection(astBody) {
|
||||||
|
if (!Array.isArray(astBody)) {
|
||||||
|
addInfiniteLoopProtection(astBody.body);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var el, randomVariableName, insertionBLocks;
|
||||||
|
|
||||||
|
for (var i = astBody.length; i--;) {
|
||||||
|
el = astBody[i];
|
||||||
|
if (el && el.type === 'ForStatement' || el.type === 'WhileStatement' || el.type === 'DoWhileStatement') {
|
||||||
|
randomVariableName = '_' + generateRandomId(3);
|
||||||
|
insertionBLocks = getLoopProtectorBlocks();
|
||||||
|
insertionBLocks.before.declarations[0].id.name = insertionBLocks.inside.test.left.right.name = randomVariableName;
|
||||||
|
// Insert time variable assignment
|
||||||
|
astBody.splice(i, 0, insertionBLocks.before);
|
||||||
|
// If the loop's body is a single statement, then convert it into a block statement
|
||||||
|
// so that we can insert our conditional break inside it.
|
||||||
|
if (!Array.isArray(el.body)) {
|
||||||
|
el.body = {
|
||||||
|
body: [ el.body ],
|
||||||
|
type: 'BlockStatement'
|
||||||
|
};
|
||||||
|
}
|
||||||
|
// Insert IfStatement
|
||||||
|
el.body.body.unshift(insertionBLocks.inside);
|
||||||
|
}
|
||||||
|
if (el.body) {
|
||||||
|
addInfiniteLoopProtection(el.body);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
window.utils = {
|
window.utils = {
|
||||||
semverCompare: semverCompare,
|
semverCompare: semverCompare,
|
||||||
generateRandomId: generateRandomId,
|
generateRandomId: generateRandomId,
|
||||||
onButtonClick: onButtonClick,
|
onButtonClick: onButtonClick,
|
||||||
|
addInfiniteLoopProtection: addInfiniteLoopProtection,
|
||||||
log: log
|
log: log
|
||||||
};
|
};
|
||||||
})();
|
})();
|
||||||
|
Reference in New Issue
Block a user