Proved security issues

Verified in the source. Each issue is tagged with when it is reachable so you can prioritise for your deployment.

Reachability legend — U = reachable when render data is untrusted · I = reachable even with internal-only data · N = reachable when network-exposed without an auth proxy. Not every issue applies to every deployment; see the security model.

These are findings for the maintainers and operators to prioritise. Several are latent in a given deployment (e.g. they require untrusted data, which some installations never pass). They are documented honestly rather than assumed exploitable everywhere.

Critical

SSRF + header injection via remote templates — U

A remote template renders a raw HTTP request from template data and executes it with an HTTP client that has no timeout and no SSRF guard. If any part of the URL/host/headers comes from untrusted data, a caller can make the server fetch internal endpoints (e.g. cloud metadata at 169.254.169.254), and CR/LF in a value can inject headers or override Host.

  • Reachable: only if you deploy a remote template whose request is built from untrusted data. Remote templates fed only trusted/fixed data are not affected.
  • Fix: parse and allowlist scheme + host; block private/link-local ranges via a custom dialer; add a client timeout, header-size and response-size caps; reject CR/LF in interpolated values. Consider gating the remote loader behind an explicit opt-in flag.

High

Browser runs with --no-sandboxU I

The default browser args include --no-sandbox. A renderer exploit (or a chained XSS, below) then executes as the container user.

  • Reachable: the sandbox is off regardless of data trust; exploitation requires a browser bug or an XSS sink.
  • Fix / mitigation: run the container under seccomp + user namespaces (or gVisor), with an egress policy, so an escape is contained. Document --no-sandbox as requiring an external sandbox.

No authentication on any endpoint — N

No route requires auth. Anyone who can reach the port can render any template, drive the remote-template fetch, and consume resources.

  • Fix: deploy behind an authenticating reverse proxy (or add optional API-key/mTLS middleware). See the hardening checklist.

XSS-into-PDF via raw-HTML sinks — U

The markdown helper returns unescaped HTML and does not strip embedded HTML; embed_text and {{{triple}}} also emit raw HTML. Untrusted data reaching these executes as active content in the (unsandboxed) browser.

  • Reachable: only where a template passes untrusted data into one of these sinks. Auto-escaping ({{ }}, Go html/template) otherwise prevents injection.
  • Fix: sanitize untrusted markdown (e.g. bluemonday) or enable SkipHTML; document that these sinks must never receive untrusted data.

Medium

Predictable internal document IDs — U

The internal render server keys documents by an ID derived from a fixed-seed PRNG, so IDs are deterministic across restarts. Combined with any in-page JS (same-origin to the internal server), an attacker could predict IDs and read other renders’ HTML.

  • Fix: seed document IDs from crypto/rand.

DoS: no body limit, no server timeouts, single-slot default, hanging remote fetch, unbounded composition fan-out — N (U for the last two)

There is no request body-size limit and no HTTP read/write timeouts; the default pool is a single browser (so one slow render serializes all others); the remote-template client has no timeout; and composition planners are data-driven with no depth/breadth cap.

  • Fix: add a body-size limit and read/write/idle timeouts (at the proxy today, in-process ideally); raise the pool size; add a remote-fetch timeout; cap composition recursion/task count.

Composite sub-template schema bypass — U

Only the composition’s own schema.json is validated; each child template’s schema is not re-validated when reached through a composition, so data hardening a child relies on can be skipped.

  • Fix: validate each child task’s data against its template’s validator before rendering. Until then, validate at the composition boundary.

Priority order

  1. Remote-template SSRF (if remote templates use untrusted data).
  2. Sandbox + auth + egress (deploy-time controls).
  3. Raw-HTML XSS sinks (if untrusted data reaches them).
  4. Predictable document IDs.
  5. DoS controls (body limit, timeouts, pool size, fetch timeout, composition caps).
  6. Composite validation bypass.

See Possible issues for suspected items needing further verification, and the hardening checklist for the operator controls that mitigate several of the above at once.