PDF-X Secure Runner
Secure sandboxed document processing for PHP/Linux using Bubblewrap.
Run external document-processing tools against untrusted files while reducing their access to the application filesystem, environment, network and system resources.
Install
composer require pdf-x/secure-runner
Requires PHP 8.1+, and Linux with Bubblewrap (bwrap) for real containment.
prlimit (util-linux) is recommended for resource limits.
Why use it?
PHP and Laravel applications often call PDF, image or office converters on files that users upload. Those parsers are large and receive hostile input. If such a tool runs directly under the web application’s Unix identity, a bug in it can expose everything that account can reach: application source, environment secrets, other jobs’ files, the network, and unbounded CPU, disk and time. Secure Runner runs just that one process inside a restricted boundary. This reduces the attack surface; it is a defence-in-depth control, not a guarantee.
What it isolates
- Job workspace: each job gets a private directory, mounted at
/work. Parent and sibling job directories are not mounted. - Network: disabled by default.
- Environment: inherited variables are cleared; only the ones you list are passed.
- Subprocesses: a wall-clock deadline and output cap kill the whole sandbox; nothing is left running.
- Resource usage: CPU time, file size and open files via
prlimit, with core dumps disabled. - Symlink escape attempts: only the workspace is mounted, and outputs are read through a check that refuses symlinks and paths outside it.
- Fail-closed: if Bubblewrap is missing or unusable, the runner throws. It never runs the tool unsandboxed.
Quick example
use PdfX\SecureRunner\{JobWorkspace, ResourceLimits, SandboxConfig, SecureRunner};
$workspace = JobWorkspace::create('/var/lib/myapp/jobs'); // private 0700 directory, must already exist
try {
$workspace->write('in.pdf', $uploadedBytes);
$runner = new SecureRunner(SandboxConfig::strict()); // strict = static binaries need nothing else
$result = $runner->run(
executable: '/usr/local/bin/pdfcpu', // absolute path to your own installed tool
arguments: ['info', $workspace->sandboxPath('in.pdf')], // paths as the sandbox sees them (/work/...)
workspace: $workspace,
limits: new ResourceLimits(timeoutSeconds: 60),
);
if ($result->isSuccessful()) {
echo $result->stdout;
}
} finally {
$workspace->cleanup();
}
pdfcpu is not bundled; install and verify your own binary. See the pdfcpu and Laravel examples.
Security boundary
Secure Runner isolates the spawned document-processing process. It does not automatically sandbox the parent PHP/Laravel
application, your queue worker, unrelated PHP code running in your own process (including in-process image decoding such as GD),
or the host kernel. A vulnerability in Bubblewrap, user namespaces or the kernel can defeat the sandbox. Treat produced files as untrusted.
Run bin/pdfx-sandbox-check on each host; a pass shows that environment behaved as tested, not a universal guarantee.
Read the security model before relying on it.
Documentation
- Architecture
- Security model
- Integration guide
- Security policy (how to report vulnerabilities)
- README
Engineering notes
- Sandboxing PDF Processing in PHP with Bubblewrap: the problem, the containment model, and its limits.
Package
- Packagist: https://packagist.org/packages/pdf-x/secure-runner
- GitHub: https://github.com/smithveg-stack/pdf-x-secure-runner
- Install:
composer require pdf-x/secure-runner - License: Apache-2.0
Project origin
Developed from security engineering work performed while building PDF-X, an online PDF processing service (https://pdf-x.co). This is a separate, standalone library.