Block eval && new Function - javascript

I'm just working on writing some random puzzles on codewars.com and am curious if anyone can think of a way to eval code after the following code has been run:
eval = function(){};
delete Function.prototype.constructor;
Function = undefined;
// the following are to block require('vm') -- if anyone wants to run this
// in production it may be better to block that one module (others?)
require = undefined;
module.__proto__.require = undefined; // added this due to alexpod's answer, modified due to Fabrício Matté's :)
module.constructor = undefined; // added this due to alexpod's answer
This is in node.js, so setTimeout( "string" ) doesn't work.

Well, also you have module variable in node. So you can require vm package and run code using its require method:
var vm = module.require('vm');
vm.runInThisContext(' console.log("hello") ');
UPD
Well, you updated the question, but we can hack it again:
var vm = module.constructor.prototype.require('vm');
vm.runInThisContext(' console.log("hello") ');
UPD2
Another variant:
var vm = module.constructor._load('vm');
vm.runInThisContext(' console.log("hello") ');
UPD3
Again conditions are changed so the next variant:
module.constructor.prototype._compile(' console.log("again hacked") ');
// or
module.__proto__._compile(' console.log("again hacked") ');
// or
Object.getPrototypeOf(module)._compile(' console.log("again hacked") ');
I think better to set module = undefined to make question more complex:)
UPD4
There are another variant without module:)
process.stdin.push(' console.log("here we are") \n ');
But it works only in CLI ("repl")
UPD5
Also in iojs and in node with version >= 0.11.x you can use contextify binding:
var contextify = process.binding('contextify');
var script = new contextify.ContextifyScript(' console.log("im here, buddy") ');
script.runInThisContext();
In node with version < 0.11.x you can use evals binding:
var evals = process.binding('evals');
var script = new evals.NodeScript(' console.log("here I am") ')
script.runInThisContext();

module.require = undefined; is not enough as require is inherited from the Module prototype:
module.require = undefined;
var vm = module.__proto__.require('vm');
vm.runInThisContext('console.log(1)');
Instead, you should:
module.__proto__.require = undefined;
// now this fails and you can't use the __proto__ trick:
var vm = module.require('vm');

Using the GeneratorFunction constructor:
(function*(){}).constructor('console.log(1);')().next().value;

Related

Can I override the value returned by require() in webpack?

For testing purposes, I want to use require('whatever.js') to return an arbitrary content rather than having it actually load the file.
// made up code
var AAA = {};
require.setFor('./whatever.js', AAA);
var BBB = require('./whatever.js');
console.assert(AAA === BBB);
Is this possible?
Simply overwrite the require function:
var realRequire = require;
require = function (what) {
return realRequire('mocked-' + what);
}
You could use a lookup table for your mocks as well and call realRequire() if no mock exists.

Using Node locally like a console in the browser, lots of errors

