securerun / bubblewrap-sandbox
Security layer to run external commands inside bubblewrap for Laravel 5 through 12.
Installs: 3
Dependents: 0
Suggesters: 0
Security: 0
Stars: 0
Watchers: 0
Forks: 0
Open Issues: 0
pkg:composer/securerun/bubblewrap-sandbox
Requires
- php: >=7.0
- illuminate/support: 5.*|6.*|7.*|8.*|9.*|10.*|11.*|12.*
- symfony/process: ^3.4|^4.4|^5.4|^6.4|^7.0
Requires (Dev)
- phpunit/phpunit: ^6.5 || ^7.5 || ^8.5 || ^9.6 || ^10.5 || ^11.0
This package is not auto-updated.
Last update: 2026-01-14 09:44:18 UTC
README
Security layer that forbids executing external commands without a bubblewrap sandbox. Designed for Laravel apps from 5 through 12 that need to process files (PDF, image, video, document) with mandatory bubblewrap (bwrap) isolation.
Why use this
- Prevents RCE via unsafe
shell_exec/exec/system/passthru/proc_open. - Isolates filesystem, environment variables, and network for the child process.
- Compatible with Laravel 5.x to 12.x on PHP 7.0+ (Composer requirement). Runtime code sticks to older syntax for legacy apps, but tests and support start on PHP 7.x; use PHP 7+ (or newer) in production.
- Runs on Linux only (bubblewrap is a Linux-specific sandbox).
Installation
composer require securerun/bubblewrap-sandbox
For Laravel >= 5.5, package auto-discovery already registers the provider and the BubblewrapSandbox alias (now pointing to the facade at SecureRun\BubblewrapSandbox).
For older versions, add manually in config/app.php:
SecureRun\Sandbox\BubblewrapServiceProvider::class, 'BubblewrapSandbox' => SecureRun\BubblewrapSandbox::class,
Publish the configuration (optional):
php artisan vendor:publish --tag=sandbox-config
Basic usage
use SecureRun\BubblewrapSandboxRunner; $runner = app(\SecureRun\BubblewrapSandboxRunner::class); // or the BubblewrapSandbox facade for static calls // Command to run inside the sandbox $command = array('gs', '-q', '-sDEVICE=png16m', '-o', '/tmp/out.png', '/tmp/in.pdf'); // Bind mounts for input/output (read-only by default) $binds = array( array('from' => storage_path('uploads/in.pdf'), 'to' => '/tmp/in.pdf', 'read_only' => true), array('from' => storage_path('tmp'), 'to' => '/tmp', 'read_only' => false), ); $wrapper = $runner->run($command, $binds, '/tmp', null, 120); $output = $wrapper->getOutput(); // ProcessWrapper works like Process // Optional: access environment variables (when explicitly enabled) use SecureRun\RunOptions; $wrapper = $runner->run($command, $binds, '/tmp', ['VAR' => 'value'], 120, [ RunOptions::UNSECURE_ENV_ACCESS => true ]); $env = $wrapper->getEnv(); // returns ['VAR' => 'value']
Or via the Laravel facade (no /Laravel namespace anymore):
use SecureRun\BubblewrapSandbox; $wrapper = BubblewrapSandbox::run(['ls', '-la']); $output = $wrapper->getOutput(); // ProcessWrapper is compatible with Process
Note: SecureRun\Sandbox\BubblewrapSandbox remains as a backwards-compatible shim for apps that imported the old namespace. Prefer SecureRun\BubblewrapSandbox (or the BubblewrapSandbox alias).
Documentation
- Quick usage guide: docs/USING_SANDBOX.md
- Run method parameters: docs/PARAMETROS_RUN.md
- Environment variables access examples: docs/EXEMPLOS_ENV.md
Advanced features
- RunOptions: Centralized option constants for the
run()method. UseRunOptions::UNSECURE_ENV_ACCESSinstead of string literals to prevent typos. - ProcessWrapper: The
run()method always returnsProcessWrapper(which is compatible with Symfony Process) for consistent return types. Environment variable access viagetEnv()is only available whenunsecure_env_accessis explicitly enabled.
Security rules enforced
- Every command is prefixed with
bwrapand--unshare-all --die-with-parent --new-session. - Default mounts:
/usr,/bin,/lib,/sbin,/etc/resolv.conf,/etc/sslas read-only (adds/lib64when the host has it);/tmpisolated and writable. - Default binary points to
/usr/bin/bwrap(adjustconfig/sandbox.phpifbwraplives elsewhere). - PATH is limited (
/usr/bin:/bin:/usr/sbin:/sbin). - If
bwrapis unavailable or not executable, aBubblewrapUnavailableExceptionis thrown.
Do not
- Do not call
shell_exec,exec,system,passthru,proc_open, or rawSymfony Processfor sensitive binaries. Always go throughBubblewrapSandbox. - Do not mount directories containing secrets (e.g.,
/home,/var/www/.env).
Configuration
Edit config/sandbox.php after publishing:
binary: path tobwrap(default/usr/bin/bwrap; usebwrapif it’s on PATH).base_args: default flags (avoid removing unshare/die-with-parent).read_only_binds: automatic read-only binds.write_binds: writable binds (default empty;/tmpis already a sandbox tmpfs).
Quick examples
- Image with ImageMagick:
['convert', '/tmp/in.png', '-resize', '800x600', '/tmp/out.png']. - Video with FFmpeg:
['ffmpeg', '-i', '/tmp/in.mp4', '-vf', 'scale=1280:720', '/tmp/out.mp4']plus binds for input/output paths. - PDF with Ghostscript: use the basic usage example.
Tests
-
Requires PHP
ext-domenabled. -
Local run (single version):
composer install --no-interaction --no-progress vendor/bin/phpunit
On PHP 5.6–7.x, Composer will pull PHPUnit 5.7; on PHP 8.x it will use PHPUnit 9.6 (coverage is optional if
xdebug/pcovare installed). -
Matrix via Docker:
chmod +x tools/test-matrix.sh tools/test-matrix.sh
The script spins up PHP containers and runs PHPUnit across multiple PHP/Laravel pairs. Adjust the
COMBOSlist to narrow versions. Note: the current test suite uses anonymous classes, so the PHP 5.6/Laravel 5.4 combo is commented out (PHP 5.6 lacks that feature).