RaBU1234 commited on
Commit
d543d66
·
verified ·
1 Parent(s): 216e98d

Update simple-sandbox-manager.js

Browse files
Files changed (1) hide show
  1. simple-sandbox-manager.js +49 -16
simple-sandbox-manager.js CHANGED
@@ -12,11 +12,32 @@ class SimpleSandboxManager extends EventEmitter {
12
  this.portCounter = 3000;
13
  this.streamClients = new Map();
14
  this.commandData = new Map();
 
15
  }
16
 
17
  async initialize() {
18
  await fs.mkdir(this.basePath, { recursive: true });
19
- console.log('✅ Namespace-Isolated Sandbox Manager initialized');
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
20
  }
21
 
22
  async createSandbox(sandboxId, options = {}) {
@@ -27,12 +48,20 @@ class SimpleSandboxManager extends EventEmitter {
27
  const workspacePath = path.join(sandboxPath, 'workspace');
28
  await fs.mkdir(workspacePath, { recursive: true });
29
 
 
 
 
 
 
 
 
30
  const port = this.portCounter++;
31
 
32
  const sandbox = {
33
  sandboxId,
34
  path: sandboxPath,
35
  workspacePath,
 
36
  port,
37
  exposedPorts: ports,
38
  timeout,
@@ -42,7 +71,7 @@ class SimpleSandboxManager extends EventEmitter {
42
  };
43
 
44
  this.sandboxes.set(sandboxId, sandbox);
45
- console.log(`✅ Sandbox created: ${sandboxId} (port: ${port}, timeout: ${timeout}ms)`);
46
 
47
  return sandbox;
48
  }
@@ -90,18 +119,24 @@ class SimpleSandboxManager extends EventEmitter {
90
  const { background = false, timeout = 300000 } = options;
91
  const fullCommand = `${command} ${args.join(' ')}`;
92
 
93
- // ✅ NO WHITELIST - Uses Linux namespaces for isolation
94
- // unshare creates isolated namespace environment:
95
- // --net: Network namespace (isolated network stack)
96
- // --pid: PID namespace (can't see/kill external processes)
97
- // --mount: Mount namespace (isolated filesystem view)
98
- // --fork: Fork into namespace before executing
99
- const isolatedCommand = `
100
- unshare --net --pid --mount --fork sh -c "
101
- cd ${sandbox.workspacePath}
102
- ${fullCommand}
103
- "
104
- `;
 
 
 
 
 
 
105
 
106
  if (background) {
107
  return this.startBackgroundProcess(sandboxId, isolatedCommand, sandbox.path, sandbox.port);
@@ -268,12 +303,10 @@ class SimpleSandboxManager extends EventEmitter {
268
  const filePath = file.path;
269
  let fileContent = file.content;
270
 
271
- // Handle object content (JSON files)
272
  if (typeof fileContent === 'object' && fileContent !== null) {
273
  fileContent = JSON.stringify(fileContent, null, 2);
274
  }
275
 
276
- // Ensure string
277
  if (typeof fileContent !== 'string') {
278
  fileContent = String(fileContent);
279
  }
 
12
  this.portCounter = 3000;
13
  this.streamClients = new Map();
14
  this.commandData = new Map();
15
+ this.gvisorAvailable = false;
16
  }
17
 
18
  async initialize() {
19
  await fs.mkdir(this.basePath, { recursive: true });
20
+
21
+ // Check if gVisor is available
22
+ try {
23
+ await new Promise((resolve, reject) => {
24
+ exec('runsc --version', (error, stdout) => {
25
+ if (error) {
26
+ console.warn('⚠️ gVisor not available, falling back to direct execution');
27
+ this.gvisorAvailable = false;
28
+ resolve();
29
+ } else {
30
+ console.log('✅ gVisor available:', stdout.trim());
31
+ this.gvisorAvailable = true;
32
+ resolve();
33
+ }
34
+ });
35
+ });
36
+ } catch (e) {
37
+ this.gvisorAvailable = false;
38
+ }
39
+
40
+ console.log(`✅ Sandbox Manager initialized (gVisor: ${this.gvisorAvailable ? 'enabled' : 'disabled'})`);
41
  }
42
 
43
  async createSandbox(sandboxId, options = {}) {
 
48
  const workspacePath = path.join(sandboxPath, 'workspace');
49
  await fs.mkdir(workspacePath, { recursive: true });
50
 
51
+ // Create rootfs structure for gVisor
52
+ const rootfsPath = path.join(sandboxPath, 'rootfs');
53
+ await fs.mkdir(rootfsPath, { recursive: true });
54
+ await fs.mkdir(path.join(rootfsPath, 'tmp'), { recursive: true });
55
+ await fs.mkdir(path.join(rootfsPath, 'proc'), { recursive: true });
56
+ await fs.mkdir(path.join(rootfsPath, 'dev'), { recursive: true });
57
+
58
  const port = this.portCounter++;
59
 
60
  const sandbox = {
61
  sandboxId,
62
  path: sandboxPath,
63
  workspacePath,
64
+ rootfsPath,
65
  port,
66
  exposedPorts: ports,
67
  timeout,
 
71
  };
72
 
73
  this.sandboxes.set(sandboxId, sandbox);
74
+ console.log(`✅ Sandbox created: ${sandboxId} (port: ${port}, gVisor: ${this.gvisorAvailable})`);
75
 
76
  return sandbox;
77
  }
 
119
  const { background = false, timeout = 300000 } = options;
120
  const fullCommand = `${command} ${args.join(' ')}`;
121
 
122
+ let isolatedCommand;
123
+
124
+ if (this.gvisorAvailable) {
125
+ // Try gVisor execution
126
+ // Create OCI bundle configuration
127
+ const bundlePath = path.join(sandbox.path, 'bundle');
128
+ await fs.mkdir(bundlePath, { recursive: true });
129
+
130
+ // gVisor command with runsc
131
+ isolatedCommand = `runsc --rootless=true --network=none run -bundle ${bundlePath} sh -c "cd /workspace && ${fullCommand}"`;
132
+
133
+ console.log(`🛡️ Running with gVisor: ${command}`);
134
+ } else {
135
+ // ❌ Fallback to direct execution with resource limits
136
+ isolatedCommand = `sh -c "cd ${sandbox.workspacePath} && ulimit -t 300 -v 1048576 && timeout 300s ${fullCommand}"`;
137
+
138
+ console.log(`⚠️ Running without isolation: ${command}`);
139
+ }
140
 
141
  if (background) {
142
  return this.startBackgroundProcess(sandboxId, isolatedCommand, sandbox.path, sandbox.port);
 
303
  const filePath = file.path;
304
  let fileContent = file.content;
305
 
 
306
  if (typeof fileContent === 'object' && fileContent !== null) {
307
  fileContent = JSON.stringify(fileContent, null, 2);
308
  }
309
 
 
310
  if (typeof fileContent !== 'string') {
311
  fileContent = String(fileContent);
312
  }