Ecosyste.ms: Awesome

An open API service indexing awesome lists of open source software.

Awesome Lists | Featured Topics | Projects

https://github.com/discue/somewhat-secure-insecure-fn-executor

Tries to mitigate risks of untrusted code by running it in an isolated environment
https://github.com/discue/somewhat-secure-insecure-fn-executor

discue isolate js untrusted-code

Last synced: 10 days ago
JSON representation

Tries to mitigate risks of untrusted code by running it in an isolated environment

Awesome Lists containing this project

README

        


discue.io logo



[![GitHub tag](https://img.shields.io/github/tag/discue/somewhat-secure-insecure-fn-executor?include_prereleases=&sort=semver&color=blue)](https://github.com/discue/somewhat-secure-insecure-fn-executor/releases/)
[![Latest Stable Version](https://img.shields.io/npm/v/@discue/somewhat-secure-insecure-fn-executor.svg)](https://www.npmjs.com/package/@discue/somewhat-secure-insecure-fn-executor)
[![License](https://img.shields.io/npm/l/@discue/somewhat-secure-insecure-fn-executor.svg)](https://www.npmjs.com/package/@discue/somewhat-secure-insecure-fn-executor)


[![NPM Downloads](https://img.shields.io/npm/dt/@discue/somewhat-secure-insecure-fn-executor.svg)](https://www.npmjs.com/package/@discue/somewhat-secure-insecure-fn-executor)
[![NPM Downloads](https://img.shields.io/npm/dm/@discue/somewhat-secure-insecure-fn-executor.svg)](https://www.npmjs.com/package/@discue/somewhat-secure-insecure-fn-executor)


[![contributions - welcome](https://img.shields.io/badge/contributions-welcome-blue)](/CONTRIBUTING.md "Go to contributions doc")
[![Made with Node.js](https://img.shields.io/badge/Node.js->=20-blue?logo=node.js&logoColor=white)](https://nodejs.org "Go to Node.js homepage")

# somewhat-secure-insecure-fn-executor
**Don't let the funny title fool you**. There was definitely not enough testing to make sure this library can provide significant security for running untrusted code. What it **does** is to run untrusted code in an isolated environment with a minimum set of APIs to reduce the attack surface.

**Generally:** You should not run untrusted code anywhere.

And if you have to? Make sure you mitigate the risks on various levels:

**Mitigations provided by this module:**
- ✅ Allow only certain functions to be executed
- ✅ Disable code generation via e.g. `eval` and `new Function()`
- ✅ Limit access to I/O APIs like filesystem, socket, and http
- ✅ Ensure global variables are immutable

**Mitigations out-of-scope for this module:**
- ❌ Do not run code that was was obfuscated
- ❌ Run the sandbox with smallest possible set of permissions
- ❌ Run the container of the sandbox with smallest possible set of permissions
- ❌ Run the smallest number of services in the same account as the sandbox
- more.. :)

## Installation
```bash
npm install @discue/somewhat-secure-insecure-fn-executor
```

## Constraints
- Execution of `eval()`, `Function()`, `new Function()`, and `WebAssembly.*` is not allowed.
- Return values of scripts must be `Primitives`, or `Objects`. `Functions`, `Symbols` and others are not allowed.
- Built-in global variables cannot be changed.

## API
The main export of the module is a function. It expects the following parameters:
1. JS code as `string`
2. Optional: An object to be passed to the script as `args`.

The return value is an object with the following properties:
- **result**: The return value of the script. May be null.
- **logs**: Logs captured during script execution
- **durationMillis**: Duration of script execution in milliseconds
- **ExecutionError**: Details about a captured error. May be null.

```js
/**
* @typedef ExecutionResult
* @property {any} [result] the return value of the given script
* @property {Object} [logs] the logs captured during script execution
* @property {number} durationMillis duration of the script execution in milliseconds
* @property {ExecutionError} [error] error details captured during script execution
*/

/**
* @typedef ExecutionError
* @property {string} [cause] the cause from the caputured error. May be null.
* @property {number} [code] the code from the captured error. May be null.
* @property {Array.} stack the stack trace from the captured error.
* @property {string} message the message from the captured error.
*/

/**
* @param {string} script the script to run
* @param {object} args arguments to pass to the script
* @returns {ExecutionResult}
*/
const executor = require('@discue/somewhat-secure-insecure-fn-executor')

```

## Examples
### Simple script execution
The executor runs the script `return 1+1` and returns the result of the calculation.
```js
const executor = require('@discue/somewhat-secure-insecure-fn-executor')
const result = await executor('return 1+1')
// {
// "result": 2,
// "logs": {
// "log": [],
// "error": [],
// "warn": [],
// "info": []
// },
// "durationMillis": 0
// }
```

### Execution with parameters
To pass parameters to the script call the executor function with an object as second parameter. The parameter object will be available as `args` for execution of your script.
```js
const executor = require('@discue/somewhat-secure-insecure-fn-executor')
const result = await executor('return args.a + args.b', { a: 1, b: 3 })
// {
// "result": 4,
// "logs": {
// "log": [],
// "error": [],
// "warn": [],
// "info": []
// },
// "durationMillis": 0
// }
```

### Node globals are not available
The user provided scripts are executed in a dedicated v8 environment. NodeJS globals are not available in this context.

```js
const executor = require('@discue/somewhat-secure-insecure-fn-executor')
const result = await executor('process.exit(0)')
// {
// "logs": {
// "log": [],
// "error": [
// "ReferenceError: process is not defined"
// ],
// "warn": [],
// "info": []
// },
// "error": {
// "message": "process is not defined",
// "stack": [
// "ReferenceError: process is not defined",
// "at userSuppliedScript (file:///user-supplied-script.js:1:1)",
// "at runtime.js:38:24"
// ]
// },
// "durationMillis": 1
// }
```

### Error handling
Any exceptions occuring during script execution are captured and returned to the caller. The `error` object contains details of the exception: `cause`, `code`, `message`, and `stack`.
```js
const executor = require('@discue/somewhat-secure-insecure-fn-executor')
const result = await executor('eval(1+1)')
// {
// "logs": {
// "log": [],
// "error": [
// "Error: \"eval\" is not allowed in this context."
// ],
// "warn": [],
// "info": []
// },
// "error": {
// "message": "\"eval\" is not allowed in this context.",
// "stack": [
// "Error: \"eval\" is not allowed in this context.",
// "at global. (file:///code-generation.js:1:19)",
// "at userSuppliedScript (file:///user-supplied-script.js:2:9)",
// "at runtime.js:38:24"
// ]
// },
// "durationMillis": 0
// }
```

## License
[MIT](https://choosealicense.com/licenses/mit/)