Restricting core module usage

Some core modules are very powerful, and we often depend on third-party modules that may perform powerful operations with little transparency.

This could lead to unintended vulnerabilities where user input is passed through a dependency tree that eventually leads to shell commands that could inadvertently allow for malicious input to control our server. While the chances of this happening seem rare, the implications are severe. Depending on our use case, if we can eliminate the risk, we're better off for it.

Let's write a small function that we can use to throw when a given core module is used thus allowing us to vet or at least monitor code (dependencies or otherwise) that uses the module.

To demonstrate, let's create a folder called core-restrict with an index.js file and an example.js file:

$ mkdir core-restrict
$ cd core-restrict
$ touch index.js example.js

In our index.js file we'll put the following code:

module.exports = function (name) {
require.cache[name] = {}
Object.defineProperty(require.cache[name], 'exports', {
get: () => { throw Error(`The ${name} module is restricted`) }
})
}

Now we can try it out with the example.js file:

const restrict = require('./')
restrict('child_process')

const cp = require('child_process')

If we run example.js:

$ node example.js 

It should throw an error, stating The child_process module is restricted.

This technique takes advantage of Node's module loading algorithm, it checks the loaded module cache (which we access through require.cache) for namespace before it attempts to load a built-in module. We override the cache with that namespace and use Object.defineProperty to make a property definition on the exports key that throws an error when the key is accessed.