@secure-exec/core 0.1.1-rc.2 → 0.2.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.
Files changed (102) hide show
  1. package/dist/esm-compiler.d.ts +5 -1
  2. package/dist/esm-compiler.js +5 -1
  3. package/dist/fs-helpers.d.ts +1 -1
  4. package/dist/generated/isolate-runtime.d.ts +15 -15
  5. package/dist/generated/isolate-runtime.js +15 -15
  6. package/dist/index.d.ts +25 -6
  7. package/dist/index.js +23 -3
  8. package/dist/isolate-runtime/apply-custom-global-policy.js +3 -3
  9. package/dist/isolate-runtime/apply-timing-mitigation-freeze.js +10 -8
  10. package/dist/isolate-runtime/apply-timing-mitigation-off.js +2 -2
  11. package/dist/isolate-runtime/bridge-attach.js +2 -2
  12. package/dist/isolate-runtime/bridge-initial-globals.js +3 -3
  13. package/dist/isolate-runtime/eval-script-result.js +1 -1
  14. package/dist/isolate-runtime/global-exposure-helpers.js +2 -2
  15. package/dist/isolate-runtime/init-commonjs-module-globals.js +2 -2
  16. package/dist/isolate-runtime/override-process-cwd.js +1 -1
  17. package/dist/isolate-runtime/override-process-env.js +1 -1
  18. package/dist/isolate-runtime/require-setup.js +2236 -19
  19. package/dist/isolate-runtime/set-commonjs-file-globals.js +2 -2
  20. package/dist/isolate-runtime/set-stdin-data.js +1 -1
  21. package/dist/isolate-runtime/setup-dynamic-import.js +47 -15
  22. package/dist/isolate-runtime/setup-fs-facade.js +2 -2
  23. package/dist/kernel/command-registry.d.ts +44 -0
  24. package/dist/kernel/command-registry.js +114 -0
  25. package/dist/kernel/device-layer.d.ts +12 -0
  26. package/dist/kernel/device-layer.js +262 -0
  27. package/dist/kernel/dns-cache.d.ts +29 -0
  28. package/dist/kernel/dns-cache.js +52 -0
  29. package/dist/kernel/fd-table.d.ts +84 -0
  30. package/dist/kernel/fd-table.js +278 -0
  31. package/dist/kernel/file-lock.d.ts +34 -0
  32. package/dist/kernel/file-lock.js +123 -0
  33. package/dist/kernel/host-adapter.d.ts +50 -0
  34. package/dist/kernel/host-adapter.js +8 -0
  35. package/dist/kernel/index.d.ts +36 -0
  36. package/dist/kernel/index.js +34 -0
  37. package/dist/kernel/inode-table.d.ts +43 -0
  38. package/dist/kernel/inode-table.js +85 -0
  39. package/dist/kernel/kernel.d.ts +9 -0
  40. package/dist/kernel/kernel.js +1396 -0
  41. package/dist/kernel/permissions.d.ts +27 -0
  42. package/dist/kernel/permissions.js +118 -0
  43. package/dist/kernel/pipe-manager.d.ts +64 -0
  44. package/dist/kernel/pipe-manager.js +267 -0
  45. package/dist/kernel/proc-layer.d.ts +11 -0
  46. package/dist/kernel/proc-layer.js +501 -0
  47. package/dist/kernel/process-table.d.ts +124 -0
  48. package/dist/kernel/process-table.js +631 -0
  49. package/dist/kernel/pty.d.ts +108 -0
  50. package/dist/kernel/pty.js +541 -0
  51. package/dist/kernel/socket-table.d.ts +305 -0
  52. package/dist/kernel/socket-table.js +1124 -0
  53. package/dist/kernel/timer-table.d.ts +54 -0
  54. package/dist/kernel/timer-table.js +108 -0
  55. package/dist/kernel/types.d.ts +500 -0
  56. package/dist/kernel/types.js +89 -0
  57. package/dist/kernel/user.d.ts +29 -0
  58. package/dist/kernel/user.js +35 -0
  59. package/dist/kernel/vfs.d.ts +54 -0
  60. package/dist/kernel/vfs.js +8 -0
  61. package/dist/kernel/wait.d.ts +45 -0
  62. package/dist/kernel/wait.js +112 -0
  63. package/dist/kernel/wstatus.d.ts +21 -0
  64. package/dist/kernel/wstatus.js +33 -0
  65. package/dist/module-resolver.d.ts +4 -0
  66. package/dist/module-resolver.js +4 -0
  67. package/dist/package-bundler.d.ts +6 -1
  68. package/dist/runtime-driver.d.ts +3 -1
  69. package/dist/shared/bridge-contract.d.ts +529 -94
  70. package/dist/shared/bridge-contract.js +86 -3
  71. package/dist/shared/console-formatter.js +4 -0
  72. package/dist/shared/global-exposure.js +345 -0
  73. package/dist/shared/in-memory-fs.d.ts +30 -11
  74. package/dist/shared/in-memory-fs.js +383 -109
  75. package/dist/shared/permissions.d.ts +4 -6
  76. package/dist/shared/permissions.js +24 -28
  77. package/dist/types.d.ts +20 -130
  78. package/dist/types.js +5 -0
  79. package/package.json +12 -22
  80. package/dist/bridge/active-handles.d.ts +0 -22
  81. package/dist/bridge/active-handles.js +0 -55
  82. package/dist/bridge/child-process.d.ts +0 -99
  83. package/dist/bridge/child-process.js +0 -656
  84. package/dist/bridge/fs.d.ts +0 -281
  85. package/dist/bridge/fs.js +0 -2231
  86. package/dist/bridge/index.d.ts +0 -10
  87. package/dist/bridge/index.js +0 -41
  88. package/dist/bridge/module.d.ts +0 -75
  89. package/dist/bridge/module.js +0 -299
  90. package/dist/bridge/network.d.ts +0 -250
  91. package/dist/bridge/network.js +0 -1433
  92. package/dist/bridge/os.d.ts +0 -13
  93. package/dist/bridge/os.js +0 -256
  94. package/dist/bridge/polyfills.d.ts +0 -2
  95. package/dist/bridge/polyfills.js +0 -11
  96. package/dist/bridge/process.d.ts +0 -89
  97. package/dist/bridge/process.js +0 -994
  98. package/dist/bridge.js +0 -11766
  99. package/dist/python-runtime.d.ts +0 -16
  100. package/dist/python-runtime.js +0 -45
  101. package/dist/runtime.d.ts +0 -31
  102. package/dist/runtime.js +0 -69
