Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/espadrine/localeval
Safe ES5 eval() that doesn't leak globals.
https://github.com/espadrine/localeval
Last synced: 10 days ago
JSON representation
Safe ES5 eval() that doesn't leak globals.
- Host: GitHub
- URL: https://github.com/espadrine/localeval
- Owner: espadrine
- Created: 2013-04-03T03:37:06.000Z (over 11 years ago)
- Default Branch: master
- Last Pushed: 2021-10-03T12:21:33.000Z (about 3 years ago)
- Last Synced: 2024-10-11T14:38:54.746Z (about 1 month ago)
- Language: JavaScript
- Homepage: http://espadrine.github.io/localeval/
- Size: 46.9 KB
- Stars: 34
- Watchers: 3
- Forks: 5
- Open Issues: 1
-
Metadata Files:
- Readme: readme.md
Awesome Lists containing this project
README
# Local Eval
Evaluate a string of JS code without access to the global object.
In Node.js, always use that instead of `eval()`. Always.
In the browser, do not expect it to be able to safely execute untrusted code
yet.API:
localeval(code :: String, sandbox :: Object) :: Object.
localeval(code :: String, sandbox :: Object,
options :: Object, cb :: Function)- `code`: string of JS code.
- `sandbox`: object whose values will be in the global object in the sandbox.
- `options`: object containing the following optional fields:
- `timeout`: number of milliseconds that the child process has to run the
code, beyond which it will be killed.
- `uid`: user id under which the child process must be set, if any.
- `gid`: group id under which the child process must be set, if any.It returns the last evaluated piece of JS code in `code`, if no timeout is
given. Otherwise, after at most `timeout` milliseconds, the callback gives that
result as a parameter: `function(error, result) {…}`.Node.js example:
```javascript
var localeval = require('localeval');
localeval('console.log("Do I have access to the console?")'); // Throws.
localeval.clear(); // Kills processes used internally.
```Browser example (experimental):
```html
alert(localeval('a + b', {a: 14, b: 18}))
```You may find an example of use in browser code in `main.html`.
# Security
In Node.js, the following barriers are in place:
- The code executes in a process-separated environment, benefitting from
OS-level security protections such as memory separation. That is true for both
the asynchronous and the synchronous version.
- The code can be put on a timeout, to ensure it cannot loop indefinitely to
cause a denial of service.
- The code can be set to a zero-access user ID, ensuring that even if there was
a vulnerability that allowed file system access, the OS would prevent reading
confidential information, overwriting it, or executing sensitive code.
- The code executes inside of a V8 isolate, which ensures the execution is
separated from the process' code (which after all needs enough access to send
the result back to the main process). Thus the code has a separate object
graph and cannot affect that of the process it runs in.
The environment is destroyed afterwards, as the whole process is exited.
- On top of that, the isolate sandbox is crippled: only whitelisted globals are
accessible. The others are not just syntactically shadowed, but outright
garbage-collected.# Warning
## In Node.js
We strongly recommend to set a timeout, and to set a uid and gid.
## In the browser
The following inputs leak:
- `({}).constructor.getOwnPropertyNames = function(){return 'leak';}`
- `Function("this.foo = 'leak'")()`If a timeout is given, an attacker can still use XHR:
- `Function("this.XMLHttpRequest(…); …")()`
That said, it strives to achieve the following:
1. All local and global variables are inaccessible.
2. Variables defined while evaluating code don't pollute any scope.
3. Evaluated code cannot fiddle with global object's properties.
Think
`localeval('([]).__proto__.push = function(a) { return "nope"; }')`.# Things to try
In comments are what should be executed outside the sandbox.
```js
String.prototype.slice = function() { return 'leaked'; };
// 'nice'.slice(1) === 'ice'
String.fromCharCode = function() { return 'leaked'; };
// String.fromCharCode(42) === '*'
// var foo = 1
foo = 7
this.foo = 7
window.foo = 7
eval('foo = 7')
// foo === 1
delete Number.parseInt
// Number.parseInt('1337') === 1337
String.prototype.leak = function() { return 'leak'; }
// try { ''.leak() } catch(e) { /not a function/.test(e.message) }
```---
This work is licensed under the Creative Commons Attribution 3.0 Unported
License. To view a copy of this license, visit
.