greenn-labs / bubblewrap-sandbox
Security layer to run external commands inside bubblewrap for Laravel 5 through 12.
Installs: 6
Dependents: 0
Suggesters: 0
Security: 0
Stars: 0
Watchers: 0
Forks: 0
Open Issues: 0
pkg:composer/greenn-labs/bubblewrap-sandbox
Requires
- php: >=5.6
- 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: ^5.7 || ^9.6
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 PHP 5.6+ and Laravel 5.x to 12.x.
Installation
composer require greenn-labs/bubblewrap-sandbox
For 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-config
Basic usage
use 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();
Documentation
- Quick usage guide: docs/USING_SANDBOX.md
Security rules enforced
- 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
- 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.base_args: default flags (avoid removing unshare/die-with-parent).read_only_binds: automatic read-only binds.write_binds: writable binds (default only/tmp).
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 ifxdebug/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 theCOMBOSlist 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).