sandboxbox 1.0.5 → 1.0.7

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/bin/bwrap ADDED
@@ -0,0 +1,50 @@
1
+ #!/bin/bash
2
+ # Minimal bubblewrap fallback for SandboxBox
3
+ # This provides basic namespace isolation functionality
4
+
5
+ # Handle --version flag for compatibility
6
+ if [[ "$1" == "--version" ]]; then
7
+ echo "bubblewrap 0.11.0 (minimal fallback for SandboxBox)"
8
+ exit 0
9
+ fi
10
+
11
+ # Handle --help flag
12
+ if [[ "$1" == "--help" ]] || [[ "$1" == "-h" ]]; then
13
+ echo "bubblewrap - minimal fallback version"
14
+ echo ""
15
+ echo "⚠️ This is a minimal fallback for SandboxBox"
16
+ echo "💡 For full functionality, install bubblewrap:"
17
+ echo " sudo apt-get install bubblewrap"
18
+ echo ""
19
+ echo "Usage: bwrap [options] -- command [args]"
20
+ exit 0
21
+ fi
22
+
23
+ echo "⚠️ Using minimal bubblewrap fallback"
24
+ echo "💡 For full functionality, install bubblewrap:"
25
+ echo " sudo apt-get install bubblewrap"
26
+ echo ""
27
+
28
+ # Filter out bubblewrap-specific options that unshare doesn't support
29
+ ARGS=()
30
+ for arg in "$@"; do
31
+ case "$arg" in
32
+ --ro-bind|--bind|--dev-bind|--proc|--tmpfs|--symlink|--dir|--file|--setenv|--die-with-parent|--new-session|--share-net|--unshare-net|--unshare-pid|--unshare-ipc|--unshare-uts|--unshare-cgroup|--unshare-user)
33
+ # Skip bubblewrap-specific options
34
+ ;;
35
+ *)
36
+ ARGS+=("$arg")
37
+ ;;
38
+ esac
39
+ done
40
+
41
+ # Basic namespace isolation using unshare
42
+ exec unshare \
43
+ --pid \
44
+ --mount \
45
+ --uts \
46
+ --ipc \
47
+ --net \
48
+ --fork \
49
+ --mount-proc \
50
+ "${ARGS[@]}"
package/cli.js CHANGED
@@ -10,9 +10,9 @@
10
10
  * npx sandboxbox shell <project> # Interactive shell
11
11
  */
12
12
 
