Security model
The goal is to reduce the attack surface available to a compromised document-processing binary. It is not a proof of safety.
Threat model
Attacker controls the input document. The binary has a memory-safety or logic bug that gives the attacker code execution inside the child process. The sandbox limits what that code can then reach.
Mitigations
| Risk | Mitigation |
|---|---|
Read application source, .env, other apps |
Not mounted. Only /work and the executable exist (plus opt-in read-only paths). |
| Read/modify other jobs | Each job gets its own directory; siblings and the parent are not mounted. |
| Exfiltrate over the network | --unshare-all (no interfaces). Network is opt-in. |
| Steal secrets from environment | --clearenv; explicit allow-list only. |
| Runaway CPU/time | Wall-clock deadline (SIGKILL) and prlimit --cpu. |
| Disk/FD exhaustion, output flood | prlimit --fsize/--nofile; stdout/stderr caps. |
| Core dumps leaking memory | prlimit --core=0. |
| Orphaned processes | PID namespace, --die-with-parent, --new-session. |
| Symlink tricks | Only the workspace is mounted, so links to host paths dangle. Outputs are read via resolveOutput() which refuses symlinks/escapes. cleanup() never follows links. |
| Silent downgrade | Missing/unusable Bubblewrap throws; there is no unsandboxed path. |
| Shell injection | argv array, no shell. |
Limitations
- The PHP process that calls the runner is not sandboxed.
- Kernel, Bubblewrap and user-namespace vulnerabilities are out of reach of this library.
prlimitis optional by default; without it only the deadline and output cap apply (withRequiredResourceLimits()fails closed instead).- Memory is not capped by default.
- Read-only paths you add (for example
withSystemLibraries()) are visible to the process. - The library does not inspect document content or outputs. Treat produced files as untrusted.
- Seccomp filtering is not applied by default.
Run bin/pdfx-sandbox-check on every host you deploy to; user-namespace policy differs between distributions.