isolated-function 0.1.0 → 0.1.2

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 CHANGED
File without changes
package/README.md CHANGED
@@ -41,7 +41,7 @@ npm install isolated-function --save
41
41
 
42
42
  ## Quickstart
43
43
 
44
- **isolated-functions** is a modern solution for running arbitrary code using Node.js.
44
+ **isolated-function** is a modern solution for running untrusted code in Node.js.
45
45
 
46
46
  ```js
47
47
  const isolatedFunction = require('isolated-function')
@@ -60,11 +60,9 @@ console.log({ value, profiling })
60
60
  await teardown()
61
61
  ```
62
62
 
63
- The hosted code runs in a separate process with limited permissions, returning the result and profiling the execution.
64
-
65
63
  ### Minimal privilege execution
66
64
 
67
- The hosted code will be executed with minimal privilege. If you exceed your limit, an error will occur.
65
+ The hosted code runs in a separate process, with minimal privilege, using [Node.js permission model API](https://nodejs.org/api/permissions.html#permission-model).
68
66
 
69
67
  ```js
70
68
  const [fn, teardown] = isolatedFunction(() => {
@@ -76,7 +74,7 @@ await fn()
76
74
  // => PermissionError: Access to 'FileSystemWrite' has been restricted.
77
75
  ```
78
76
 
79
- Any of the following interaction will throw an error:
77
+ If you exceed your limit, an error will occur. Any of the following interaction will throw an error:
80
78
 
81
79
  - Native modules
82
80
  - Child process
@@ -92,7 +90,7 @@ The hosted code is parsed for detecting `require`/`import` calls and install the
92
90
  ```js
93
91
  const [isEmoji, teardown] = isolatedFunction(emoji => {
94
92
  /* this dependency only exists inside the isolated function */
95
- const isEmoji = require('is-standard-emoji')
93
+ const isEmoji = require('is-standard-emoji@1.0.0') // default is latest
96
94
  return isEmoji(emoji)
97
95
  })
98
96
 
@@ -101,11 +99,11 @@ await isEmoji('foo') // => false
101
99
  await teardown()
102
100
  ```
103
101
 
104
- The dependencies, along with the hosted code, are bundled into a single file that will be evaluated at runtime.
102
+ The dependencies, along with the hosted code, are bundled by [esbuild](https://esbuild.github.io/) into a single file that will be evaluated at runtime.
105
103
 
106
104
  ### Execution profiling
107
105
 
108
- Any hosted code execution has a profiling associated:
106
+ Any hosted code execution will be run in their own separate process:
109
107
 
110
108
  ```js
111
109
  /** make a function to consume ~128MB */
@@ -130,6 +128,8 @@ console.log(profiling)
130
128
  // }
131
129
  ```
132
130
 
131
+ Each execution has a profiling, which helps understand what happened.
132
+
133
133
  ### Resource limits
134
134
 
135
135
  You can limit a **isolated-function** by memory:
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.1.0",
5
+ "version": "0.1.2",
6
6
  "main": "src/index.js",
7
7
  "exports": {
8
8
  ".": "./src/index.js"
@@ -62,6 +62,18 @@
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
+ },
65
77
  "license": "MIT",
66
78
  "commitlint": {
67
79
  "extends": [
@@ -85,17 +97,5 @@
85
97
  "simple-git-hooks": {
86
98
  "commit-msg": "npx commitlint --edit",
87
99
  "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
+ }
package/src/compile.js CHANGED
@@ -37,17 +37,74 @@ const detectDependencies = code => {
37
37
  node.arguments.length === 1 &&
38
38
  node.arguments[0].type === 'Literal'
39
39
  ) {
40
- dependencies.add(node.arguments[0].value)
40
+ const dependency = node.arguments[0].value
41
+ // Check if the dependency string contains '@' symbol for versioning
42
+ if (dependency.includes('@')) {
43
+ // Split by '@' to separate module name and version
44
+ const parts = dependency.split('@')
45
+ // Handle edge case where module name might also contain '@' (scoped packages)
46
+ const moduleName = parts.length > 2 ? `${parts[0]}@${parts[1]}` : parts[0]
47
+ const version = parts[parts.length - 1]
48
+ dependencies.add(`${moduleName}@${version}`)
49
+ } else {
50
+ dependencies.add(`${dependency}@latest`)
51
+ }
41
52
  }
42
53
  },
43
54
  ImportDeclaration (node) {
44
- dependencies.add(node.source.value)
55
+ const source = node.source.value
56
+ if (source.includes('@')) {
57
+ const parts = source.split('@')
58
+ const moduleName = parts.length > 2 ? `${parts[0]}@${parts[1]}` : parts[0]
59
+ const version = parts[parts.length - 1]
60
+ dependencies.add(`${moduleName}@${version}`)
61
+ } else {
62
+ dependencies.add(`${source}@latest`)
63
+ }
45
64
  }
46
65
  })
47
66
 
48
67
  return Array.from(dependencies)
49
68
  }
50
69
 
70
+ const transformDependencies = code => {
71
+ const ast = acorn.parse(code, { ecmaVersion: 2020, sourceType: 'module' })
72
+
73
+ let newCode = ''
74
+ let lastIndex = 0
75
+
76
+ // Helper function to process and transform nodes
77
+ const processNode = node => {
78
+ if (node.type === 'Literal' && node.value.includes('@')) {
79
+ // Extract module name without version
80
+ const [moduleName] = node.value.split('@')
81
+ // Append code before this node
82
+ newCode += code.substring(lastIndex, node.start)
83
+ // Append transformed dependency
84
+ newCode += `'${moduleName}'`
85
+ // Update lastIndex to end of current node
86
+ lastIndex = node.end
87
+ }
88
+ }
89
+
90
+ // Traverse the AST to find require and import declarations
91
+ walk.simple(ast, {
92
+ CallExpression (node) {
93
+ if (node.callee.name === 'require' && node.arguments.length === 1) {
94
+ processNode(node.arguments[0])
95
+ }
96
+ },
97
+ ImportDeclaration (node) {
98
+ processNode(node.source)
99
+ }
100
+ })
101
+
102
+ // Append remaining code after last modified dependency
103
+ newCode += code.substring(lastIndex)
104
+
105
+ return newCode
106
+ }
107
+
51
108
  const getTmp = async content => {
52
109
  const cwd = await fs.mkdtemp(path.join(tmpdir(), 'compile-'))
53
110
  await fs.mkdir(cwd, { recursive: true })
@@ -60,8 +117,10 @@ const getTmp = async content => {
60
117
  }
61
118
 
62
119
  module.exports = async snippet => {
63
- const tmp = await getTmp(generateTemplate(snippet))
64
- const dependencies = detectDependencies(tmp.content)
120
+ const compiledTemplate = generateTemplate(snippet)
121
+ const dependencies = detectDependencies(compiledTemplate)
122
+ const tmp = await getTmp(transformDependencies(compiledTemplate))
123
+
65
124
  await $(packageManager.init, { cwd: tmp.cwd })
66
125
  await $(`${packageManager.install} ${dependencies.join(' ')}`, {
67
126
  cwd: tmp.cwd