docs/examples/file-processing/libreoffice-pdf.mdx
<Tooltip tip="This workflow prepares and restores a local disk snapshot, which is not available on microsandbox cloud."><span className="msb-badge-local">Local-only <Icon icon="circle-info" size={11} /></span></Tooltip>
Document parsers process complex, attacker-controlled file formats. This example installs LibreOffice and Poppler once, snapshots the toolchain, and performs each conversion in a fresh worker with networking disabled.
Create one script to prepare the reusable toolchain:
#!/bin/sh
set -eu
apt-get update
DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
libreoffice-writer poppler-utils
rm -rf /var/lib/apt/lists/*
useradd --system --create-home --shell /usr/sbin/nologin converter
mkdir -p /input /out
chown converter:converter /out
chmod 0555 /input
chmod 0700 /out
libreoffice --version
pdftoppm -v
Create the workload script that each worker will run:
#!/bin/sh
set -eu
test "$(id -u)" -ne 0
if (printf x >> /input/input.docx) 2>/dev/null; then
echo "input unexpectedly writable" >&2
exit 1
fi
if getent ahosts example.com >/dev/null 2>&1; then
echo "network unexpectedly reachable" >&2
exit 1
fi
echo "input read-only; network blocked"
libreoffice --headless --convert-to pdf --outdir /out /input/input.docx
pdftoppm -png -singlefile -r 96 /out/input.pdf /out/preview
test -s /out/input.pdf
test -s /out/preview.png
stat -c "%n: %s bytes" /out/input.pdf /out/preview.png
Install both scripts in the guest, then run the preparation script as the entrypoint:
<CodeGroup> ```sh macOS & Linux msb run --name office-base --replace \ --cpus 2 --memory 2G --root-disk 5G \ --script-path prepare-office:./prepare-office.sh \ --script-path convert-document:./convert-document.sh \ --entrypoint prepare-office \ debian:bookworm-slim ```msb run --name office-base --replace `
--cpus 2 --memory 2G --root-disk 5G `
--script-path prepare-office:./prepare-office.sh `
--script-path convert-document:./convert-document.sh `
--entrypoint prepare-office `
debian:bookworm-slim
Capture the prepared toolchain:
msb snapshot create office-tools --from office-base --integrity --force
Verify the snapshot before using it:
msb snapshot verify office-tools
Bookworm currently downloads roughly 130 MB and adds about 440 MB to the guest for this toolchain; package updates can change those numbers. The snapshot lets later workers skip that installation.
</Step> <Step title="Start an offline worker">The example accepts a Word document named input.docx:
msb run -d --name document-worker --replace `
--from-snapshot office-tools `
--cpus 2 --memory 2G `
--user converter --security restricted `
--no-net --max-duration 2m `
-- sh -lc 'exec sleep 2m'
Copy the document into the running worker:
msb cp ./input.docx document-worker:/input/input.docx
Make the copied input read-only:
msb exec --user root document-worker -- chmod 0444 /input/input.docx
Run the bounded conversion as the unprivileged converter user:
msb exec --user converter --timeout 90s `
--rlimit nproc=128 --rlimit nofile=512 --rlimit fsize=536870912 `
document-worker -- convert-document
The worker boots before the document is copied because rootfs patches cannot be combined with --from-snapshot. The root-owned input directory prevents the unprivileged converter from replacing the document, while its home directory and /out hold the only workload-writable state. The untrusted conversion command sets its own process, file-descriptor, and per-file limits.
Prepare a fresh artifact directory on the host:
<CodeGroup> ```sh macOS & Linux export OFFICE_ARTIFACT_DIR="${OFFICE_ARTIFACT_DIR:-$PWD/.office-artifacts}"if [ -e "$OFFICE_ARTIFACT_DIR" ] || [ -L "$OFFICE_ARTIFACT_DIR" ]; then echo "artifact path already exists: $OFFICE_ARTIFACT_DIR" >&2 exit 1 fi
mkdir -m 700 "$OFFICE_ARTIFACT_DIR"
```powershell Windows
if (-not $env:OFFICE_ARTIFACT_DIR) {
$env:OFFICE_ARTIFACT_DIR = Join-Path $PWD '.office-artifacts'
}
if (Test-Path -LiteralPath $env:OFFICE_ARTIFACT_DIR) {
throw "artifact path already exists: $env:OFFICE_ARTIFACT_DIR"
}
New-Item -ItemType Directory -Path $env:OFFICE_ARTIFACT_DIR | Out-Null
Stop the worker before exporting its output:
msb stop document-worker
Copy out only the generated artifacts:
<CodeGroup> ```sh macOS & Linux msb cp document-worker:/out/input.pdf "$OFFICE_ARTIFACT_DIR/output.pdf" msb cp document-worker:/out/preview.png "$OFFICE_ARTIFACT_DIR/preview.png" ```$pdfPath = Join-Path $env:OFFICE_ARTIFACT_DIR 'output.pdf'
$previewPath = Join-Path $env:OFFICE_ARTIFACT_DIR 'preview.png'
msb cp document-worker:/out/input.pdf $pdfPath
msb cp document-worker:/out/preview.png $previewPath
Validate both exported files:
<CodeGroup> ```sh macOS & Linux for artifact in output.pdf preview.png; do test ! -L "$OFFICE_ARTIFACT_DIR/$artifact" && test -f "$OFFICE_ARTIFACT_DIR/$artifact" && test -s "$OFFICE_ARTIFACT_DIR/$artifact" && test "$(wc -c < "$OFFICE_ARTIFACT_DIR/$artifact")" -le 536870912 done ```foreach ($artifactPath in $pdfPath, $previewPath) {
$artifact = Get-Item -LiteralPath $artifactPath
$isLink = ($artifact.Attributes -band [IO.FileAttributes]::ReparsePoint) -ne 0
if ($artifact.PSIsContainer -or $isLink -or $artifact.Length -eq 0 -or $artifact.Length -gt 512MB) {
throw "$artifactPath failed artifact validation"
}
}
Stopping the worker prevents artifact races. The fresh private directory and checks require two nonempty regular files no larger than the conversion command's 512 MiB per-file limit.
</Step> <Step title="Clean up">Remove the prepared sandbox and worker:
<CodeGroup> ```sh macOS & Linux msb rm -f office-base document-worker rm -f prepare-office.sh convert-document.sh ```msb rm -f office-base document-worker
Remove-Item prepare-office.sh, convert-document.sh
Remove the reusable toolchain snapshot:
msb snapshot rm office-tools
The checked output.pdf and preview.png remain in the artifact directory configured above.