isolated-function 0.0.5 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE.md +0 -0
- package/README.md +167 -31
- package/package.json +14 -14
package/LICENSE.md
CHANGED
|
File without changes
|
package/README.md
CHANGED
|
@@ -1,15 +1,37 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
-
|
|
1
|
+
<h3 align="center">
|
|
2
|
+
<img
|
|
3
|
+
src="https://github.com/Kikobeats/isolated-function/blob/master/logo.png?raw=true"
|
|
4
|
+
width="200">
|
|
5
|
+
<br>
|
|
6
|
+
<p>isolated-function</p>
|
|
7
|
+
<a target="_blank" rel="noopener noreferrer nofollow"><img
|
|
8
|
+
src="https://img.shields.io/github/tag/Kikobeats/isolated-function.svg?style=flat-square"
|
|
9
|
+
style="max-width: 100%;"></a>
|
|
10
|
+
<a href="https://coveralls.io/github/Kikobeats/isolated-function"
|
|
11
|
+
rel="nofollow"><img
|
|
12
|
+
src="https://img.shields.io/coveralls/Kikobeats/isolated-function.svg?style=flat-square"
|
|
13
|
+
alt="Coverage Status" style="max-width: 100%;"></a>
|
|
14
|
+
<a href="https://www.npmjs.org/package/isolated-function" rel="nofollow"><img
|
|
15
|
+
src="https://img.shields.io/npm/dm/isolated-function.svg?style=flat-square"
|
|
16
|
+
alt="NPM Status" style="max-width: 100%;"></a>
|
|
17
|
+
</h3>
|
|
18
|
+
|
|
19
|
+
- [Install](#install)
|
|
20
|
+
- [Quickstart](#quickstart)
|
|
21
|
+
- [Minimal privilege execution](#minimal-privilege-execution)
|
|
22
|
+
- [Auto install dependencies](#auto-install-dependencies)
|
|
23
|
+
- [Execution profiling](#execution-profiling)
|
|
24
|
+
- [Resource limits](#resource-limits)
|
|
25
|
+
- [API](#api)
|
|
26
|
+
- [isolatedFunction(code, \[options\])](#isolatedfunctioncode-options)
|
|
27
|
+
- [code](#code)
|
|
28
|
+
- [options](#options)
|
|
29
|
+
- [timeout](#timeout)
|
|
30
|
+
- [timeout](#timeout-1)
|
|
31
|
+
- [=\> (fn(\[...args\]), teardown())](#-fnargs-teardown)
|
|
32
|
+
- [fn](#fn)
|
|
33
|
+
- [teardown](#teardown)
|
|
34
|
+
- [License](#license)
|
|
13
35
|
|
|
14
36
|
## Install
|
|
15
37
|
|
|
@@ -17,55 +39,169 @@
|
|
|
17
39
|
npm install isolated-function --save
|
|
18
40
|
```
|
|
19
41
|
|
|
20
|
-
##
|
|
42
|
+
## Quickstart
|
|
43
|
+
|
|
44
|
+
**isolated-functions** is a modern solution for running arbitrary code using Node.js.
|
|
21
45
|
|
|
22
46
|
```js
|
|
23
47
|
const isolatedFunction = require('isolated-function')
|
|
24
48
|
|
|
25
|
-
/*
|
|
26
|
-
const sum = isolatedFunction((y, z) => y + z
|
|
49
|
+
/* create an isolated-function, with resources limitation */
|
|
50
|
+
const [sum, teardown] = isolatedFunction((y, z) => y + z, {
|
|
51
|
+
memory: 128, // in MB
|
|
52
|
+
timeout: 10000 // in milliseconds
|
|
53
|
+
})
|
|
54
|
+
|
|
55
|
+
/* interact with the isolated-function */
|
|
56
|
+
const [value, profiling] = await sum(3, 2)
|
|
57
|
+
console.log({ value, profiling })
|
|
58
|
+
|
|
59
|
+
/* close resources associated with the isolated-function initialization */
|
|
60
|
+
await teardown()
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
The hosted code runs in a separate process with limited permissions, returning the result and profiling the execution.
|
|
64
|
+
|
|
65
|
+
### Minimal privilege execution
|
|
27
66
|
|
|
28
|
-
|
|
29
|
-
const result = await sum(3, 2)
|
|
67
|
+
The hosted code will be executed with minimal privilege. If you exceed your limit, an error will occur.
|
|
30
68
|
|
|
31
|
-
|
|
69
|
+
```js
|
|
70
|
+
const [fn, teardown] = isolatedFunction(() => {
|
|
71
|
+
const fs = require('fs')
|
|
72
|
+
fs.writeFileSync('/etc/passwd', 'foo')
|
|
73
|
+
})
|
|
74
|
+
|
|
75
|
+
await fn()
|
|
76
|
+
// => PermissionError: Access to 'FileSystemWrite' has been restricted.
|
|
32
77
|
```
|
|
33
78
|
|
|
34
|
-
|
|
79
|
+
Any of the following interaction will throw an error:
|
|
80
|
+
|
|
81
|
+
- Native modules
|
|
82
|
+
- Child process
|
|
83
|
+
- Worker Threads
|
|
84
|
+
- Inspector protocol
|
|
85
|
+
- File system access
|
|
86
|
+
- WASI
|
|
87
|
+
|
|
88
|
+
### Auto install dependencies
|
|
89
|
+
|
|
90
|
+
The hosted code is parsed for detecting `require`/`import` calls and install these dependencies:
|
|
35
91
|
|
|
36
92
|
```js
|
|
37
|
-
const isEmoji = isolatedFunction(emoji => {
|
|
93
|
+
const [isEmoji, teardown] = isolatedFunction(emoji => {
|
|
94
|
+
/* this dependency only exists inside the isolated function */
|
|
38
95
|
const isEmoji = require('is-standard-emoji')
|
|
39
96
|
return isEmoji(emoji)
|
|
40
97
|
})
|
|
41
98
|
|
|
42
99
|
await isEmoji('🙌') // => true
|
|
43
100
|
await isEmoji('foo') // => false
|
|
101
|
+
await teardown()
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
The dependencies, along with the hosted code, are bundled into a single file that will be evaluated at runtime.
|
|
105
|
+
|
|
106
|
+
### Execution profiling
|
|
107
|
+
|
|
108
|
+
Any hosted code execution has a profiling associated:
|
|
109
|
+
|
|
110
|
+
```js
|
|
111
|
+
/** make a function to consume ~128MB */
|
|
112
|
+
const [fn, teardown] = isolatedFunction(() => {
|
|
113
|
+
const storage = []
|
|
114
|
+
const oneMegabyte = 1024 * 1024
|
|
115
|
+
while (storage.length < 78) {
|
|
116
|
+
const array = new Uint8Array(oneMegabyte)
|
|
117
|
+
for (let ii = 0; ii < oneMegabyte; ii += 4096) {
|
|
118
|
+
array[ii] = 1
|
|
119
|
+
}
|
|
120
|
+
storage.push(array)
|
|
121
|
+
}
|
|
122
|
+
})
|
|
123
|
+
t.teardown(cleanup)
|
|
124
|
+
|
|
125
|
+
const [value, profiling] = await fn()
|
|
126
|
+
console.log(profiling)
|
|
127
|
+
// {
|
|
128
|
+
// memory: 128204800,
|
|
129
|
+
// duration: 54.98325
|
|
130
|
+
// }
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
### Resource limits
|
|
134
|
+
|
|
135
|
+
You can limit a **isolated-function** by memory:
|
|
136
|
+
|
|
137
|
+
```js
|
|
138
|
+
const [fn, teardown] = isolatedFunction(() => {
|
|
139
|
+
const storage = []
|
|
140
|
+
const oneMegabyte = 1024 * 1024
|
|
141
|
+
while (storage.length < 78) {
|
|
142
|
+
const array = new Uint8Array(oneMegabyte)
|
|
143
|
+
for (let ii = 0; ii < oneMegabyte; ii += 4096) {
|
|
144
|
+
array[ii] = 1
|
|
145
|
+
}
|
|
146
|
+
storage.push(array)
|
|
147
|
+
}
|
|
148
|
+
}, { memory: 64 })
|
|
149
|
+
|
|
150
|
+
await fn()
|
|
151
|
+
// => MemoryError: Out of memory
|
|
44
152
|
```
|
|
45
153
|
|
|
46
|
-
|
|
154
|
+
or by execution duration:
|
|
47
155
|
|
|
48
|
-
|
|
156
|
+
```js
|
|
157
|
+
const [fn, teardown] = isolatedFunction(() => {
|
|
158
|
+
const delay = ms => new Promise(resolve => setTimeout(resolve, ms))
|
|
159
|
+
await delay(duration)
|
|
160
|
+
return 'done'
|
|
161
|
+
}, { timeout: 50 })
|
|
162
|
+
|
|
163
|
+
await fn(100)
|
|
164
|
+
// => TimeoutError: Execution timed out
|
|
165
|
+
```
|
|
49
166
|
|
|
50
167
|
## API
|
|
51
168
|
|
|
52
|
-
### isolatedFunction(
|
|
169
|
+
### isolatedFunction(code, [options])
|
|
53
170
|
|
|
54
|
-
####
|
|
171
|
+
#### code
|
|
55
172
|
|
|
56
|
-
|
|
57
|
-
Type: `
|
|
173
|
+
_Required_<br>
|
|
174
|
+
Type: `function`
|
|
58
175
|
|
|
59
|
-
The
|
|
176
|
+
The hosted function to run.
|
|
60
177
|
|
|
61
178
|
#### options
|
|
62
179
|
|
|
63
|
-
#####
|
|
180
|
+
##### timeout
|
|
181
|
+
|
|
182
|
+
Type: `number`
|
|
183
|
+
|
|
184
|
+
Timeout after a specified amount of time, in milliseconds.
|
|
185
|
+
|
|
186
|
+
##### timeout
|
|
187
|
+
|
|
188
|
+
Type: `number`
|
|
189
|
+
|
|
190
|
+
Set the functino memory limit, in megabytes.
|
|
191
|
+
|
|
192
|
+
### => (fn([...args]), teardown())
|
|
193
|
+
|
|
194
|
+
#### fn
|
|
195
|
+
|
|
196
|
+
Type: `function`
|
|
197
|
+
|
|
198
|
+
The isolated function to execute. You can pass arguments over it.
|
|
199
|
+
|
|
200
|
+
#### teardown
|
|
64
201
|
|
|
65
|
-
Type: `
|
|
66
|
-
Default: `false`
|
|
202
|
+
Type: `function`
|
|
67
203
|
|
|
68
|
-
|
|
204
|
+
A function to be called to release resources associated with the **isolated-function**.
|
|
69
205
|
|
|
70
206
|
## License
|
|
71
207
|
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "isolated-function",
|
|
3
3
|
"description": "Runs untrusted code in a Node.js v8 sandbox.",
|
|
4
4
|
"homepage": "https://github.com/Kikobeats/isolated-function",
|
|
5
|
-
"version": "0.0
|
|
5
|
+
"version": "0.1.0",
|
|
6
6
|
"main": "src/index.js",
|
|
7
7
|
"exports": {
|
|
8
8
|
".": "./src/index.js"
|
|
@@ -62,18 +62,6 @@
|
|
|
62
62
|
"files": [
|
|
63
63
|
"src"
|
|
64
64
|
],
|
|
65
|
-
"scripts": {
|
|
66
|
-
"clean": "rm -rf node_modules",
|
|
67
|
-
"contributors": "(npx git-authors-cli && npx finepack && git add package.json && git commit -m 'build: contributors' --no-verify) || true",
|
|
68
|
-
"coverage": "c8 report --reporter=text-lcov > coverage/lcov.info",
|
|
69
|
-
"lint": "standard",
|
|
70
|
-
"postrelease": "npm run release:tags && npm run release:github && (ci-publish || npm publish --access=public)",
|
|
71
|
-
"pretest": "npm run lint",
|
|
72
|
-
"release": "standard-version -a",
|
|
73
|
-
"release:github": "github-generate-release",
|
|
74
|
-
"release:tags": "git push --follow-tags origin HEAD:master",
|
|
75
|
-
"test": "c8 ava"
|
|
76
|
-
},
|
|
77
65
|
"license": "MIT",
|
|
78
66
|
"commitlint": {
|
|
79
67
|
"extends": [
|
|
@@ -97,5 +85,17 @@
|
|
|
97
85
|
"simple-git-hooks": {
|
|
98
86
|
"commit-msg": "npx commitlint --edit",
|
|
99
87
|
"pre-commit": "npx nano-staged"
|
|
88
|
+
},
|
|
89
|
+
"scripts": {
|
|
90
|
+
"clean": "rm -rf node_modules",
|
|
91
|
+
"contributors": "(npx git-authors-cli && npx finepack && git add package.json && git commit -m 'build: contributors' --no-verify) || true",
|
|
92
|
+
"coverage": "c8 report --reporter=text-lcov > coverage/lcov.info",
|
|
93
|
+
"lint": "standard",
|
|
94
|
+
"postrelease": "npm run release:tags && npm run release:github && (ci-publish || npm publish --access=public)",
|
|
95
|
+
"pretest": "npm run lint",
|
|
96
|
+
"release": "standard-version -a",
|
|
97
|
+
"release:github": "github-generate-release",
|
|
98
|
+
"release:tags": "git push --follow-tags origin HEAD:master",
|
|
99
|
+
"test": "c8 ava"
|
|
100
100
|
}
|
|
101
|
-
}
|
|
101
|
+
}
|