0

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?

Community
  • 1
  • 1
CatDadCode
  • 58,507
  • 61
  • 212
  • 318

1 Answers1

2

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();
Community
  • 1
  • 1
Laurent Perrin
  • 14,671
  • 5
  • 50
  • 49
  • I appreciate it. I'm already running the repl instances in separate child processes where they have the freedom to blow up without any major server impact. I just need to figure out how to get rid of the core modules. I **really** hate that those are not configurable. I think I'll probably just fork the repl module and make those things configurable and publish the modified module. – CatDadCode Oct 16 '13 at 14:57
  • Yes, it's odd that you can't opt out. Even if you change the context, the builtins will be added. The devs probably decided that repl was unsafe by design and this wouldn't make things worse. – Laurent Perrin Oct 16 '13 at 15:05
  • Actually, I think you can opt out. You linked me to the module code. I think all I need to do is override `repl._builtinLibs` before I call `repl.start()`. I'm going to try that now. – CatDadCode Oct 16 '13 at 15:06
  • that's right. I didn't realize that. However, there are other builtin things that might get in your way, like `.save`: https://github.com/joyent/node/blob/master/lib/repl.js#L856 – Laurent Perrin Oct 16 '13 at 15:10
  • Good call. I'll have to check those out too but I think we're finally getting there. It worked by the way. Just do `repl._builtinLibs = [];` before starting the repl. I'm going to edit and accept your answer if that's alright. – CatDadCode Oct 16 '13 at 15:13
  • nice :) that might prove useful some day. – Laurent Perrin Oct 16 '13 at 15:14
  • I also figured out how to remove unwanted repl commands. I'm going to add that to your answer. – CatDadCode Oct 16 '13 at 15:38