secure-exec 0.0.1 → 0.1.0-rc.1
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 +191 -0
- package/README.md +7 -0
- package/dist/bridge/active-handles.d.ts +21 -0
- package/dist/bridge/active-handles.js +60 -0
- package/dist/bridge/child-process.d.ts +87 -0
- package/dist/bridge/child-process.js +523 -0
- package/dist/bridge/fs.d.ts +227 -0
- package/dist/bridge/fs.js +1572 -0
- package/dist/bridge/index.d.ts +10 -0
- package/dist/bridge/index.js +41 -0
- package/dist/bridge/module.d.ts +73 -0
- package/dist/bridge/module.js +329 -0
- package/dist/bridge/network.d.ts +208 -0
- package/dist/bridge/network.js +1117 -0
- package/dist/bridge/os.d.ts +13 -0
- package/dist/bridge/os.js +257 -0
- package/dist/bridge/polyfills.d.ts +2 -0
- package/dist/bridge/polyfills.js +12 -0
- package/dist/bridge/process.d.ts +64 -0
- package/dist/bridge/process.js +916 -0
- package/dist/bridge-loader.d.ts +1 -0
- package/dist/bridge-loader.js +2 -0
- package/dist/bridge-setup.d.ts +1 -0
- package/dist/bridge-setup.js +2 -0
- package/dist/bridge.js +10586 -0
- package/dist/browser/driver.d.ts +42 -0
- package/dist/browser/driver.js +263 -0
- package/dist/browser/index.d.ts +5 -0
- package/dist/browser/index.js +4 -0
- package/dist/browser/worker.d.ts +1 -0
- package/dist/browser/worker.js +3 -0
- package/dist/browser-runtime.d.ts +6 -0
- package/dist/browser-runtime.js +4 -0
- package/dist/esm-compiler.d.ts +1 -0
- package/dist/esm-compiler.js +2 -0
- package/dist/execution.d.ts +1 -0
- package/dist/execution.js +2 -0
- package/dist/fs-helpers.d.ts +2 -0
- package/dist/fs-helpers.js +1 -0
- package/dist/generated/isolate-runtime.d.ts +19 -0
- package/dist/generated/isolate-runtime.js +21 -0
- package/dist/generated/polyfills.d.ts +82 -0
- package/dist/generated/polyfills.js +82 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.js +11 -0
- package/dist/isolate-runtime/apply-custom-global-policy.js +54 -0
- package/dist/isolate-runtime/apply-timing-mitigation-freeze.js +44 -0
- package/dist/isolate-runtime/apply-timing-mitigation-off.js +14 -0
- package/dist/isolate-runtime/bridge-attach.js +29 -0
- package/dist/isolate-runtime/bridge-initial-globals.js +78 -0
- package/dist/isolate-runtime/eval-script-result.js +8 -0
- package/dist/isolate-runtime/global-exposure-helpers.js +36 -0
- package/dist/isolate-runtime/init-commonjs-module-globals.js +28 -0
- package/dist/isolate-runtime/override-process-cwd.js +8 -0
- package/dist/isolate-runtime/override-process-env.js +8 -0
- package/dist/isolate-runtime/require-setup.js +606 -0
- package/dist/isolate-runtime/set-commonjs-file-globals.js +36 -0
- package/dist/isolate-runtime/set-stdin-data.js +10 -0
- package/dist/isolate-runtime/setup-dynamic-import.js +64 -0
- package/dist/isolate-runtime/setup-fs-facade.js +40 -0
- package/dist/isolate.d.ts +1 -0
- package/dist/isolate.js +2 -0
- package/dist/module-resolver.d.ts +1 -0
- package/dist/module-resolver.js +2 -0
- package/dist/node/bridge-setup.d.ts +1 -0
- package/dist/node/bridge-setup.js +2 -0
- package/dist/node/driver.d.ts +2 -0
- package/dist/node/driver.js +2 -0
- package/dist/node/esm-compiler.d.ts +1 -0
- package/dist/node/esm-compiler.js +2 -0
- package/dist/node/execution-driver.d.ts +2 -0
- package/dist/node/execution-driver.js +2 -0
- package/dist/node/execution-lifecycle.d.ts +1 -0
- package/dist/node/execution-lifecycle.js +2 -0
- package/dist/node/isolate-bootstrap.d.ts +2 -0
- package/dist/node/isolate-bootstrap.js +1 -0
- package/dist/node/module-access.d.ts +2 -0
- package/dist/node/module-access.js +2 -0
- package/dist/node/module-resolver.d.ts +1 -0
- package/dist/node/module-resolver.js +2 -0
- package/dist/package-bundler.d.ts +2 -0
- package/dist/package-bundler.js +1 -0
- package/dist/polyfills.d.ts +1 -0
- package/dist/polyfills.js +2 -0
- package/dist/python-runtime.d.ts +2 -0
- package/dist/python-runtime.js +2 -0
- package/dist/runtime-driver.d.ts +1 -0
- package/dist/runtime-driver.js +1 -0
- package/dist/runtime.d.ts +2 -0
- package/dist/runtime.js +2 -0
- package/dist/shared/api-types.d.ts +1 -0
- package/dist/shared/api-types.js +1 -0
- package/dist/shared/bridge-contract.d.ts +2 -0
- package/dist/shared/bridge-contract.js +1 -0
- package/dist/shared/console-formatter.d.ts +2 -0
- package/dist/shared/console-formatter.js +1 -0
- package/dist/shared/errors.d.ts +2 -0
- package/dist/shared/errors.js +1 -0
- package/dist/shared/esm-utils.d.ts +1 -0
- package/dist/shared/esm-utils.js +2 -0
- package/dist/shared/global-exposure.d.ts +2 -0
- package/dist/shared/global-exposure.js +1 -0
- package/dist/shared/in-memory-fs.d.ts +1 -0
- package/dist/shared/in-memory-fs.js +2 -0
- package/dist/shared/permissions.d.ts +1 -0
- package/dist/shared/permissions.js +2 -0
- package/dist/shared/require-setup.d.ts +1 -0
- package/dist/shared/require-setup.js +2 -0
- package/dist/types.d.ts +2 -0
- package/dist/types.js +1 -0
- package/package.json +51 -4
- package/index.js +0 -1
|
@@ -0,0 +1,523 @@
|
|
|
1
|
+
// @ts-nocheck
|
|
2
|
+
// child_process module polyfill for isolated-vm
|
|
3
|
+
// Provides Node.js child_process module emulation that bridges to host
|
|
4
|
+
//
|
|
5
|
+
// Uses the active handles mechanism to keep the sandbox alive while child
|
|
6
|
+
// processes are running. See: docs-internal/node/ACTIVE_HANDLES.md
|
|
7
|
+
import { exposeCustomGlobal } from "../shared/global-exposure.js";
|
|
8
|
+
// Active children registry - maps session ID to ChildProcess
|
|
9
|
+
const activeChildren = new Map();
|
|
10
|
+
// Global dispatcher - host calls this when data arrives
|
|
11
|
+
const childProcessDispatch = (sessionId, type, data) => {
|
|
12
|
+
const child = activeChildren.get(sessionId);
|
|
13
|
+
if (!child)
|
|
14
|
+
return;
|
|
15
|
+
if (type === "stdout") {
|
|
16
|
+
const buf = typeof Buffer !== "undefined" ? Buffer.from(data) : data;
|
|
17
|
+
child.stdout.emit("data", buf);
|
|
18
|
+
}
|
|
19
|
+
else if (type === "stderr") {
|
|
20
|
+
const buf = typeof Buffer !== "undefined" ? Buffer.from(data) : data;
|
|
21
|
+
child.stderr.emit("data", buf);
|
|
22
|
+
}
|
|
23
|
+
else if (type === "exit") {
|
|
24
|
+
child.exitCode = data;
|
|
25
|
+
child.stdout.emit("end");
|
|
26
|
+
child.stderr.emit("end");
|
|
27
|
+
child.emit("close", data, null);
|
|
28
|
+
child.emit("exit", data, null);
|
|
29
|
+
activeChildren.delete(sessionId);
|
|
30
|
+
// Unregister handle - allows sandbox to exit if no other handles remain
|
|
31
|
+
// See: docs-internal/node/ACTIVE_HANDLES.md
|
|
32
|
+
if (typeof _unregisterHandle === "function") {
|
|
33
|
+
_unregisterHandle(`child:${sessionId}`);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
exposeCustomGlobal("_childProcessDispatch", childProcessDispatch);
|
|
38
|
+
// ChildProcess class - simplified interface, not strictly satisfying nodeChildProcess.ChildProcess
|
|
39
|
+
class ChildProcess {
|
|
40
|
+
_listeners = {};
|
|
41
|
+
_onceListeners = {};
|
|
42
|
+
pid = Math.floor(Math.random() * 10000) + 1000;
|
|
43
|
+
killed = false;
|
|
44
|
+
exitCode = null;
|
|
45
|
+
signalCode = null;
|
|
46
|
+
connected = false;
|
|
47
|
+
spawnfile = "";
|
|
48
|
+
spawnargs = [];
|
|
49
|
+
stdin;
|
|
50
|
+
stdout;
|
|
51
|
+
stderr;
|
|
52
|
+
stdio;
|
|
53
|
+
constructor() {
|
|
54
|
+
// Create stdin stream stub
|
|
55
|
+
this.stdin = {
|
|
56
|
+
writable: true,
|
|
57
|
+
_buffer: [],
|
|
58
|
+
write(data) {
|
|
59
|
+
this._buffer.push(data);
|
|
60
|
+
return true;
|
|
61
|
+
},
|
|
62
|
+
end() {
|
|
63
|
+
this.writable = false;
|
|
64
|
+
},
|
|
65
|
+
on() {
|
|
66
|
+
return this;
|
|
67
|
+
},
|
|
68
|
+
once() {
|
|
69
|
+
return this;
|
|
70
|
+
},
|
|
71
|
+
emit() {
|
|
72
|
+
return false;
|
|
73
|
+
},
|
|
74
|
+
};
|
|
75
|
+
// Create stdout stream stub
|
|
76
|
+
this.stdout = {
|
|
77
|
+
readable: true,
|
|
78
|
+
_data: "",
|
|
79
|
+
_listeners: {},
|
|
80
|
+
_onceListeners: {},
|
|
81
|
+
on(event, listener) {
|
|
82
|
+
if (!this._listeners[event])
|
|
83
|
+
this._listeners[event] = [];
|
|
84
|
+
this._listeners[event].push(listener);
|
|
85
|
+
return this;
|
|
86
|
+
},
|
|
87
|
+
once(event, listener) {
|
|
88
|
+
if (!this._onceListeners[event])
|
|
89
|
+
this._onceListeners[event] = [];
|
|
90
|
+
this._onceListeners[event].push(listener);
|
|
91
|
+
return this;
|
|
92
|
+
},
|
|
93
|
+
emit(event, ...args) {
|
|
94
|
+
if (this._listeners[event]) {
|
|
95
|
+
this._listeners[event].forEach((fn) => fn(...args));
|
|
96
|
+
}
|
|
97
|
+
if (this._onceListeners[event]) {
|
|
98
|
+
this._onceListeners[event].forEach((fn) => fn(...args));
|
|
99
|
+
this._onceListeners[event] = [];
|
|
100
|
+
}
|
|
101
|
+
return true;
|
|
102
|
+
},
|
|
103
|
+
read() {
|
|
104
|
+
return null;
|
|
105
|
+
},
|
|
106
|
+
setEncoding() {
|
|
107
|
+
return this;
|
|
108
|
+
},
|
|
109
|
+
pipe(dest) {
|
|
110
|
+
return dest;
|
|
111
|
+
},
|
|
112
|
+
};
|
|
113
|
+
// Create stderr stream stub
|
|
114
|
+
this.stderr = {
|
|
115
|
+
readable: true,
|
|
116
|
+
_data: "",
|
|
117
|
+
_listeners: {},
|
|
118
|
+
_onceListeners: {},
|
|
119
|
+
on(event, listener) {
|
|
120
|
+
if (!this._listeners[event])
|
|
121
|
+
this._listeners[event] = [];
|
|
122
|
+
this._listeners[event].push(listener);
|
|
123
|
+
return this;
|
|
124
|
+
},
|
|
125
|
+
once(event, listener) {
|
|
126
|
+
if (!this._onceListeners[event])
|
|
127
|
+
this._onceListeners[event] = [];
|
|
128
|
+
this._onceListeners[event].push(listener);
|
|
129
|
+
return this;
|
|
130
|
+
},
|
|
131
|
+
emit(event, ...args) {
|
|
132
|
+
if (this._listeners[event]) {
|
|
133
|
+
this._listeners[event].forEach((fn) => fn(...args));
|
|
134
|
+
}
|
|
135
|
+
if (this._onceListeners[event]) {
|
|
136
|
+
this._onceListeners[event].forEach((fn) => fn(...args));
|
|
137
|
+
this._onceListeners[event] = [];
|
|
138
|
+
}
|
|
139
|
+
return true;
|
|
140
|
+
},
|
|
141
|
+
read() {
|
|
142
|
+
return null;
|
|
143
|
+
},
|
|
144
|
+
setEncoding() {
|
|
145
|
+
return this;
|
|
146
|
+
},
|
|
147
|
+
pipe(dest) {
|
|
148
|
+
return dest;
|
|
149
|
+
},
|
|
150
|
+
};
|
|
151
|
+
this.stdio = [this.stdin, this.stdout, this.stderr];
|
|
152
|
+
}
|
|
153
|
+
on(event, listener) {
|
|
154
|
+
if (!this._listeners[event])
|
|
155
|
+
this._listeners[event] = [];
|
|
156
|
+
this._listeners[event].push(listener);
|
|
157
|
+
return this;
|
|
158
|
+
}
|
|
159
|
+
once(event, listener) {
|
|
160
|
+
if (!this._onceListeners[event])
|
|
161
|
+
this._onceListeners[event] = [];
|
|
162
|
+
this._onceListeners[event].push(listener);
|
|
163
|
+
return this;
|
|
164
|
+
}
|
|
165
|
+
off(event, listener) {
|
|
166
|
+
if (this._listeners[event]) {
|
|
167
|
+
const idx = this._listeners[event].indexOf(listener);
|
|
168
|
+
if (idx !== -1)
|
|
169
|
+
this._listeners[event].splice(idx, 1);
|
|
170
|
+
}
|
|
171
|
+
return this;
|
|
172
|
+
}
|
|
173
|
+
removeListener(event, listener) {
|
|
174
|
+
return this.off(event, listener);
|
|
175
|
+
}
|
|
176
|
+
emit(event, ...args) {
|
|
177
|
+
let handled = false;
|
|
178
|
+
if (this._listeners[event]) {
|
|
179
|
+
this._listeners[event].forEach((fn) => {
|
|
180
|
+
fn(...args);
|
|
181
|
+
handled = true;
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
if (this._onceListeners[event]) {
|
|
185
|
+
this._onceListeners[event].forEach((fn) => {
|
|
186
|
+
fn(...args);
|
|
187
|
+
handled = true;
|
|
188
|
+
});
|
|
189
|
+
this._onceListeners[event] = [];
|
|
190
|
+
}
|
|
191
|
+
return handled;
|
|
192
|
+
}
|
|
193
|
+
kill(_signal) {
|
|
194
|
+
this.killed = true;
|
|
195
|
+
this.signalCode = (typeof _signal === "string" ? _signal : "SIGTERM");
|
|
196
|
+
return true;
|
|
197
|
+
}
|
|
198
|
+
ref() {
|
|
199
|
+
return this;
|
|
200
|
+
}
|
|
201
|
+
unref() {
|
|
202
|
+
return this;
|
|
203
|
+
}
|
|
204
|
+
disconnect() {
|
|
205
|
+
this.connected = false;
|
|
206
|
+
}
|
|
207
|
+
_complete(stdout, stderr, code) {
|
|
208
|
+
this.exitCode = code;
|
|
209
|
+
// Emit data events for stdout/stderr as single chunks
|
|
210
|
+
if (stdout) {
|
|
211
|
+
const buf = typeof Buffer !== "undefined" ? Buffer.from(stdout) : stdout;
|
|
212
|
+
this.stdout.emit("data", buf);
|
|
213
|
+
}
|
|
214
|
+
if (stderr) {
|
|
215
|
+
const buf = typeof Buffer !== "undefined" ? Buffer.from(stderr) : stderr;
|
|
216
|
+
this.stderr.emit("data", buf);
|
|
217
|
+
}
|
|
218
|
+
// Emit end events
|
|
219
|
+
this.stdout.emit("end");
|
|
220
|
+
this.stderr.emit("end");
|
|
221
|
+
// Emit close event (code, signal)
|
|
222
|
+
this.emit("close", code, this.signalCode);
|
|
223
|
+
// Emit exit event
|
|
224
|
+
this.emit("exit", code, this.signalCode);
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
// exec - execute shell command, callback when done
|
|
228
|
+
// Uses spawn("bash", ["-c", command]) internally
|
|
229
|
+
// NOTE: WASIX bash returns incorrect exit codes (45 instead of 0) for -c flag,
|
|
230
|
+
// so error will be set even on successful commands. The stdout/stderr are correct.
|
|
231
|
+
function exec(command, options, callback) {
|
|
232
|
+
if (typeof options === "function") {
|
|
233
|
+
callback = options;
|
|
234
|
+
options = {};
|
|
235
|
+
}
|
|
236
|
+
// Use spawn with shell to execute the command
|
|
237
|
+
const child = spawn("bash", ["-c", command], { shell: false });
|
|
238
|
+
child.spawnargs = ["bash", "-c", command];
|
|
239
|
+
child.spawnfile = "bash";
|
|
240
|
+
// Collect output and invoke callback
|
|
241
|
+
let stdout = "";
|
|
242
|
+
let stderr = "";
|
|
243
|
+
child.stdout.on("data", (data) => {
|
|
244
|
+
stdout += String(data);
|
|
245
|
+
});
|
|
246
|
+
child.stderr.on("data", (data) => {
|
|
247
|
+
stderr += String(data);
|
|
248
|
+
});
|
|
249
|
+
child.on("close", (code) => {
|
|
250
|
+
if (callback) {
|
|
251
|
+
if (code !== 0) {
|
|
252
|
+
const err = new Error("Command failed: " + command);
|
|
253
|
+
err.code = code;
|
|
254
|
+
err.killed = false;
|
|
255
|
+
err.signal = null;
|
|
256
|
+
err.cmd = command;
|
|
257
|
+
err.stdout = stdout;
|
|
258
|
+
err.stderr = stderr;
|
|
259
|
+
callback(err, stdout, stderr);
|
|
260
|
+
}
|
|
261
|
+
else {
|
|
262
|
+
callback(null, stdout, stderr);
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
});
|
|
266
|
+
child.on("error", (err) => {
|
|
267
|
+
if (callback) {
|
|
268
|
+
const error = err instanceof Error ? err : new Error(String(err));
|
|
269
|
+
error.code = 1;
|
|
270
|
+
error.stdout = stdout;
|
|
271
|
+
error.stderr = stderr;
|
|
272
|
+
callback(error, stdout, stderr);
|
|
273
|
+
}
|
|
274
|
+
});
|
|
275
|
+
return child;
|
|
276
|
+
}
|
|
277
|
+
// execSync - synchronous shell execution
|
|
278
|
+
// Uses spawnSync("bash", ["-c", command]) internally
|
|
279
|
+
function execSync(command, options) {
|
|
280
|
+
const opts = options || {};
|
|
281
|
+
if (typeof _childProcessSpawnSync === "undefined") {
|
|
282
|
+
throw new Error("child_process.execSync requires CommandExecutor to be configured");
|
|
283
|
+
}
|
|
284
|
+
// Use synchronous bridge call - result is JSON string
|
|
285
|
+
const jsonResult = _childProcessSpawnSync.applySyncPromise(undefined, [
|
|
286
|
+
"bash",
|
|
287
|
+
JSON.stringify(["-c", command]),
|
|
288
|
+
JSON.stringify({ cwd: opts.cwd, env: opts.env }),
|
|
289
|
+
]);
|
|
290
|
+
const result = JSON.parse(jsonResult);
|
|
291
|
+
if (result.code !== 0) {
|
|
292
|
+
const err = new Error("Command failed: " + command);
|
|
293
|
+
err.status = result.code;
|
|
294
|
+
err.stdout = result.stdout;
|
|
295
|
+
err.stderr = result.stderr;
|
|
296
|
+
err.output = [null, result.stdout, result.stderr];
|
|
297
|
+
throw err;
|
|
298
|
+
}
|
|
299
|
+
if (opts.encoding === "buffer" || !opts.encoding) {
|
|
300
|
+
return typeof Buffer !== "undefined" ? Buffer.from(result.stdout) : result.stdout;
|
|
301
|
+
}
|
|
302
|
+
return result.stdout;
|
|
303
|
+
}
|
|
304
|
+
// spawn - spawn a command with streaming
|
|
305
|
+
function spawn(command, args, options) {
|
|
306
|
+
let argsArray = [];
|
|
307
|
+
let opts = {};
|
|
308
|
+
if (!Array.isArray(args)) {
|
|
309
|
+
opts = args || {};
|
|
310
|
+
}
|
|
311
|
+
else {
|
|
312
|
+
argsArray = args;
|
|
313
|
+
opts = options || {};
|
|
314
|
+
}
|
|
315
|
+
const child = new ChildProcess();
|
|
316
|
+
child.spawnfile = command;
|
|
317
|
+
child.spawnargs = [command, ...argsArray];
|
|
318
|
+
// Check if streaming mode is available
|
|
319
|
+
if (typeof _childProcessSpawnStart !== "undefined") {
|
|
320
|
+
// Use process.cwd() as default if no cwd specified
|
|
321
|
+
// This ensures process.chdir() changes are reflected in child processes
|
|
322
|
+
const effectiveCwd = opts.cwd ?? (typeof process !== "undefined" ? process.cwd() : "/");
|
|
323
|
+
// Streaming mode - spawn immediately
|
|
324
|
+
const sessionId = _childProcessSpawnStart.applySync(undefined, [
|
|
325
|
+
command,
|
|
326
|
+
JSON.stringify(argsArray),
|
|
327
|
+
JSON.stringify({ cwd: effectiveCwd, env: opts.env }),
|
|
328
|
+
]);
|
|
329
|
+
activeChildren.set(sessionId, child);
|
|
330
|
+
// Register handle to keep sandbox alive until child exits
|
|
331
|
+
// See: docs-internal/node/ACTIVE_HANDLES.md
|
|
332
|
+
if (typeof _registerHandle === "function") {
|
|
333
|
+
_registerHandle(`child:${sessionId}`, `child_process: ${command} ${argsArray.join(" ")}`);
|
|
334
|
+
}
|
|
335
|
+
// Override stdin methods for streaming
|
|
336
|
+
child.stdin.write = (data) => {
|
|
337
|
+
if (typeof _childProcessStdinWrite === "undefined")
|
|
338
|
+
return false;
|
|
339
|
+
const bytes = typeof data === "string" ? new TextEncoder().encode(data) : data;
|
|
340
|
+
_childProcessStdinWrite.applySync(undefined, [sessionId, bytes]);
|
|
341
|
+
return true;
|
|
342
|
+
};
|
|
343
|
+
child.stdin.end = () => {
|
|
344
|
+
if (typeof _childProcessStdinClose !== "undefined") {
|
|
345
|
+
_childProcessStdinClose.applySync(undefined, [sessionId]);
|
|
346
|
+
}
|
|
347
|
+
child.stdin.writable = false;
|
|
348
|
+
};
|
|
349
|
+
// Override kill method
|
|
350
|
+
child.kill = (signal) => {
|
|
351
|
+
if (typeof _childProcessKill === "undefined")
|
|
352
|
+
return false;
|
|
353
|
+
const sig = signal === "SIGKILL" || signal === 9
|
|
354
|
+
? 9
|
|
355
|
+
: signal === "SIGINT" || signal === 2
|
|
356
|
+
? 2
|
|
357
|
+
: 15;
|
|
358
|
+
_childProcessKill.applySync(undefined, [sessionId, sig]);
|
|
359
|
+
child.killed = true;
|
|
360
|
+
child.signalCode = (typeof signal === "string" ? signal : "SIGTERM");
|
|
361
|
+
return true;
|
|
362
|
+
};
|
|
363
|
+
return child;
|
|
364
|
+
}
|
|
365
|
+
// Fallback: no CommandExecutor available
|
|
366
|
+
const err = new Error("child_process.spawn requires CommandExecutor to be configured");
|
|
367
|
+
// Emit error asynchronously to match Node.js behavior
|
|
368
|
+
setTimeout(() => {
|
|
369
|
+
child.emit("error", err);
|
|
370
|
+
child._complete("", err.message, 1);
|
|
371
|
+
}, 0);
|
|
372
|
+
return child;
|
|
373
|
+
}
|
|
374
|
+
// spawnSync - synchronous spawn
|
|
375
|
+
function spawnSync(command, args, options) {
|
|
376
|
+
let argsArray = [];
|
|
377
|
+
let opts = {};
|
|
378
|
+
if (!Array.isArray(args)) {
|
|
379
|
+
opts = args || {};
|
|
380
|
+
}
|
|
381
|
+
else {
|
|
382
|
+
argsArray = args;
|
|
383
|
+
opts = options || {};
|
|
384
|
+
}
|
|
385
|
+
if (typeof _childProcessSpawnSync === "undefined") {
|
|
386
|
+
return {
|
|
387
|
+
pid: 0,
|
|
388
|
+
output: [null, "", "child_process.spawnSync requires CommandExecutor to be configured"],
|
|
389
|
+
stdout: "",
|
|
390
|
+
stderr: "child_process.spawnSync requires CommandExecutor to be configured",
|
|
391
|
+
status: 1,
|
|
392
|
+
signal: null,
|
|
393
|
+
error: new Error("child_process.spawnSync requires CommandExecutor to be configured"),
|
|
394
|
+
};
|
|
395
|
+
}
|
|
396
|
+
try {
|
|
397
|
+
// Use process.cwd() as default if no cwd specified
|
|
398
|
+
// This ensures process.chdir() changes are reflected in child processes
|
|
399
|
+
const effectiveCwd = opts.cwd ?? (typeof process !== "undefined" ? process.cwd() : "/");
|
|
400
|
+
// Args passed as JSON string for transferability
|
|
401
|
+
const jsonResult = _childProcessSpawnSync.applySyncPromise(undefined, [
|
|
402
|
+
command,
|
|
403
|
+
JSON.stringify(argsArray),
|
|
404
|
+
JSON.stringify({ cwd: effectiveCwd, env: opts.env }),
|
|
405
|
+
]);
|
|
406
|
+
const result = JSON.parse(jsonResult);
|
|
407
|
+
const stdoutBuf = typeof Buffer !== "undefined" ? Buffer.from(result.stdout) : result.stdout;
|
|
408
|
+
const stderrBuf = typeof Buffer !== "undefined" ? Buffer.from(result.stderr) : result.stderr;
|
|
409
|
+
return {
|
|
410
|
+
pid: Math.floor(Math.random() * 10000) + 1000,
|
|
411
|
+
output: [null, stdoutBuf, stderrBuf],
|
|
412
|
+
stdout: stdoutBuf,
|
|
413
|
+
stderr: stderrBuf,
|
|
414
|
+
status: result.code,
|
|
415
|
+
signal: null,
|
|
416
|
+
error: undefined,
|
|
417
|
+
};
|
|
418
|
+
}
|
|
419
|
+
catch (err) {
|
|
420
|
+
const errMsg = err instanceof Error ? err.message : String(err);
|
|
421
|
+
const stderrBuf = typeof Buffer !== "undefined" ? Buffer.from(errMsg) : errMsg;
|
|
422
|
+
return {
|
|
423
|
+
pid: 0,
|
|
424
|
+
output: [null, "", stderrBuf],
|
|
425
|
+
stdout: typeof Buffer !== "undefined" ? Buffer.from("") : "",
|
|
426
|
+
stderr: stderrBuf,
|
|
427
|
+
status: 1,
|
|
428
|
+
signal: null,
|
|
429
|
+
error: err instanceof Error ? err : new Error(String(err)),
|
|
430
|
+
};
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
// execFile - execute a file directly
|
|
434
|
+
function execFile(file, args, options, callback) {
|
|
435
|
+
let argsArray = [];
|
|
436
|
+
let opts = {};
|
|
437
|
+
let cb;
|
|
438
|
+
if (typeof args === "function") {
|
|
439
|
+
cb = args;
|
|
440
|
+
}
|
|
441
|
+
else if (typeof options === "function") {
|
|
442
|
+
argsArray = args.slice();
|
|
443
|
+
cb = options;
|
|
444
|
+
}
|
|
445
|
+
else {
|
|
446
|
+
argsArray = Array.isArray(args) ? args : [];
|
|
447
|
+
opts = options || {};
|
|
448
|
+
cb = callback;
|
|
449
|
+
}
|
|
450
|
+
// execFile is like spawn but with callback
|
|
451
|
+
const child = spawn(file, argsArray, opts);
|
|
452
|
+
let stdout = "";
|
|
453
|
+
let stderr = "";
|
|
454
|
+
child.stdout.on("data", (data) => {
|
|
455
|
+
stdout += String(data);
|
|
456
|
+
});
|
|
457
|
+
child.stderr.on("data", (data) => {
|
|
458
|
+
stderr += String(data);
|
|
459
|
+
});
|
|
460
|
+
child.on("close", (code) => {
|
|
461
|
+
if (cb) {
|
|
462
|
+
if (code !== 0) {
|
|
463
|
+
const err = new Error("Command failed: " + file);
|
|
464
|
+
err.code = code;
|
|
465
|
+
err.stdout = stdout;
|
|
466
|
+
err.stderr = stderr;
|
|
467
|
+
cb(err, stdout, stderr);
|
|
468
|
+
}
|
|
469
|
+
else {
|
|
470
|
+
cb(null, stdout, stderr);
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
});
|
|
474
|
+
child.on("error", (err) => {
|
|
475
|
+
if (cb) {
|
|
476
|
+
cb(err, stdout, stderr);
|
|
477
|
+
}
|
|
478
|
+
});
|
|
479
|
+
return child;
|
|
480
|
+
}
|
|
481
|
+
// execFileSync
|
|
482
|
+
function execFileSync(file, args, options) {
|
|
483
|
+
let argsArray = [];
|
|
484
|
+
let opts = {};
|
|
485
|
+
if (!Array.isArray(args)) {
|
|
486
|
+
opts = args || {};
|
|
487
|
+
}
|
|
488
|
+
else {
|
|
489
|
+
argsArray = args;
|
|
490
|
+
opts = options || {};
|
|
491
|
+
}
|
|
492
|
+
const result = spawnSync(file, argsArray, opts);
|
|
493
|
+
if (result.status !== 0) {
|
|
494
|
+
const err = new Error("Command failed: " + file);
|
|
495
|
+
err.status = result.status ?? undefined;
|
|
496
|
+
err.stdout = String(result.stdout);
|
|
497
|
+
err.stderr = String(result.stderr);
|
|
498
|
+
throw err;
|
|
499
|
+
}
|
|
500
|
+
if (opts.encoding === "buffer" || !opts.encoding) {
|
|
501
|
+
return result.stdout;
|
|
502
|
+
}
|
|
503
|
+
return typeof result.stdout === "string" ? result.stdout : result.stdout.toString(opts.encoding);
|
|
504
|
+
}
|
|
505
|
+
// fork - intentionally not implemented (IPC between processes not supported in sandbox)
|
|
506
|
+
function fork(_modulePath, _args, _options) {
|
|
507
|
+
throw new Error("child_process.fork is not supported in sandbox");
|
|
508
|
+
}
|
|
509
|
+
// Create the child_process module
|
|
510
|
+
const childProcess = {
|
|
511
|
+
ChildProcess,
|
|
512
|
+
exec,
|
|
513
|
+
execSync,
|
|
514
|
+
spawn,
|
|
515
|
+
spawnSync,
|
|
516
|
+
execFile,
|
|
517
|
+
execFileSync,
|
|
518
|
+
fork,
|
|
519
|
+
};
|
|
520
|
+
// Expose to global for require() to use
|
|
521
|
+
exposeCustomGlobal("_childProcessModule", childProcess);
|
|
522
|
+
export { ChildProcess, exec, execSync, spawn, spawnSync, execFile, execFileSync, fork };
|
|
523
|
+
export default childProcess;
|