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.
- Prevents RCE via unsafe
shell_exec/exec/system/passthru/proc_open. - Isolates filesystem, environment variables, and network for the child process.
- Compatible with PHP 5.6+ and Laravel 5.x to 12.x.
composer require greenn-labs/bubblewrap-sandboxFor Laravel >= 5.5, package auto-discovery already registers the provider and the BubblewrapSandbox alias.
For older versions, add manually in config/app.php:
Greenn\Libs\Laravel\BubblewrapServiceProvider::class,
'BubblewrapSandbox' => Greenn\Libs\Laravel\BubblewrapSandbox::class,Publish the configuration (optional):
php artisan vendor:publish --tag=sandbox-configuse Greenn\Libs\BubblewrapSandbox;
$runner = app(BubblewrapSandbox::class); // or BubblewrapSandbox facade
// 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),
);
$process = $runner->run($command, $binds, '/tmp', null, 120);
$output = $process->getOutput();- Quick usage guide: docs/USING_SANDBOX.md
- Every command is prefixed with
bwrapand--unshare-all --die-with-parent --new-session. - Default mounts:
/usr,/bin,/lib*,/sbinas read-only;/tmpisolated and writable. - PATH is limited (
/usr/bin:/bin:/usr/sbin:/sbin). - If
bwrapis unavailable or not executable, aBubblewrapUnavailableExceptionis thrown.
- 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).
Edit config/sandbox.php after publishing:
binary: path tobwrap.base_args: default flags (avoid removing unshare/die-with-parent).read_only_binds: automatic read-only binds.write_binds: writable binds (default only/tmp).
- 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.
- Requires PHP
ext-domenabled. - Local run (single version):
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
composer install --no-interaction --no-progress vendor/bin/phpunit
xdebug/pcovare installed). - Matrix via Docker:
The script spins up PHP containers and runs PHPUnit across multiple PHP/Laravel pairs. Adjust the
chmod +x tools/test-matrix.sh tools/test-matrix.sh
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).