13
- // Debug: Make sure the script starts
14
- console.log('🚀 SandboxBox starting...');
13
+ // Optional debug output
15
14
  if (process.env.DEBUG) {
15
+ console.log('🔧 Debug: SandboxBox CLI starting...');
16
16
  console.log(`🔧 Debug: Platform: ${process.platform}`);
17
17
  console.log(`🔧 Debug: Node.js: ${process.version}`);
18
18
  console.log(`🔧 Debug: Args: ${process.argv.slice(2).join(' ')}`);
@@ -192,7 +192,13 @@ async function main() {
192
192
  case 'run':
193
193
  const projectDir = commandArgs[0] || '.';
194
194
  console.log(color('blue', '🚀 Running Playwright tests...'));
195
- if (!(await checkBubblewrap())) process.exit(1);
195
+ console.log(color('yellow', `Project directory: ${projectDir}`));
196
+
197
+ if (!(await checkBubblewrap())) {
198
+ console.log(color('red', '❌ Cannot run tests without bubblewrap'));
199
+ process.exit(1);
200
+ }
201
+
196
202
  runScript('./container.js', ['run', projectDir]);
197
203
  break;
198
204
 
package/package.json CHANGED
@@ -1,11 +1,11 @@
1
1
  {
2
2
  "name": "sandboxbox",
3
- "version": "1.0.5",
3
+ "version": "1.0.7",
4
4
  "description": "Zero-privilege container runner with Playwright support",
5
5
  "type": "module",
6
6
  "main": "index.js",
7
7
  "bin": {
8
- "sandboxbox": "./test-cli.js"
8
+ "sandboxbox": "./cli.js"
9
9
  },
10
10
  "scripts": {
11
11
  "install": "node scripts/build.js",
package/scripts/build.js CHANGED
@@ -59,10 +59,146 @@ async function downloadAndBuild() {
59
59
  // System bwrap not found, continue with build
60
60
  }
61
61
 
62
- // Build from source like SQLite does
62
+ // Try to download pre-built binary first
63
+ if (await downloadPreBuiltBinary(binaryPath)) {
64
+ return;
65
+ }
66
+
67
+ // Build from source like SQLite does as last resort
63
68
  await buildFromSource(binaryPath);
64
69
  }
65
70
 
71
+ async function downloadPreBuiltBinary(binaryPath) {
72
+ console.log('📥 Trying pre-built bubblewrap binary...');
73
+
74
+ const arch = process.arch === 'x64' ? 'x86_64' : process.arch;
75
+ const possibleUrls = [
76
+ // Alpine packages (HTTPS) - use the actual available version
77
+ `https://dl-cdn.alpinelinux.org/alpine/v3.20/main/${arch}/bubblewrap-0.10.0-r0.apk`,
78
+ // Try some common locations for pre-built binaries
79
+ `https://github.com/containers/bubblewrap/releases/download/v${BWRAP_VERSION}/bubblewrap-${BWRAP_VERSION}-${arch}.tar.xz`,
80
+ `https://github.com/containers/bubblewrap/releases/download/v${BWRAP_VERSION}/bubblewrap-${BWRAP_VERSION}.tar.gz`,
81
+ ];
82
+
83
+ for (const url of possibleUrls) {
84
+ try {
85
+ console.log(`📦 Trying: ${url.split('/').pop()}`);
86
+
87
+ const response = await new Promise((resolve, reject) => {
88
+ https.get(url, (res) => {
89
+ if (res.statusCode === 200) {
90
+ resolve(res);
91
+ } else {
92
+ reject(new Error(`HTTP ${res.statusCode}`));
93
+ }
94
+ }).on('error', reject);
95
+ });
96
+
97
+ if (url.endsWith('.apk')) {
98
+ // Handle Alpine package
99
+ console.log('📦 Alpine package found, extracting...');
100
+ return await extractAlpinePackage(url, binaryPath);
101
+ } else {
102
+ // Handle tarball
103
+ return await extractTarball(url, binaryPath);
104
+ }
105
+ } catch (error) {
106
+ console.log(`❌ Failed: ${error.message}`);
107
+ continue;
108
+ }
109
+ }
110
+
111
+ console.log('❌ No pre-built binaries available');
112
+ return false;
113
+ }
114
+
115
+ async function extractAlpinePackage(url, binaryPath) {
116
+ const tmpDir = fs.mkdtempSync(path.join(process.env.TMPDIR || '/tmp', 'apk-extract-'));
117
+
118
+ try {
119
+ const apkPath = path.join(tmpDir, 'bubblewrap.apk');
120
+
121
+ // Download APK
122
+ await new Promise((resolve, reject) => {
123
+ const file = fs.createWriteStream(apkPath);
124
+ https.get(url, (response) => {
125
+ response.pipe(file);
126
+ file.on('finish', resolve);
127
+ }).on('error', reject);
128
+ });
129
+
130
+ // Extract APK (tar.gz format)
131
+ execSync(`tar -xzf "${apkPath}" -C "${tmpDir}"`, { stdio: 'inherit' });
132
+
133
+ // Find the binary
134
+ const possiblePaths = [
135
+ path.join(tmpDir, 'usr', 'bin', 'bwrap'),
136
+ path.join(tmpDir, 'bin', 'bwrap'),
137
+ ];
138
+
139
+ for (const possiblePath of possiblePaths) {
140
+ if (fs.existsSync(possiblePath)) {
141
+ fs.copyFileSync(possiblePath, binaryPath);
142
+ fs.chmodSync(binaryPath, 0o755);
143
+ console.log('✅ Extracted pre-built binary from Alpine package');
144
+ return true;
145
+ }
146
+ }
147
+
148
+ throw new Error('Binary not found in package');
149
+ } finally {
150
+ fs.rmSync(tmpDir, { recursive: true, force: true });
151
+ }
152
+ }
153
+
154
+ async function extractTarball(url, binaryPath) {
155
+ const tmpDir = fs.mkdtempSync(path.join(process.env.TMPDIR || '/tmp', 'tar-extract-'));
156
+
157
+ try {
158
+ const tarballPath = path.join(tmpDir, 'bubblewrap.tar');
159
+
160
+ // Download tarball
161
+ await new Promise((resolve, reject) => {
162
+ const file = fs.createWriteStream(tarballPath);
163
+ https.get(url, (response) => {
164
+ response.pipe(file);
165
+ file.on('finish', resolve);
166
+ }).on('error', reject);
167
+ });
168
+
169
+ // Extract with available tools
170
+ if (tarballPath.endsWith('.xz')) {
171
+ try {
172
+ execSync(`tar -xf "${tarballPath}" -C "${tmpDir}"`, { stdio: 'inherit' });
173
+ } catch (e) {
174
+ throw new Error('xz extraction failed - need xz-utils');
175
+ }
176
+ } else {
177
+ execSync(`tar -xzf "${tarballPath}" -C "${tmpDir}"`, { stdio: 'inherit' });
178
+ }
179
+
180
+ // Find the binary
181
+ const possiblePaths = [
182
+ path.join(tmpDir, `bubblewrap-${BWRAP_VERSION}`, 'bwrap'),
183
+ path.join(tmpDir, 'bwrap'),
184
+ path.join(tmpDir, 'bin', 'bwrap'),
185
+ ];
186
+
187
+ for (const possiblePath of possiblePaths) {
188
+ if (fs.existsSync(possiblePath)) {
189
+ fs.copyFileSync(possiblePath, binaryPath);
190
+ fs.chmodSync(binaryPath, 0o755);
191
+ console.log('✅ Extracted pre-built binary');
192
+ return true;
193
+ }
194
+ }
195
+
196
+ throw new Error('Binary not found in tarball');
197
+ } finally {
198
+ fs.rmSync(tmpDir, { recursive: true, force: true });
199
+ }
200
+ }
201
+
66
202
  async function buildFromSource(binaryPath) {
67
203
  console.log('🔨 Building bubblewrap from source (SQLite-style)...');
68
204
 
@@ -193,5 +329,68 @@ exit 1
193
329
  downloadAndBuild().catch(error => {
194
330
  console.error('❌ Build failed:', error.message);
195
331
  console.log('💡 SandboxBox will still work with system bubblewrap if available');
332
+
333
+ // Create a minimal fallback as last resort
334
+ createMinimalBubblewrap(path.join(BINARY_DIR, 'bwrap'));
196
335
  process.exit(0); // Don't fail npm install
197
- });
336
+ });
337
+
338
+ function createMinimalBubblewrap(binaryPath) {
339
+ console.log('🔧 Creating minimal bubblewrap fallback...');
340
+
341
+ const minimalBwrap = `#!/bin/bash
342
+ # Minimal bubblewrap fallback for SandboxBox
343
+ # This provides basic namespace isolation functionality
344
+
345
+ # Handle --version flag for compatibility
346
+ if [[ "$1" == "--version" ]]; then
347
+ echo "bubblewrap 0.11.0 (minimal fallback for SandboxBox)"
348
+ exit 0
349
+ fi
350
+
351
+ # Handle --help flag
352
+ if [[ "$1" == "--help" ]] || [[ "$1" == "-h" ]]; then
353
+ echo "bubblewrap - minimal fallback version"
354
+ echo ""
355
+ echo "⚠️ This is a minimal fallback for SandboxBox"
356
+ echo "💡 For full functionality, install bubblewrap:"
357
+ echo " sudo apt-get install bubblewrap"
358
+ echo ""
359
+ echo "Usage: bwrap [options] -- command [args]"
360
+ exit 0
361
+ fi
362
+
363
+ echo "⚠️ Using minimal bubblewrap fallback"
364
+ echo "💡 For full functionality, install bubblewrap:"
365
+ echo " sudo apt-get install bubblewrap"
366
+ echo ""
367
+
368
+ # Filter out bubblewrap-specific options that unshare doesn't support
369
+ ARGS=()
370
+ for arg in "$@"; do
371
+ case "$arg" in
372
+ --ro-bind|--bind|--dev-bind|--proc|--tmpfs|--symlink|--dir|--file|--setenv|--die-with-parent|--new-session|--share-net|--unshare-net|--unshare-pid|--unshare-ipc|--unshare-uts|--unshare-cgroup|--unshare-user)
373
+ # Skip bubblewrap-specific options
374
+ ;;
375
+ *)
376
+ ARGS+=("$arg")
377
+ ;;
378
+ esac
379
+ done
380
+
381
+ # Basic namespace isolation using unshare
382
+ exec unshare \\
383
+ --pid \\
384
+ --mount \\
385
+ --uts \\
386
+ --ipc \\
387
+ --net \\
388
+ --fork \\
389
+ --mount-proc \\
390
+ "\${ARGS[@]}"
391
+ `;
392
+
393
+ fs.writeFileSync(binaryPath, minimalBwrap);
394
+ fs.chmodSync(binaryPath, 0o755);
395
+ console.log('✅ Created minimal bubblewrap fallback');
396
+ }
package/test-cli.js CHANGED
File without changes