@@ -1,656 +0,0 @@
1
- // child_process module polyfill for isolated-vm
2
- // Provides Node.js child_process module emulation that bridges to host
3
- //
4
- // Uses the active handles mechanism to keep the sandbox alive while child
5
- // processes are running. See: docs-internal/node/ACTIVE_HANDLES.md
6
- import { exposeCustomGlobal } from "../shared/global-exposure.js";
7
- // Active children registry - maps session ID to ChildProcess
8
- const activeChildren = new Map();
9
- /**
10
- * Global dispatcher invoked by the host when child process data arrives.
11
- * Routes stdout/stderr chunks and exit codes to the corresponding ChildProcess
12
- * instance by session ID, and unregisters the active handle on exit.
13
- */
14
- const childProcessDispatch = (sessionId, type, data) => {
15
- const child = activeChildren.get(sessionId);
16
- if (!child)
17
- return;
18
- if (type === "stdout") {
19
- const buf = typeof Buffer !== "undefined" ? Buffer.from(data) : data;
20
- child.stdout.emit("data", buf);
21
- }
22
- else if (type === "stderr") {
23
- const buf = typeof Buffer !== "undefined" ? Buffer.from(data) : data;
24
- child.stderr.emit("data", buf);
25
- }
26
- else if (type === "exit") {
27
- child.exitCode = data;
28
- child.stdout.emit("end");
29
- child.stderr.emit("end");
30
- child.emit("close", data, null);
31
- child.emit("exit", data, null);
32
- activeChildren.delete(sessionId);
33
- // Unregister handle - allows sandbox to exit if no other handles remain
34
- // See: docs-internal/node/ACTIVE_HANDLES.md
35
- if (typeof _unregisterHandle === "function") {
36
- _unregisterHandle(`child:${sessionId}`);
37
- }
38
- }
39
- };
40
- exposeCustomGlobal("_childProcessDispatch", childProcessDispatch);
41
- /** Warn when listener count exceeds max (Node.js: warn, don't crash) */
42
- function checkStreamMaxListeners(stream, event) {
43
- if (stream._maxListeners > 0 && !stream._maxListenersWarned.has(event)) {
44
- const total = (stream._listeners[event]?.length ?? 0) + (stream._onceListeners[event]?.length ?? 0);
45
- if (total > stream._maxListeners) {
46
- stream._maxListenersWarned.add(event);
47
- const warning = `MaxListenersExceededWarning: Possible EventEmitter memory leak detected. ${total} ${event} listeners added. MaxListeners is ${stream._maxListeners}. Use emitter.setMaxListeners() to increase limit`;
48
- if (typeof console !== "undefined" && console.error) {
49
- console.error(warning);
50
- }
51
- }
52
- }
53
- }
54
- // Monotonic counter for unique ChildProcess PIDs
55
- let _nextChildPid = 1000;
56
- /**
57
- * Polyfill of Node.js `ChildProcess`. Provides event-emitting stdin/stdout/stderr
58
- * streams. In streaming mode, data arrives via the `_childProcessDispatch` global
59
- * that the host calls with stdout/stderr/exit events keyed by session ID.
60
- */
61
- class ChildProcess {
62
- _listeners = {};
63
- _onceListeners = {};
64
- _maxListeners = 10;
65
- _maxListenersWarned = new Set();
66
- pid = _nextChildPid++;
67
- killed = false;
68
- exitCode = null;
69
- signalCode = null;
70
- connected = false;
71
- spawnfile = "";
72
- spawnargs = [];
73
- stdin;
74
- stdout;
75
- stderr;
76
- stdio;
77
- constructor() {
78
- // Create stdin stream stub
79
- this.stdin = {
80
- writable: true,
81
- write(_data) {
82
- return true;
83
- },
84
- end() {
85
- this.writable = false;
86
- },
87
- on() {
88
- return this;
89
- },
90
- once() {
91
- return this;
92
- },
93
- emit() {
94
- return false;
95
- },
96
- };
97
- // Create stdout stream stub
98
- this.stdout = {
99
- readable: true,
100
- _listeners: {},
101
- _onceListeners: {},
102
- _maxListeners: 10,
103
- _maxListenersWarned: new Set(),
104
- on(event, listener) {
105
- if (!this._listeners[event])
106
- this._listeners[event] = [];
107
- this._listeners[event].push(listener);
108
- checkStreamMaxListeners(this, event);
109
- return this;
110
- },
111
- once(event, listener) {
112
- if (!this._onceListeners[event])
113
- this._onceListeners[event] = [];
114
- this._onceListeners[event].push(listener);
115
- checkStreamMaxListeners(this, event);
116
- return this;
117
- },
118
- emit(event, ...args) {
119
- if (this._listeners[event]) {
120
- this._listeners[event].forEach((fn) => fn(...args));
121
- }
122
- if (this._onceListeners[event]) {
123
- this._onceListeners[event].forEach((fn) => fn(...args));
124
- this._onceListeners[event] = [];
125
- }
126
- return true;
127
- },
128
- read() {
129
- return null;
130
- },
131
- setEncoding() {
132
- return this;
133
- },
134
- setMaxListeners(n) {
135
- this._maxListeners = n;
136
- return this;
137
- },
138
- getMaxListeners() {
139
- return this._maxListeners;
140
- },
141
- pipe(dest) {
142
- return dest;
143
- },
144
- };
145
- // Create stderr stream stub
146
- this.stderr = {
147
- readable: true,
148
- _listeners: {},
149
- _onceListeners: {},
150
- _maxListeners: 10,
151
- _maxListenersWarned: new Set(),
152
- on(event, listener) {
153
- if (!this._listeners[event])
154
- this._listeners[event] = [];
155
- this._listeners[event].push(listener);
156
- checkStreamMaxListeners(this, event);
157
- return this;
158
- },
159
- once(event, listener) {
160
- if (!this._onceListeners[event])
161
- this._onceListeners[event] = [];
162
- this._onceListeners[event].push(listener);
163
- checkStreamMaxListeners(this, event);
164
- return this;
165
- },
166
- emit(event, ...args) {
167
- if (this._listeners[event]) {
168
- this._listeners[event].forEach((fn) => fn(...args));
169
- }
170
- if (this._onceListeners[event]) {
171
- this._onceListeners[event].forEach((fn) => fn(...args));
172
- this._onceListeners[event] = [];
173
- }
174
- return true;
175
- },
176
- read() {
177
- return null;
178
- },
179
- setEncoding() {
180
- return this;
181
- },
182
- setMaxListeners(n) {
183
- this._maxListeners = n;
184
- return this;
185
- },
186
- getMaxListeners() {
187
- return this._maxListeners;
188
- },
189
- pipe(dest) {
190
- return dest;
191
- },
192
- };
193
- this.stdio = [this.stdin, this.stdout, this.stderr];
194
- }
195
- on(event, listener) {
196
- if (!this._listeners[event])
197
- this._listeners[event] = [];
198
- this._listeners[event].push(listener);
199
- this._checkMaxListeners(event);
200
- return this;
201
- }
202
- once(event, listener) {
203
- if (!this._onceListeners[event])
204
- this._onceListeners[event] = [];
205
- this._onceListeners[event].push(listener);
206
- this._checkMaxListeners(event);
207
- return this;
208
- }
209
- off(event, listener) {
210
- if (this._listeners[event]) {
211
- const idx = this._listeners[event].indexOf(listener);
212
- if (idx !== -1)
213
- this._listeners[event].splice(idx, 1);
214
- }
215
- return this;
216
- }
217
- removeListener(event, listener) {
218
- return this.off(event, listener);
219
- }
220
- setMaxListeners(n) {
221
- this._maxListeners = n;
222
- return this;
223
- }
224
- getMaxListeners() {
225
- return this._maxListeners;
226
- }
227
- _checkMaxListeners(event) {
228
- if (this._maxListeners > 0 && !this._maxListenersWarned.has(event)) {
229
- const total = (this._listeners[event]?.length ?? 0) + (this._onceListeners[event]?.length ?? 0);
230
- if (total > this._maxListeners) {
231
- this._maxListenersWarned.add(event);
232
- const warning = `MaxListenersExceededWarning: Possible EventEmitter memory leak detected. ${total} ${event} listeners added to [ChildProcess]. MaxListeners is ${this._maxListeners}. Use emitter.setMaxListeners() to increase limit`;
233
- if (typeof console !== "undefined" && console.error) {
234
- console.error(warning);
235
- }
236
- }
237
- }
238
- }
239
- emit(event, ...args) {
240
- let handled = false;
241
- if (this._listeners[event]) {
242
- this._listeners[event].forEach((fn) => {
243
- fn(...args);
244
- handled = true;
245
- });
246
- }
247
- if (this._onceListeners[event]) {
248
- this._onceListeners[event].forEach((fn) => {
249
- fn(...args);
250
- handled = true;
251
- });
252
- this._onceListeners[event] = [];
253
- }
254
- return handled;
255
- }
256
- kill(_signal) {
257
- this.killed = true;
258
- this.signalCode = (typeof _signal === "string" ? _signal : "SIGTERM");
259
- return true;
260
- }
261
- ref() {
262
- return this;
263
- }
264
- unref() {
265
- return this;
266
- }
267
- disconnect() {
268
- this.connected = false;
269
- }
270
- _complete(stdout, stderr, code) {
271
- this.exitCode = code;
272
- // Emit data events for stdout/stderr as single chunks
273
- if (stdout) {
274
- const buf = typeof Buffer !== "undefined" ? Buffer.from(stdout) : stdout;
275
- this.stdout.emit("data", buf);
276
- }
277
- if (stderr) {
278
- const buf = typeof Buffer !== "undefined" ? Buffer.from(stderr) : stderr;
279
- this.stderr.emit("data", buf);
280
- }
281
- // Emit end events
282
- this.stdout.emit("end");
283
- this.stderr.emit("end");
284
- // Emit close event (code, signal)
285
- this.emit("close", code, this.signalCode);
286
- // Emit exit event
287
- this.emit("exit", code, this.signalCode);
288
- }
289
- }
290
- // exec - execute shell command, callback when done
291
- // Uses spawn("bash", ["-c", command]) internally
292
- // NOTE: WASIX bash returns incorrect exit codes (45 instead of 0) for -c flag,
293
- // so error will be set even on successful commands. The stdout/stderr are correct.
294
- function exec(command, options, callback) {
295
- if (typeof options === "function") {
296
- callback = options;
297
- options = {};
298
- }
299
- // Use spawn with shell to execute the command
300
- const child = spawn("bash", ["-c", command], { shell: false });
301
- child.spawnargs = ["bash", "-c", command];
302
- child.spawnfile = "bash";
303
- // Collect output and invoke callback with maxBuffer enforcement
304
- const maxBuffer = options?.maxBuffer ?? 1024 * 1024;
305
- let stdout = "";
306
- let stderr = "";
307
- let stdoutBytes = 0;
308
- let stderrBytes = 0;
309
- let maxBufferExceeded = false;
310
- child.stdout.on("data", (data) => {
311
- if (maxBufferExceeded)
312
- return;
313
- const chunk = String(data);
314
- stdout += chunk;
315
- stdoutBytes += chunk.length;
316
- if (stdoutBytes > maxBuffer) {
317
- maxBufferExceeded = true;
318
- child.kill("SIGTERM");
319
- }
320
- });
321
- child.stderr.on("data", (data) => {
322
- if (maxBufferExceeded)
323
- return;
324
- const chunk = String(data);
325
- stderr += chunk;
326
- stderrBytes += chunk.length;
327
- if (stderrBytes > maxBuffer) {
328
- maxBufferExceeded = true;
329
- child.kill("SIGTERM");
330
- }
331
- });
332
- child.on("close", (...args) => {
333
- const code = args[0];
334
- if (callback) {
335
- if (maxBufferExceeded) {
336
- const err = new Error("stdout maxBuffer length exceeded");
337
- err.code = "ERR_CHILD_PROCESS_STDIO_MAXBUFFER";
338
- err.killed = true;
339
- err.cmd = command;
340
- err.stdout = stdout;
341
- err.stderr = stderr;
342
- callback(err, stdout, stderr);
343
- }
344
- else if (code !== 0) {
345
- const err = new Error("Command failed: " + command);
346
- err.code = code;
347
- err.killed = false;
348
- err.signal = null;
349
- err.cmd = command;
350
- err.stdout = stdout;
351
- err.stderr = stderr;
352
- callback(err, stdout, stderr);
353
- }
354
- else {
355
- callback(null, stdout, stderr);
356
- }
357
- }
358
- });
359
- child.on("error", (err) => {
360
- if (callback) {
361
- const error = err instanceof Error ? err : new Error(String(err));
362
- error.code = 1;
363
- error.stdout = stdout;
364
- error.stderr = stderr;
365
- callback(error, stdout, stderr);
366
- }
367
- });
368
- return child;
369
- }
370
- // execSync - synchronous shell execution
371
- // Uses spawnSync("bash", ["-c", command]) internally
372
- function execSync(command, options) {
373
- const opts = options || {};
374
- if (typeof _childProcessSpawnSync === "undefined") {
375
- throw new Error("child_process.execSync requires CommandExecutor to be configured");
376
- }
377
- // Default maxBuffer 1MB (Node.js convention)
378
- const maxBuffer = opts.maxBuffer ?? 1024 * 1024;
379
- // Use synchronous bridge call
380
- const result = _childProcessSpawnSync("bash", JSON.stringify(["-c", command]), JSON.stringify({ cwd: opts.cwd, env: opts.env, maxBuffer }));
381
- if (result.maxBufferExceeded) {
382
- const err = new Error("stdout maxBuffer length exceeded");
383
- err.code = "ERR_CHILD_PROCESS_STDIO_MAXBUFFER";
384
- err.stdout = result.stdout;
385
- err.stderr = result.stderr;
386
- throw err;
387
- }
388
- if (result.code !== 0) {
389
- const err = new Error("Command failed: " + command);
390
- err.status = result.code;
391
- err.stdout = result.stdout;
392
- err.stderr = result.stderr;
393
- err.output = [null, result.stdout, result.stderr];
394
- throw err;
395
- }
396
- if (opts.encoding === "buffer" || !opts.encoding) {
397
- return typeof Buffer !== "undefined" ? Buffer.from(result.stdout) : result.stdout;
398
- }
399
- return result.stdout;
400
- }
401
- // spawn - spawn a command with streaming
402
- function spawn(command, args, options) {
403
- let argsArray = [];
404
- let opts = {};
405
- if (!Array.isArray(args)) {
406
- opts = args || {};
407
- }
408
- else {
409
- argsArray = args;
410
- opts = options || {};
411
- }
412
- const child = new ChildProcess();
413
- child.spawnfile = command;
414
- child.spawnargs = [command, ...argsArray];
415
- // Check if streaming mode is available
416
- if (typeof _childProcessSpawnStart !== "undefined") {
417
- // Use process.cwd() as default if no cwd specified
418
- // This ensures process.chdir() changes are reflected in child processes
419
- const effectiveCwd = opts.cwd ?? (typeof process !== "undefined" ? process.cwd() : "/");
420
- // Streaming mode - spawn immediately
421
- const sessionId = _childProcessSpawnStart(command, JSON.stringify(argsArray), JSON.stringify({ cwd: effectiveCwd, env: opts.env }));
422
- activeChildren.set(sessionId, child);
423
- // Register handle to keep sandbox alive until child exits
424
- // See: docs-internal/node/ACTIVE_HANDLES.md
425
- if (typeof _registerHandle === "function") {
426
- _registerHandle(`child:${sessionId}`, `child_process: ${command} ${argsArray.join(" ")}`);
427
- }
428
- // Override stdin methods for streaming
429
- child.stdin.write = (data) => {
430
- if (typeof _childProcessStdinWrite === "undefined")
431
- return false;
432
- const bytes = typeof data === "string" ? new TextEncoder().encode(data) : data;
433
- _childProcessStdinWrite(sessionId, bytes);
434
- return true;
435
- };
436
- child.stdin.end = () => {
437
- if (typeof _childProcessStdinClose !== "undefined") {
438
- _childProcessStdinClose(sessionId);
439
- }
440
- child.stdin.writable = false;
441
- };
442
- // Override kill method
443
- child.kill = (signal) => {
444
- if (typeof _childProcessKill === "undefined")
445
- return false;
446
- const sig = signal === "SIGKILL" || signal === 9
447
- ? 9
448
- : signal === "SIGINT" || signal === 2
449
- ? 2
450
- : 15;
451
- _childProcessKill(sessionId, sig);
452
- child.killed = true;
453
- child.signalCode = (typeof signal === "string" ? signal : "SIGTERM");
454
- return true;
455
- };
456
- return child;
457
- }
458
- // Fallback: no CommandExecutor available
459
- const err = new Error("child_process.spawn requires CommandExecutor to be configured");
460
- // Emit error asynchronously to match Node.js behavior
461
- setTimeout(() => {
462
- child.emit("error", err);
463
- child._complete("", err.message, 1);
464
- }, 0);
465
- return child;
466
- }
467
- // spawnSync - synchronous spawn
468
- function spawnSync(command, args, options) {
469
- let argsArray = [];
470
- let opts = {};
471
- if (!Array.isArray(args)) {
472
- opts = args || {};
473
- }
474
- else {
475
- argsArray = args;
476
- opts = options || {};
477
- }
478
- if (typeof _childProcessSpawnSync === "undefined") {
479
- return {
480
- pid: _nextChildPid++,
481
- output: [null, "", "child_process.spawnSync requires CommandExecutor to be configured"],
482
- stdout: "",
483
- stderr: "child_process.spawnSync requires CommandExecutor to be configured",
484
- status: 1,
485
- signal: null,
486
- error: new Error("child_process.spawnSync requires CommandExecutor to be configured"),
487
- };
488
- }
489
- try {
490
- // Use process.cwd() as default if no cwd specified
491
- // This ensures process.chdir() changes are reflected in child processes
492
- const effectiveCwd = opts.cwd ?? (typeof process !== "undefined" ? process.cwd() : "/");
493
- // Pass maxBuffer through to host for enforcement
494
- const maxBuffer = opts.maxBuffer;
495
- // Args and options passed as JSON strings for transferability
496
- const result = _childProcessSpawnSync(command, JSON.stringify(argsArray), JSON.stringify({ cwd: effectiveCwd, env: opts.env, maxBuffer }));
497
- const stdoutBuf = typeof Buffer !== "undefined" ? Buffer.from(result.stdout) : result.stdout;
498
- const stderrBuf = typeof Buffer !== "undefined" ? Buffer.from(result.stderr) : result.stderr;
499
- if (result.maxBufferExceeded) {
500
- const err = new Error("stdout maxBuffer length exceeded");
501
- err.code = "ERR_CHILD_PROCESS_STDIO_MAXBUFFER";
502
- return {
503
- pid: _nextChildPid++,
504
- output: [null, stdoutBuf, stderrBuf],
505
- stdout: stdoutBuf,
506
- stderr: stderrBuf,
507
- status: result.code,
508
- signal: null,
509
- error: err,
510
- };
511
- }
512
- return {
513
- pid: _nextChildPid++,
514
- output: [null, stdoutBuf, stderrBuf],
515
- stdout: stdoutBuf,
516
- stderr: stderrBuf,
517
- status: result.code,
518
- signal: null,
519
- error: undefined,
520
- };
521
- }
522
- catch (err) {
523
- const errMsg = err instanceof Error ? err.message : String(err);
524
- const stderrBuf = typeof Buffer !== "undefined" ? Buffer.from(errMsg) : errMsg;
525
- return {
526
- pid: _nextChildPid++,
527
- output: [null, "", stderrBuf],
528
- stdout: typeof Buffer !== "undefined" ? Buffer.from("") : "",
529
- stderr: stderrBuf,
530
- status: 1,
531
- signal: null,
532
- error: err instanceof Error ? err : new Error(String(err)),
533
- };
534
- }
535
- }
536
- // execFile - execute a file directly
537
- function execFile(file, args, options, callback) {
538
- let argsArray = [];
539
- let opts = {};
540
- let cb;
541
- if (typeof args === "function") {
542
- cb = args;
543
- }
544
- else if (typeof options === "function") {
545
- argsArray = args.slice();
546
- cb = options;
547
- }
548
- else {
549
- argsArray = Array.isArray(args) ? args : [];
550
- opts = options || {};
551
- cb = callback;
552
- }
553
- // execFile is like spawn but with callback, with maxBuffer enforcement
554
- const maxBuffer = opts.maxBuffer ?? 1024 * 1024;
555
- const child = spawn(file, argsArray, opts);
556
- let stdout = "";
557
- let stderr = "";
558
- let stdoutBytes = 0;
559
- let stderrBytes = 0;
560
- let maxBufferExceeded = false;
561
- child.stdout.on("data", (data) => {
562
- const chunk = String(data);
563
- stdout += chunk;
564
- stdoutBytes += chunk.length;
565
- if (stdoutBytes > maxBuffer && !maxBufferExceeded) {
566
- maxBufferExceeded = true;
567
- child.kill("SIGTERM");
568
- }
569
- });
570
- child.stderr.on("data", (data) => {
571
- const chunk = String(data);
572
- stderr += chunk;
573
- stderrBytes += chunk.length;
574
- if (stderrBytes > maxBuffer && !maxBufferExceeded) {
575
- maxBufferExceeded = true;
576
- child.kill("SIGTERM");
577
- }
578
- });
579
- child.on("close", (...args) => {
580
- const code = args[0];
581
- if (cb) {
582
- if (maxBufferExceeded) {
583
- const err = new Error("stdout maxBuffer length exceeded");
584
- err.code = "ERR_CHILD_PROCESS_STDIO_MAXBUFFER";
585
- err.killed = true;
586
- err.stdout = stdout;
587
- err.stderr = stderr;
588
- cb(err, stdout, stderr);
589
- }
590
- else if (code !== 0) {
591
- const err = new Error("Command failed: " + file);
592
- err.code = code;
593
- err.stdout = stdout;
594
- err.stderr = stderr;
595
- cb(err, stdout, stderr);
596
- }
597
- else {
598
- cb(null, stdout, stderr);
599
- }
600
- }
601
- });
602
- child.on("error", (err) => {
603
- if (cb) {
604
- cb(err, stdout, stderr);
605
- }
606
- });
607
- return child;
608
- }
609
- // execFileSync
610
- function execFileSync(file, args, options) {
611
- let argsArray = [];
612
- let opts = {};
613
- if (!Array.isArray(args)) {
614
- opts = args || {};
615
- }
616
- else {
617
- argsArray = args;
618
- opts = options || {};
619
- }
620
- // Default maxBuffer 1MB for execFileSync (Node.js convention)
621
- const maxBuffer = opts.maxBuffer ?? 1024 * 1024;
622
- const result = spawnSync(file, argsArray, { ...opts, maxBuffer });
623
- if (result.error && String(result.error.code) === "ERR_CHILD_PROCESS_STDIO_MAXBUFFER") {
624
- throw result.error;
625
- }
626
- if (result.status !== 0) {
627
- const err = new Error("Command failed: " + file);
628
- err.status = result.status ?? undefined;
629
- err.stdout = String(result.stdout);
630
- err.stderr = String(result.stderr);
631
- throw err;
632
- }
633
- if (opts.encoding === "buffer" || !opts.encoding) {
634
- return result.stdout;
635
- }
636
- return typeof result.stdout === "string" ? result.stdout : result.stdout.toString(opts.encoding);
637
- }
638
- // fork - intentionally not implemented (IPC between processes not supported in sandbox)
639
- function fork(_modulePath, _args, _options) {
640
- throw new Error("child_process.fork is not supported in sandbox");
641
- }
642
- // Create the child_process module
643
- const childProcess = {
644
- ChildProcess,
645
- exec,
646
- execSync,
647
- spawn,
648
- spawnSync,
649
- execFile,
650
- execFileSync,
651
- fork,
652
- };
653
- // Expose to global for require() to use
654
- exposeCustomGlobal("_childProcessModule", childProcess);
655
- export { ChildProcess, exec, execSync, spawn, spawnSync, execFile, execFileSync, fork };
656
- export default childProcess;