I am learning Javascript and I wish to "interact" with my program to see if I wrote functions properly. When I say interact I mean create objects, call recursive functions, get/set properties, etc.
When I load my script in Chrome I can do all of this in the console (works properly). However, when I try the same approach on my Mac using Node I get all sorts of weird behaviour.
Code example:
"use strict";
// simple recursive function
let countDownFrom = (num) => {
if (num === 0) return;
console.log(num)
countDownFrom(num - 1)
}
// Closures
var createPet = function(name) {
var sex;
return {
setName: function(newName) {
name = newName;
},
getName: function() {
return name;
},
getSex: function() {
return sex;
},
setSex: function(newSex) {
if(typeof newSex === "string" && (newSex.toLowerCase() === "male" || newSex.toLowerCase() === "female")) {
sex = newSex;
}
}
}
}
Example 1.
While in a directory that holds my test.js file I tried the node test.js command doesn't print anything to terminal, it just moves to a new line.
js git:master ❯ node test.js
js git:master ❯
This is of course logical - the script gets loaded into the memory, it get's executed, the result is printed onto a screen (nothing visible since no function calls have been made within the script) and after that the memory is freed (variables destroyed). It is obvious that from here I cannot do something like countdownFrom(10); or var pet = createPet("Charlie");.
Example 2.
When I enter interactive mode (REPL) I do a:
require('./test')
which prints an empty object {} onto a new line. If I then try my countDownFrom(10); I get the following error:
ReferenceError: countDownFrom is not defined
at repl:1:9
at REPLServer.defaultEval (repl.js:269:27)
at bound (domain.js:287:14)
at REPLServer.runBound [as eval] (domain.js:300:12)
at REPLServer. (repl.js:439:12)
at emitOne (events.js:95:20)
at REPLServer.emit (events.js:182:7)
at REPLServer.Interface._onLine (readline.js:211:10)
at REPLServer.Interface._line (readline.js:550:8)
at REPLServer.Interface._ttyWrite (readline.js:827:14)
It seems to me that Node does not load and "hold" the script like a browser does. I wish to be able to do things like these:
var pet = createPet(/name/);
pet.getName();
pet.setSex("female");
// ... etc
P.S. I did try loading the whole test.js in a variable like so var t = require("./test"); but also no luck:
'> t.createPet("Charlie");
TypeError: t.createPet is not a function
What is the right way to go about this? Thanks!

preprocessor to replace javascript keywords

I am using the Angular version of the $q library however this would also be relevant for the original q library.
Usage example:
$q
.when(someFunction)
.then(function(){
// ..
})
.catch(function(){
// ..
})
.finally(function(){
// ..
});
Unfortunately some function names (e.g. finally) conflict with javascript keywords.
From the Angular reference:
"Because finally is a reserved word in JavaScript and reserved keywords are not supported as property names by ES3, you'll need to invoke the method like promise['finally'](callback) to make your code IE8 and Android 2.x compatible."
ECMA-262, the official standard, available at http://www.ecma-international.org/publications/standards/Ecma-262.htm, states:
7.6.1.1 Keywords
The following tokens are ECMAScript keywords and may not be used as
Identifiers in ECMAScript programs.
break do instanceof typeof
case else new var
catch finally return void
continue for switch while
debugger function this with
default if throw
delete in try
This means that the first example has to be changed into the following code to get it working with IE8:
$q
.when(someFunction)
.then(function(){
// ..
})
['catch'](function(){
// ..
})
['finally'](function(){
// ..
});
As this code is harder to maintain I am looking for a javascript preprocessor (maybe a grunt task) which turns the first example into the IE8 compatible version.
Is there such a preprocessor?
Your friendly neighborhood Stef Panner created such a tool just for that a few months ago. It's called es3-safe-recast. There is also a grunt task for it.
Let's go through what it does. First it requires the Esprima package for JS syntax tree analysis and recast on top of it that is built for these conversions:
'use strict';
var esprima = require('esprima');
var recast = require('recast');
var Visitor = recast.Visitor;
var types = recast.types;
var namedTypes = types.namedTypes;
var builders = types.builders;
Then it contains a big map of all the identifiers - just like your list:
identifierToLiteral.finally = true; // here is `finally` for example
Here is how it parses the tree and replaces it with a visitor:
var ES6Safe = Visitor.extend({
visitProperty: function(node) { // go through all properties
// check if need to replace name with literal
if (namedTypes.Identifier.check(node.key) && identifierToLiteral[node.key.name]) {
node.key = builders.literal(node.key.name);
}
return this.genericVisit(node);
},
visitMemberExpression: function(node) { // and all member expressions
var property = node.property;
var newNode;
// check if need to replace name with literal
if (namedTypes.Identifier.check(property) && identifierToLiteral[property.name]) {
newNode = builders.memberExpression(node.object, builders.literal(property.name), true);
} else {
newNode = node;
}
return this.genericVisit(newNode);
}
});
Finally, it runs the code through recast:
ast = recast.parse(source, { esprima: esprima } );
new ES6Safe().visit(ast);
code = recast.print(ast).code;
Producing the safe version of the above code.

Tiny JavaScript library stopped working

