Pulp Engine — Security Policy
We take security seriously. This document describes how to report a vulnerability, what we commit to in response, and what is in and out of scope.
Pulp Engine is self-hosted software: each deployment is operated by its owner. A vulnerability in Pulp Engine itself may expose data in every deployment, so we prefer to hear about security issues before they become public.
Reporting a vulnerability
Email: troy@tksolutions.co.nz (replace with your organisation’s contact in forks).
Please include:
- A description of the issue and the potential impact.
- Reproduction steps, a proof-of-concept, or a patch.
- The Pulp Engine version (commit SHA or release tag) you tested against.
- Whether you believe the issue is being exploited in the wild.
If you require encryption for transport, request our PGP public key in your initial message and we will reply with it.
Please do not post to a public forum, tweet, or otherwise disclose the issue until we have had a chance to respond and ship a fix.
What you can expect from us
| Stage | Target |
|---|---|
| Acknowledge receipt | Within 3 business days |
| Initial triage & severity assessment | Within 7 business days |
| Status update cadence during investigation | Every 14 days until resolved or closed |
| Fix window for critical issues (RCE, auth bypass, cross-tenant leak) | Aim for a patched release within 30 days |
| Fix window for other issues | Aim for a patched release within 90 days |
| Public disclosure | Coordinated — we will agree a date with the reporter, typically ≤ 90 days from triage or on release, whichever comes first |
We will credit reporters in the release notes unless they prefer to remain anonymous. We do not currently operate a paid bug-bounty programme.
Scope
In scope:
- The Pulp Engine API server (
apps/api). - The editor web app (
apps/editor). - The render workers and controllers (
apps/worker, Dockerfile.worker / Dockerfile.controller). - Official packages under
packages/(SDKs, CLI, plugin API, renderers). - Official Docker images published to GHCR.
Out of scope:
- Vulnerabilities in third-party dependencies that are already publicly disclosed (please file upstream; we will upgrade through normal dependency maintenance).
- Issues that require an attacker to already have admin credentials — by design, admin access is full trust.
- Missing rate limits on endpoints where rate limiting is explicitly opt-in and the operator chose not to enable it.
- Attacks requiring physical access to the operator’s infrastructure.
- Self-hosted instances operated by third parties — please report those to the operator.
- Denial-of-service via resource exhaustion of rendering capacity (operator concern; see render isolation modes).
- Issues in community or third-party plugins — report to the plugin maintainer. Plugins run as trusted server code; see plugin-trust-model.md for the operator posture.
Coordinated disclosure
We prefer coordinated disclosure. If a fix requires a coordinated release with downstream operators (e.g. a fix that changes the upgrade path), we may ask to extend the public-disclosure window. We will communicate the reason and a revised target.
Recognition
Reporters who follow this policy are acknowledged in the release notes for the fix. Previous advisories and their reporters are listed on the releases page.
Policy versioning
This policy is versioned with the repository. See .well-known/security.txt for the machine-readable summary consumed by automated scanners (RFC 9116).
PDF credentials and archival output
PDF security uses qpdf AES-256. Passwords travel to qpdf in a private response file, never process arguments; temporary input and argument files are removed in finally. The API’s default logger redacts literal passwords in render, batch, utility and schedule requests, and its request serializer omits bodies. Schedule responses mask stored literal credentials. Keep raw payloads and schedule storage protected: a password path avoids storing passwords in templates, but the source payload still contains the credential. Custom loggers and plugins must apply equivalent handling.
Plaintext PDF bytes briefly touch the process’s temporary directory. Set TMPDIR to a private tmpfs where this matters; on Windows restrict the service account’s temporary directory ACL. Permission flags are reader-enforced and advisory. Owner-password-only protection is not confidentiality protection. PDF/A forbids encryption; these settings cannot be combined.
Archival conversion is validated against the generated corpus, not arbitrary third-party PDFs. Plugins that modify PDF bytes must preserve Info/XMP, output intents and embedded fonts and validate their output independently. The metadata marker check alone does not establish PDF/A compliance.
Deployment-owned PDF signing keys
Keep PEM/P12 key files and passphrases in least-privilege secret mounts, or delegate signing to an asymmetric KMS key. Avoid inline environment secrets where mounted _FILE values are available. Private material stays in the API process and is excluded from render-worker environment allowlists. Certificate metadata and rendered hashes are public audit data; passphrases, key bytes, TSA credentials and document bytes are excluded from signing diagnostics.
Certificate tenant and template bindings limit render credentials to approved document families. POST /render/pdf/sign is admin-only and additionally requires explicit allowUtility: true; enabling it delegates the ability to seal arbitrary uploaded bytes. Grant that capability deliberately. Operator-selected timestamp URLs may reach internal infrastructure; they are deployment configuration, never request data. Trusted TSA responses must match the nonce and signature-value imprint and pass cryptographic verification. Signing errors fail closed. This implementation provides baseline seals, not qualified eIDAS key custody or long-term revocation evidence.