From 3102c001e30cfe772009bdb3ffbb98ec668c82fa Mon Sep 17 00:00:00 2001 From: matthaywardwebdesign Date: Sat, 29 Dec 2018 21:42:02 +1100 Subject: [PATCH] Initial commit --- .babelrc | 4 + .gitignore | 2 + README.md | 154 ++++++++++++++++ example/index.js | 36 ++++ package.json | 24 +++ src/Errors.js | 7 + src/ModuleLoader.js | 82 +++++++++ src/NodeSecurity.js | 236 ++++++++++++++++++++++++ src/NodeSecurity.test.js | 288 ++++++++++++++++++++++++++++++ src/index.js | 4 + src/plugins/NodeSecurityPlugin.js | 13 ++ 11 files changed, 850 insertions(+) create mode 100644 .babelrc create mode 100644 .gitignore create mode 100644 README.md create mode 100644 example/index.js create mode 100644 package.json create mode 100644 src/Errors.js create mode 100644 src/ModuleLoader.js create mode 100644 src/NodeSecurity.js create mode 100644 src/NodeSecurity.test.js create mode 100644 src/index.js create mode 100644 src/plugins/NodeSecurityPlugin.js diff --git a/.babelrc b/.babelrc new file mode 100644 index 0000000..372ebe8 --- /dev/null +++ b/.babelrc @@ -0,0 +1,4 @@ +{ + "presets": ["es2015"], + "plugins": ["transform-object-rest-spread", "transform-class-properties"] +} \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..76add87 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +node_modules +dist \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..baa4e6f --- /dev/null +++ b/README.md @@ -0,0 +1,154 @@ +# NodeSecurity +:key: The easiest way to control what npm modules can access + +## Overview +This repo / package was inspired a Medium post by David Gilbertson - [https://hackernoon.com/npm-package-permissions-an-idea-441a02902d9b](https://hackernoon.com/npm-package-permissions-an-idea-441a02902d9b) + +> Imagine a package, created and maintained by npm (or someone equally trustworthy and farsighted). Let’s call it @npm/permissions. + +> You would include this @npm/permissions package as the first import in your app, either in a file, or you run your app like node -r @npm/permissions index.js. + +> This would override require() to enforce the permissions stated in a package’s package.json permissions property. + +With the exception of some small differences, like not using package.json to manage permissions, this package +attempts to accomplish this goal. + +## How it works +NodeSecurity works by overriding the Node.JS `require()` function, allowing us to enforce access constraints. + +## Usage + +```bash +npm install node-security +``` + +Firstly include NodeSecurity in your project at the very top of your applications entrypoint (before any other requires) and create a new instance. + +```javascript + const nodesecurity = require( 'node-security' ); + const NodeSecurity = new nodesecurity(); +``` + +**Note:** If you're using the ES6 imports you'll need to create a seperate file that is imported at the entrypoint +of your application. Without doing this it won't be possible to configure NodeSecurity before any other modules are loaded. + +**Configure NodeSecurity** + +```javascript +NodeSecurity.configure({ + /** + * The 'core' section controls + * global access to built in modules. By default + * all core modules are disabled. + */ + core: { + fs: true, + path: true, + /* You can disable specific module functions */ + os: { + arch: false, + cpus: false, + } + }, + /** + * The 'module' section controls + * per module access to built in modules. This allows + * us to disable access globally by allow it on a per + * module basis. + */ + module: { + axios: { + http: true, + https: true, + } + }, + /** + * The 'env' section controls what environment + * variables are accessible via process.env + */ + env: { + API_KEY: true, + API_HOST: true, + } +}); +``` + +:tada: **And you're done!** :tada: + +All required / imported modules from this point onwards will have to be allowed by our configuration. + +## Example + +Here's an example script! + +```javascript +/* Import and create a new instance of NodeSecurity */ +const nodesecurity = require( 'node-security' ); +const NodeSecurity = new nodesecurity(); + +/* Configure NodeSecurity */ +NodeSecurity.configure({ + core: { + /* Define global fs access */ + fs: false, + /* Enable other core modules we'll need */ + stream: true, + util: true, + path: true, + os: { + /* Deny access to OS arch */ + arch: false, + }, + assert: true, + }, + module: { + /* Allow fs-extra to access fs */ + 'fs-extra': { + fs: true, + } + } +}); + +/* This won't throw an error as fs-extra is allowed to access fs */ +require( 'fs-extra' ); + +/* Accessing fs directly will throw an error */ +require( 'fs' ); + +/* Accessing os.arch will throw an error */ +const os = require( 'os' ); +os.arch(); +``` + +## Plugins + +You can extend the functionality of NodeSecurity by creating a plugin. For example you could create a plugin to allow http/s requests to only be made to specific servers. + +An example plugin can be found at `src/plugins/NodeSecurityPlugin.js` + +Plugins work by providing a way to override the default functionality of a core module. By default every Node core module (fs, os, etc) has a plugin loaded that allows for module methods to be disabled. + +Including your own plugin is as simple as adding a plugins section to your configuration. + +```javascript +plugins: { + http: MyHTTPPlugin +} +``` + +## Contributing + +Building the package + +``` +npm run build +``` + +Running the test suite + +```bash +npm test +``` + +## Ideas +- Include a set of default plugins that allow for more granular filesystem and network access. \ No newline at end of file diff --git a/example/index.js b/example/index.js new file mode 100644 index 0000000..5b78ebf --- /dev/null +++ b/example/index.js @@ -0,0 +1,36 @@ +/* Import and create a new instance of NodeSecurity */ +const nodesecurity = require( '../dist/index.js' ); +const NodeSecurity = new nodesecurity(); + +/* Configure NodeSecurity */ +NodeSecurity.configure({ + core: { + /* Define global fs access */ + fs: false, + /* Enable other core modules we'll need */ + stream: true, + util: true, + path: true, + os: { + /* Deny access to OS arch */ + arch: false, + }, + assert: true, + }, + module: { + /* Allow fs-extra to access fs */ + 'fs-extra': { + fs: true, + } + } +}); + +/* This won't throw an error as fs-extra is allowed to access fs */ +require( 'fs-extra' ); + +/* Accessing fs directly will throw an error */ +require( 'fs' ); + +/* Accessing os.arch will throw an error */ +const os = require( 'os' ); +os.arch(); \ No newline at end of file diff --git a/package.json b/package.json new file mode 100644 index 0000000..5a089de --- /dev/null +++ b/package.json @@ -0,0 +1,24 @@ +{ + "name": "node-security", + "version": "0.0.1", + "description": ":key: The easiest way to control what npm modules can access", + "main": "dist/index.js", + "scripts": { + "build": "rimraf dist/ && babel ./src --out-dir dist/ --ignore **/*.test.js,./node_modules,./.babelrc,./package.json,./npm-debug.log --copy-files", + "test": "mocha --compilers js:babel-core/register --recursive src/" + }, + "author": "Matt Hayward", + "license": "MIT", + "devDependencies": { + "babel-cli": "^6.26.0", + "babel-core": "^6.26.3", + "babel-plugin-transform-class-properties": "^6.24.1", + "babel-plugin-transform-object-rest-spread": "^6.26.0", + "babel-preset-es2015": "^6.24.1", + "chai": "^4.2.0", + "fs-extra": "^7.0.1", + "mocha": "^5.2.0", + "rimraf": "^2.6.2", + "trash": "^4.3.0" + } +} diff --git a/src/Errors.js b/src/Errors.js new file mode 100644 index 0000000..baeae02 --- /dev/null +++ b/src/Errors.js @@ -0,0 +1,7 @@ +const ERROR_ALREADY_CONFIGURED = 'NodeSecurity has already been configured.'; +const ERROR_NOT_ALLOWED_TO_LOAD = ( module, parent ) => `NodeSecurity has blocked an attempt to access module '${module}'. Parent modules = ['${parent.join( ', ' )}']`; + +export default { + ERROR_ALREADY_CONFIGURED, + ERROR_NOT_ALLOWED_TO_LOAD, +}; \ No newline at end of file diff --git a/src/ModuleLoader.js b/src/ModuleLoader.js new file mode 100644 index 0000000..5901707 --- /dev/null +++ b/src/ModuleLoader.js @@ -0,0 +1,82 @@ +import Module from 'module'; +import Errors from './Errors'; + +class ModuleLoader { + constructor( config ) { + this.config = config; + + /** + * Store an instance of the original Module._load + * functionaliy. + **/ + this.originalLoad = Module._load; + } + + getOriginalLoader = () => { + return this.originalLoad; + } + + /* Determines whether this module was loaded by a parent module */ + findParentModules( parent ) { + let parents = []; + let currentModule = parent; + + /* Recursively walk up the parent tree, creating a list of parents */ + while ( currentModule ) { + parents.push( currentModule.filename ); + currentModule = currentModule.parent; + } + + return parents; + } + + isModuleAllowed( request, parent ) { + /* Determine the friendly module name of the parent module */ + const parentModules = this.findParentModules( parent ); + + /** + * Loop over the parent modules, checking whether any of them specify + * individual module permissions. Keep in mind that individual module + * permissions will always override any core / global preferences. + * */ + for ( let i = 0; i < parentModules.length; i++ ) { + const parentModule = parentModules[i]; + + /* Check whether config was provided for this module */ + if ( this.config.module[ parentModule ] != null ) { + /* Config was provided, merge the config with the core config */ + const moduleConfig = this.config.module[parentModule]; + + if ( moduleConfig[ request ] != null ) { + return moduleConfig[ request ]; + } + } + } + + /* Check whether we've blocked access to this module */ + if ( this.config.core[ request ] != null && this.config.core[ request ] === false ) { + return false; + } + + return true; + } + + /* Called when a module is attempted to be loaded */ + load = ( request, parent, isMain ) => { + /* Check whether loading this module is allowed */ + const allowedToLoad = this.isModuleAllowed( request, parent ); + + /* If not allowed to load then throw an error */ + if ( !allowedToLoad ) { + /* Get parent tree */ + const parents = this.findParentModules( parent ); + throw new Error( Errors.ERROR_NOT_ALLOWED_TO_LOAD( request, parents )); + } + + const module = this.originalLoad( request, parent, isMain ); + + return module; + } +}; + +export default ModuleLoader; diff --git a/src/NodeSecurity.js b/src/NodeSecurity.js new file mode 100644 index 0000000..e44bcbe --- /dev/null +++ b/src/NodeSecurity.js @@ -0,0 +1,236 @@ +/** + * Node Security + * + * Core Concept: + * The core concept behind NodeSecurity is controlling access to + * the NodeJS built-in / core modules (fs, net) etc. With addition + * to the ability to block / allow core modules from being used, + * the plugin architecture also allows us to create more fine + * grained access control. One example of this would only be + * permitting file access to a particular set of files. + * + * Default Behaviour: + * By default all core modules are blocked and will throw runtime + * exceptions when they are loaded. + * + * Plugins: + * A NodeSecurity plugin allows a core or third party module to + * have its implementation replaced, allowing for a + * finer grained control. + */ + +import Module from 'module'; +import ModuleLoader from './ModuleLoader'; +import NodeSecurityPlugin from './plugins/NodeSecurityPlugin'; +import Errors from './Errors.js'; + +class NodeSecurity { + constructor() { + /* Create an object to store the current configuration */ + this.config = { + /* By default all core node modules are disabled */ + core: { + assert: false, + buffer: false, + child_process: false, + cluster: false, + crypto: false, + dgram: false, + dns: false, + domain: false, + events: false, + fs: false, + http: false, + http2: false, + https: false, + inspector: false, + module: false, + net: false, + os: false, + path: false, + perf_hooks: false, + punycode: false, + querystring: false, + readline: false, + stream: false, + string_decoder: false, + timers: false, + tls: false, + tty: false, + url: false, + util: false, + v8: false, + vm: false, + zlib: false, + /** + * We also consider node-security itself to be a core + * module. This protects against downstream objects + * trying to override us. + **/ + 'node-security': false, + }, + /** + * The module section of the config stores permissions + * for individual modules. + **/ + module: {}, + /** + * The env config option is specified removes all of the + * values except for those specified. + */ + env: null, + /** + * Create an object to store all of the plugins + * Any additions to the core plugins needed to be included here. + **/ + plugins: { + assert: NodeSecurityPlugin, + buffer: NodeSecurityPlugin, + child_process: NodeSecurityPlugin, + cluster: NodeSecurityPlugin, + crypto: NodeSecurityPlugin, + dgram: NodeSecurityPlugin, + dns: NodeSecurityPlugin, + domain: NodeSecurityPlugin, + events: NodeSecurityPlugin, + fs: NodeSecurityPlugin, + http: NodeSecurityPlugin, + http2: NodeSecurityPlugin, + https: NodeSecurityPlugin, + inspector: NodeSecurityPlugin, + module: NodeSecurityPlugin, + net: NodeSecurityPlugin, + os: NodeSecurityPlugin, + path: NodeSecurityPlugin, + perf_hooks: NodeSecurityPlugin, + punycode: NodeSecurityPlugin, + querystring: NodeSecurityPlugin, + readline: NodeSecurityPlugin, + stream: NodeSecurityPlugin, + string_decoder: NodeSecurityPlugin, + timers: NodeSecurityPlugin, + tls: NodeSecurityPlugin, + tty: NodeSecurityPlugin, + url: NodeSecurityPlugin, + util: NodeSecurityPlugin, + v8: NodeSecurityPlugin, + vm: NodeSecurityPlugin, + zlib: NodeSecurityPlugin, + }, + }; + + /** + * Store whether or not NodeSecurity has already been configured. We use + * this value to protect against it being reconfigured by a malicious + * third party module. + * */ + this.configured = false; + } + + /* Called by the user with their own NodeSecurity configuration */ + configure = ( userConfig = {}) => { + /** + * Firstly check whether we've already configured NodeSecurity. If so + * let's throw an error. + * */ + if ( this.configured ) { + throw new Error( Errors.ERROR_ALREADY_CONFIGURED ); + } + + /* Combine the default config with the user provided config */ + this.config = { + core: { ...this.config.core, ...userConfig.core || {}}, + module: { ...this.config.module, ...userConfig.module || {}}, + env: userConfig.env, + plugins: { ...this.config.plugins, ...userConfig.plugins || {}}, + }; + + /** + * Replace all of the module names with their resolved package + * paths. + */ + this.config.module = Object + .keys( this.config.module ) + .map( key => ({ + key: require.resolve( key ), + value: this.config.module[key], + })) + .reduce(( result, current ) => { + result[current.key] = current.value; + return result; + }, {}); + + /** + * Load all of the plugins. + * Firstly loop over any core config and find key that have + * object values. We'll load up the plugin for these. + */ + Object + .keys( this.config.core ) + .filter( key => key ) + .forEach( key => { + const moduleConfig = this.config.core[key]; + /** + * If the value is an object and we have a plugin + * for this module then load it + * */ + if ( typeof moduleConfig === 'object' && this.config.plugins[ key ]) { + new this.config.plugins[ key ]( key, moduleConfig ); + } + }); + + + /** + * Check if the the 'env' config option was provided and if so + * modify the process.env object to only contain the + * environment variables specified. + */ + if ( this.config.env ) { + /* Create a new object that we'll replace process.env with */ + const newEnv = Object + .keys( this.config.env ) + .filter( key => this.config.env[key] ) + .map( key => ({ + key, + value: process.env[key], + })) + .reduce(( result, item ) => { + result[item.key] = item.value; + return result; + }, {}); + + /* Replace process.env with the new environment object */ + process.env = newEnv; + } + + /* Override the Module._load function with our own */ + if ( !this.moduleLoader ) { + this.moduleLoader = new ModuleLoader( this.config ); + Module._load = this.moduleLoader.load; + } + + /* Finally, record that we've fully configured NodeSecurity */ + this.configured = true; + } + + /** + * Resets the Module._load override + * Note: This doesn't provide an easy way for malicious modules + * to just bypass all of our checks. This is because each time NodeSecurity + * is configured it takes a new reference to the current definition of Module._load. + * This means that any instances of NodeSecurity created after the first instance + * will still apply the rules of the first instance as its copy of Module._load + * is not the Node internal one, but is the one that NodeSecurity overrided one. + * This means that each attempted module load will be processed by each configured instance + * of NodeSecurity and that calling reset() on an instance of NodeSecurity on affects that + * instance not others created before it */ + reset = () => { + /* Only do the reset if required */ + if ( this.moduleLoader ) { + Module._load = this.moduleLoader.getOriginalLoader(); + this.configured = false; + } + } +} + +export default NodeSecurity; diff --git a/src/NodeSecurity.test.js b/src/NodeSecurity.test.js new file mode 100644 index 0000000..ddc19ab --- /dev/null +++ b/src/NodeSecurity.test.js @@ -0,0 +1,288 @@ +import nodesecurity from './NodeSecurity'; +import { expect } from 'chai'; +let NodeSecurity; + +describe( 'NodeSecurity', () => { + /* Create a new instance of NodeSecurity before each test */ + beforeEach(() => { + NodeSecurity = new nodesecurity(); + }); + + afterEach(() => { + /* After each test reset the Module._load implementation */ + NodeSecurity.reset(); + + /** + * Remove all the modules from the module cache. + * The reason we have to do this is because + * the module cache remains the same between + * each test. This does highlight a potential + * issue with how module access is determined. + * + * To demonstrate let's use a real world example. + * The package 'trash' has 'fs-extra' as a + * dependency. 'fs-extra' also has 'fs' as a + * dependency. If we run one test where we + * load up 'trash' and the config allows it to + * pass, 'trash' and all of its dependencies + * including 'fs-extra' will be cached. + * + * If we now reconfigure and now do a fresh test + * of NodeSecurity and set the config to not allow + * 'fs-extra' filesystem access and go ahead and + * require 'trash' we'd expect that it wouldn't + * allow it to pass because 'fs-extra' is a + * dependency of 'trash' and 'fs-extra' doesn't + * have access to the filesystem. What happens + * instead is that because 'trash' is cached + * it skips the entire dependency resolution + * process and loads our cached module because + * our config allows 'trash' to be loaded. + * */ + Object.keys( require.cache ).forEach( key => { + delete require.cache[ key ]; + }); + }); + + it( 'should allow to an instance of NodeSecurity to be created.', () => { + expect( NodeSecurity ).to.be.an.instanceof( nodesecurity ); + }); + + it( 'should throw an exception when trying to configure NodeSecurity twice.', () => { + NodeSecurity.configure(); + + /* Attempt to configure NodeSecurity again */ + expect(() => { + NodeSecurity.configure(); + }).to.throw(); + }); + + it( 'should have all of the core modules disabled by default.', () => { + /* Create a list of all of the core modules */ + const coreModules = [ + 'assert', 'buffer', 'child_process', 'cluster', 'crypto', + 'dgram', 'dns', 'domain', 'events', 'fs', 'http', 'https', + 'http2', 'inspector', 'module', 'net', 'os', 'path', 'punycode', + 'querystring', 'perf_hooks', 'readline', 'stream', + 'string_decoder', 'timers', 'tls', 'tty', 'url', 'util', + 'v8', 'vm', 'zlib', 'module', 'node-security', + ]; + + /* Loop over each one and make sure they are set to false */ + coreModules.forEach( module => { + expect( NodeSecurity.config.core[module] ).to.be.false; + }); + }); + + it( 'should correctly merge the core config and user config.', () => { + NodeSecurity.configure({ + core: { + fs: true, + } + }); + + expect( NodeSecurity.config.core.fs ).to.be.true; + expect( NodeSecurity.config.core.os ).to.not.be.null; + }); + + it( 'should throw an exception when trying to load a blocked module', () => { + /* Configure the NodeSecurity instance */ + NodeSecurity.configure(); + + /* Attempt to load the os module and expect an error */ + expect(() => { + require( 'os' ); + }).to.throw(); + }); + + it( 'should not throw an exception when loading an allowed module', () => { + /* Configure the NodeSecurity instance */ + NodeSecurity.configure({ + core: { + os: true, + } + }); + + /* Attempt to load the os module */ + expect(() => { + require( 'os' ); + }).not.to.throw(); + }); + + it( 'should throw an exception when trying to load a third party module that loads a core module', () => { + /* Configure the NodeSecurity instance */ + NodeSecurity.configure(); + + expect(() => { + require( 'fs-extra' ); + }).to.throw(); + }); + + it( 'should not throw an exception when trying to load a third party module that access allowed core modules', () => { + /* Configure the NodeSecurity instance */ + NodeSecurity.configure({ + core: { + fs: true, + stream: true, + util: true, + path: true, + os: true, + assert: true, + } + }); + + /* Expect that loading fs-extra, which loads fs, will fail */ + expect(() => { + require( 'fs-extra' ); + }).not.to.throw(); + }); + + it( 'should not throw an exception when a globally blocked core module is accessed by a third party module that has the core module allowed in module specific configuration.', () => { + /* Configure the NodeSecurity instance */ + NodeSecurity.configure({ + core: { + stream: true, + util: true, + path: true, + os: true, + assert: true, + }, + module: { + 'fs-extra': { + fs: true, + } + } + }); + + /* Expect that loading fs will fail as it isn't allowed */ + expect(() => { + require( 'fs' ); + }).to.throw(); + + /** + * Expect that loading fs-extra will work, despite it loading fs + * as we've given fs-extra permission to load the fs module + */ + expect(() => { + require( 'fs-extra' ); + }).not.to.throw(); + }); + + it( 'should throw an exception when a globally allowed core module is accessible by a third party module that has the core module blocked in module specific configuration.', () => { + /* Configure the NodeSecurity instance */ + NodeSecurity.configure({ + core: { + fs: true, + stream: true, + util: true, + path: true, + os: true, + assert: true, + }, + module: { + 'fs-extra': { + fs: false, + } + }, + }); + + /** + * Expect that loading fs-extra won't work as despite allowing fs + * globally, we've specifically disallowed it for fs-extra + */ + expect(() => { + require( 'fs-extra' ); + }).to.throw(); + }); + + it( 'should throw an exception when the parent module is denied access to a core module that is accessed through a child module, even when the configuration allows the child module to have access', () => { + /* Configure the NodeSecurity instance */ + NodeSecurity.configure({ + core: { + stream: true, + util: true, + path: true, + os: true, + assert: true, + events: true, + child_process: true, + crypto: true, + }, + module: { + trash: { + fs: false, + }, + 'fs-extra': { + fs: true, + } + } + }); + + expect(() => { + require( 'trash' ); + }).to.throw(); + + expect(() => { + require( 'fs-extra' ); + }).to.not.throw(); + }); + + it ( 'should block specific functions on core modules when the config specifies it', () => { + /* Configure the NodeSecurity instance */ + NodeSecurity.configure({ + core: { + os: { + arch: false, + } + } + }); + + /* Attempt to access os.arch */ + const os = require( 'os' ); + + expect(() => { + os.arch(); + }).to.throw(); + }); + + it( 'continue to honour the behaviour when a secondary instance of NodeSecurity is created and then reset.', () => { + /* Configure the NodeSecurity instance */ + NodeSecurity.configure(); + + /* Attempt to load the fs module and expect an error */ + expect(() => { + require( 'fs' ); + }).to.throw(); + + /* Create a new instance of NodeSecurity */ + const NodeSecurity2 = new nodesecurity(); + + /* Configure it then immediately reset it */ + NodeSecurity2.configure(); + NodeSecurity2.reset(); + + /* Ensure that attempting to load fs still throws an error */ + expect(() => { + require( 'fs' ); + }).to.throw(); + }); + + it( 'should strip disallowed environment variables from process.env when the env configuration option is provided.', () => { + /* Set an example value on the process.env object */ + process.env.SECRET_STUFF = 'Magic'; + process.env.PUBLIC_STUFF = 'Not magic'; + + /* Configure the NodeSecurity instance */ + NodeSecurity.configure({ + env: { + PUBLIC_STUFF: true, + } + }); + + /* Ensure the public environment variable is still present */ + expect( process.env.PUBLIC_STUFF ).not.to.be.undefined; + + /* Ensure the secret environment variable is not present */ + expect( process.env.SECRET_STUFF ).to.be.undefined; + }); +}); \ No newline at end of file diff --git a/src/index.js b/src/index.js new file mode 100644 index 0000000..35b2501 --- /dev/null +++ b/src/index.js @@ -0,0 +1,4 @@ +import NodeSecurity from './NodeSecurity'; +export { default as NodeSecurityPlugin } from './plugins/NodeSecurityPlugin'; +export default NodeSecurity; +module.exports = NodeSecurity; \ No newline at end of file diff --git a/src/plugins/NodeSecurityPlugin.js b/src/plugins/NodeSecurityPlugin.js new file mode 100644 index 0000000..1924115 --- /dev/null +++ b/src/plugins/NodeSecurityPlugin.js @@ -0,0 +1,13 @@ +class NodeSecurityPlugin { + constructor( moduleName, config ) { + const module = require( moduleName ); + /* Overwrite the required functions based on the config */ + Object.keys( config ).filter( key => !config[key] ).forEach( key => { + module[key] = () => { + throw new Error( `Attempt to access ${moduleName}.${key} was blocked` ); + } + }); + } +} + +export default NodeSecurityPlugin;