Security model

Who is trusted, what is not, and what you must add around PDF Server to run it safely.

PDF Server executes templated HTML in a real browser to produce PDFs. That makes the browser the security-critical component and the request data the primary untrusted input. This page defines the trust boundaries; the specific issues and fixes are in Proved issues and Possible issues.

Trust boundaries

Actor / inputTrustRationale
Template authorTrustedYou write the templates, schemas, compositions (composition.js/jq), and remote-request templates, and you build the image. Author-supplied code is trusted.
Render-request dataOften untrustedIn many deployments the JSON body carries end-user input (a customer name/address on an invoice). Treat it as attacker-controlled unless you know otherwise.
The headless browserWeak boundaryIt executes whatever HTML/CSS/JS the render produces. It runs with --no-sandbox, so a renderer compromise lands as the container user.
OperatorResponsibleThe service ships no auth, egress control, or browser sandbox — you provide them.

Two common deployment shapes

  • Untrusted data (e.g. public-facing invoices): the request body contains end-user input. All data-driven issues below are in scope. Harden accordingly.
  • Internal data only (e.g. reports from your own systems): the body is trusted. Data-driven issues (XSS-into-PDF, URL SSRF from data) are largely not reachable, but network exposure and DoS considerations still apply.

Knowing which shape you are in tells you which issues to prioritise — each issue in the proved and possible lists is tagged with when it is reachable.

What the service does not provide

By design, PDF Server ships no:

  • authentication or authorization on any endpoint;
  • request body-size limit or HTTP read/write timeouts;
  • outbound network (egress) restriction;
  • browser OS sandbox (it runs --no-sandbox).

Safety therefore depends on operator-side controls. See the hardening checklist.

Operator responsibilities (summary)

  1. Put an authenticating reverse proxy in front of the service; set body-size and timeout limits there.
  2. Apply an egress network policy blocking private and link-local ranges (e.g. 169.254.169.254, RFC-1918).
  3. Run the container sandboxed (seccomp, and ideally user namespaces / gVisor) to compensate for --no-sandbox, with CPU/memory limits.
  4. Never feed untrusted data to raw-HTML sinks ({{{triple}}}, the markdown/embed_* helpers) or into URL/src/href positions without validation.
  5. Do not build remote-template requests from untrusted data.

Reporting

Please report security issues privately, not in a public issue. Open a confidential issue on the project’s GitLab (tick “This issue is confidential”) so the report stays visible only to the maintainers until a fix is available.