For a previous question I answered here on SO, I made the following library.
(function(){
var LS = function(){
return new LS.fn.init();
};
LS.fn = LS.prototype ={
//Check to see if the browser suports LocalStorage
init : function(){
this.ls = (typeof(Storage)!=="undefined") ? false : true;
return this;
}
}
LS.fn.init.prototype = LS.fn;
LS.fn.init.prototype = {
set : function(name, val){
if(this.ls) localStorage.setItem(name, val);
},
get : function (name){
if(this.ls) return localStorage.getItem(name);
},
remove : function(name){
if(this.ls) localStorage.removeItem(name);
},
test: function(){
alert("hi")
}
}
window.LS = window.ls = LS;
})();
This was working fine until recently.
I am getting errors like:
LS.set("foo", "bar"); // Error: undefined is not a function...
Even the following code gives an error:
LS.test();
Why has this stopped working?
Thank you.
NOTE: Previously, I had a typo, but now I have fixed that and it still doesn't work.
Your window.LS is initialized to a function, and you seem to use it as if you expect it to be initialized to the return value of that function.
Change this line:
window.LS = window.ls = LS;
to:
window.LS = window.ls = LS();
Also, try to simplify your library code. It seems very convoluted. At the very least comment it out a bit to indicate what the various parts do/are used for. The part with the typo (LS.protorype) isn't used anywhere for example.

How do I limit the node repl's access to internal node modules?

In a previous question I figured out how to eliminate unwanted global variables from the repl context. However, I figured out that the repl automatically has access to ALL internal node modules without the use of require. I have no idea how to disable this. I even tried overriding the module variables in the repl itself and it doesn't work.
> fs = "test";
> fs
It still displays fs original value. This is very unfortunate because I'm trying to expose a public repl but it gives them access to the entire server.
Any ideas?
As you said, the REPL has access to core modules.
(however, after checking, I am able to override them with node 0.10.20, so there should be a solution)
> fs
> { Stats: [Function], …
> fs = 'hello';
> fs
'hello'
A better way would be to just override repl._builtinLibs before creating a repl instance.
var repl = require('repl');
repl._builtinLibs = [];
repl.start('> ');
Also, it's fairly trivial to white-list repl commands if you don't want to expose commands like .save or .load.
var allowedReplCmds = ['.break', '.clear', '.help'];
var newRepl = repl.start('> ');
for (var key in newRepl.commands)
if (!allowedReplCmds.contains(key))
delete replInstance.commands[key];
Note: Arrays don't normally have a contains method so I added one.
Array.prototype.contains = function(v) {
for(var i = 0; i < this.length; i++) {
if(this[i] === v) return true;
}
return false;
};
If you want to remove variables from the repl instance's global scope see this question.
Please note that it is very unsafe to expose a REPL to the public.
You can easily crash the whole server
> setTimeout(function () { throw 'bye bye!'; }, 0);
Errors that happen in async callbacks are not caught by the REPL and bring down the node.js instance.
You can block the server
> while(true) {};
Your best bet would be to code your own REPL in a separate process with child_process, readline and vm. Here's a starting point:
The master:
// master.js
var fork = require('child_process').fork;
// functions exposed to the repl
var replApi = {
hello: function () {
return 'world!';
},
unknown: function () {
return 'unknown';
}
};
function forkRepl() {
var repl = fork('./child_repl');
repl.on('message', function (command) {
var fun = replApi[command] || replApi.unknown;
repl.send(fun());
});
// restart the repl if it dies
repl.on('exit', forkRepl);
}
forkRepl();
and the separate process for the repl:
// child_repl.js
var readline = require('readline'),
vm = require('vm');
var context = vm.createContext({
hello: function () {
process.send('hello');
}
});
var rl = readline.createInterface({
input: process.stdin,
output: process.stdout
});
rl.on('line', function (line) {
vm.runInContext(line, context, 'repl.vm');
});
process.on('message', function (message) {
console.log('master:', message);
});
rl.prompt();

Categories

Resources