Get started
CVE-2026-42593: Gotenberg watermark and stamp routes leak arbitrary PDFs

CVE-2026-42593: Gotenberg watermark and stamp routes leak arbitrary PDFs

CVE-2026-42593 is an unauthenticated arbitrary PDF read in Gotenberg 8.31.0 and earlier, exposed by stampExpression and watermarkExpression on six conversion routes. Self-hosters affected, managed APIs unaffected.

11 min read

CVE-2026-42593 is an unauthenticated arbitrary PDF read in Gotenberg, the popular open-source Docker-based document conversion API. Six conversion routes accept a stampExpression or watermarkExpression parameter pointing at any filesystem path the Gotenberg process can read. When no companion file is uploaded, pdfcpu opens the supplied path and composites its pages onto the response, returning the target file's contents to the caller. All versions up to and including 8.31.0 are affected, and the GitLab advisory lists no fixed version at the time of writing. Self-hosters running Gotenberg behind a public reverse proxy should treat this as an active data-leak vector this week. Managed PDF APIs that do not run Gotenberg, including PDF4.dev, are not affected. This guide explains the bug, who is on the blast radius, the routes to block today, and how to detect prior exploitation in container logs.

What CVE-2026-42593 is

CVE-2026-42593 is a path-traversal-class bug in Gotenberg's stamp and watermark composition logic, reachable on six conversion routes without authentication. NVD has scored it CVSS 5.3 on vector CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:N/A:N per the GitLab advisory record. The CWE classification is dual: CWE-22 for the path-traversal primitive and CWE-73 for the external control of a filename in a downstream library call.

The mechanics are straightforward. Gotenberg exposes stampSource and watermarkSource parameters on a number of conversion routes. When the source is pdf or image, the API expects the client to upload a file in the same multipart request. Gotenberg's intended behavior is to rewrite stampExpression (or watermarkExpression) to the on-disk path of the uploaded file. The bug is that this rewrite only happens when a matching file is actually present in the multipart body. If the client sends stampSource=pdf and stampExpression=/etc/secrets/internal.pdf and NO file part, the rewrite never fires. The original attacker-controlled path falls through to pdfcpu unchanged.

pdfcpu then opens that path, reads it as a PDF, and composites its pages onto the response document. The response body is a fresh PDF that contains the pages of the file the attacker requested. Any file readable by the Gotenberg process under the container filesystem is in scope, with the practical limit being that the file must be a valid PDF for pdfcpu to compose pages from it. The advisory was published in early May 2026 alongside a batch of other Gotenberg findings, including the unauthenticated ExifTool RCE in CVE-2026-42589 and the Chromium /tmp file read in CVE-2026-42597.

If your Gotenberg deployment accepts traffic from anywhere outside a fully trusted network, treat every container at 8.31.0 or earlier as an active data-leak vector. The exploit requires no authentication, no special HTTP headers, no second round-trip, and leaves nothing in the request body except a path string and two parameter names.

Who is affected

The blast radius is the Gotenberg HTTP API itself, not anything downstream. The six vulnerable routes appear in the table below. Routes outside the list are not exposed to this bug, though several have their own findings in the same disclosure batch.

RouteVulnerable to CVE-2026-42593?Notes
POST /forms/pdfengines/mergeYesStamp and watermark expressions both accepted
POST /forms/pdfengines/splitYesSame parameter surface
POST /forms/libreoffice/convertYesStamp and watermark applied after LibreOffice conversion
POST /forms/chromium/convert/urlYesStamp and watermark applied after Chromium render
POST /forms/chromium/convert/htmlYesSame
POST /forms/chromium/convert/markdownYesSame
POST /forms/chromium/screenshot/*NoScreenshot routes do not run pdfcpu stamping
POST /forms/pdfengines/convertNoConversion-only route with no stamp parameter
POST /forms/libreoffice/convert health check, metrics, version endpointsNoNo file-handling primitive

The exposed parameter pair is the same across all six: stampSource=pdf plus stampExpression=..., or watermarkSource=pdf plus watermarkExpression=.... Both halves of the bug pair are equivalent in impact, so a defender filtering at the proxy needs to handle both parameter names.

Anything behind the API matters too. A Gotenberg container running as root reads everything under /. A container running as a low-privileged user reads only files that user owns or that have world-read permission. The default Gotenberg image runs as user gotenberg (UID 1001), which limits but does not eliminate the exposure: temporary files written by the same Gotenberg process, prior conversion outputs that are still on disk, and any file mounted into the container with permissive modes are all reachable. Operators frequently mount config and credential files into containers without thinking about file modes, and those files are exactly what a path-traversal primitive reads first.

Patching guide

At the time of writing, May 12, 2026, no patched Gotenberg release has been published. The GitLab advisory lists "all versions up to 8.31.0" as vulnerable with no fixed range. Watch the Gotenberg releases page for the patched version, and the security advisory index for the official write-up.

When the patched tag ships, the upgrade flow on a Docker deployment is the standard one:

# Stop and remove the running container
docker stop gotenberg
docker rm gotenberg
 
# Pull the patched image (replace 8.x.y with the fixed version once published)
docker pull gotenberg/gotenberg:8.x.y
 
# Restart with your existing flags
docker run -d --name gotenberg \
  -p 3000:3000 \
  --restart unless-stopped \
  gotenberg/gotenberg:8.x.y

Verify the running version through the health endpoint before reopening traffic:

curl -s http://localhost:3000/version

The expected output is a JSON object with a version field at or above the fixed release. If you run Gotenberg through Helm or Docker Compose, bump the image tag in the values file or compose manifest, then kubectl rollout restart deployment/gotenberg or docker compose up -d --force-recreate. Pin a specific patched tag rather than tracking :8 or :latest: a floating tag has bitten plenty of operators after a regression in a follow-up release.

Mitigations if you cannot patch today

The exploit chain has three breakable links. Cut any one of them and the bug stops firing. Cut all three and the bug stops firing AND the deployment is hardened against the next variant.

Filter stampExpression and watermarkExpression at the proxy. A reverse proxy in front of Gotenberg can strip these parameters from inbound multipart bodies unless a matching file part is present. An nginx snippet using ngx_http_lua_module does the job in twenty lines. The simpler approach for operators without Lua is to reject any inbound request that contains either parameter name in the body, regardless of file part presence, and accept the loss of legitimate stamp and watermark functionality until the patch lands. Stamping and watermarking through Gotenberg is a feature few production pipelines call from anonymous clients.

Kill the affected routes outright. If your workload does not need stamp or watermark composition, block the six routes at the proxy. A Caddy snippet:

gotenberg.internal.example.com {
    @blocked {
        path /forms/pdfengines/merge
        path /forms/pdfengines/split
        path /forms/libreoffice/convert
        path /forms/chromium/convert/url
        path /forms/chromium/convert/html
        path /forms/chromium/convert/markdown
    }
    handle @blocked {
        respond "Blocked pending CVE-2026-42593 patch" 403
    }
    reverse_proxy gotenberg:3000
}

The trade-off is that legitimate calls to those routes also fail. For pipelines that only convert and never stamp, that is the right trade. For pipelines that stamp from trusted internal callers, allow them by source IP at the proxy and block everyone else.

Authenticate the Gotenberg API. Gotenberg ships without authentication by default. The advisory traffic, including CVE-2026-42593, depends on anonymous access. Add basic auth, mutual TLS, or an API key gate at the reverse proxy, and the unauthenticated exploit primitive disappears. Combine with the network controls above, since a leaked credential then rolls the exposure back to the same shape.

# Quick sanity check: confirm your proxy refuses an unauthenticated stamp attempt
curl -i -X POST https://gotenberg.internal.example.com/forms/pdfengines/merge \
  -F 'stampSource=pdf' \
  -F 'stampExpression=/etc/hosts.pdf'
# Expect: 401, 403, or 404. Anything 2xx is a finding.

Why managed PDF APIs avoid this class of bug

The root cause of CVE-2026-42593 is an architectural decision: the watermarking parameters accept either an uploaded file or a server-side filesystem path, and the path is interpolated into a downstream library call. Take either half of that design away and the bug cannot exist.

Managed PDF generation APIs typically do not expose a server-side filesystem path on the public surface. Watermarking and stamping are handled through a defined input schema, where the client either uploads a file or names an entity that the API owner manages, never a path string. PDF4.dev follows that pattern: watermark and stamp logic runs through declared template fields, not through filesystem paths the client supplies, so there is no place for a path-traversal primitive to take hold on the API surface. The Gotenberg model is more flexible, which is why self-hosters chose it, and that flexibility is the same property that creates this CVE.

The architectural lesson generalizes. Any time a public API accepts a parameter that is "either upload a file or name where it lives on disk," the safe design is to remove the disk-path branch and require the upload. If the disk-path branch has to stay, the safe design is to namespace it under an authenticated administrative endpoint, never on the same route that handles anonymous traffic. CVE-2026-42593 is what happens when the two branches share a single anonymous route.

Detection: are you already exploited?

Gotenberg writes structured logs to stdout by default. The signal patterns to look for in container log retention covering March through May 2026:

  • POST requests to the six affected routes that contain stampExpression or watermarkExpression in the request body, with no corresponding multipart file part. The Gotenberg request logger emits the route, status, and duration; the body itself is not logged unless LOG_LEVEL=debug is set. Detecting prior exploitation may require correlating proxy logs that captured the multipart fields.
  • Response sizes on the six routes that are anomalous compared to the workload's normal output. A burst of small PDF responses (single-page composites of a target file) against a baseline of large multi-page conversions is a useful tell.
  • Outbound conversion responses to first-time-seen client IPs or to client IPs that have not previously called any other Gotenberg route. Exploration traffic for this bug typically calls one of the six routes once or twice from an IP that never touches conversion routes legitimately.
  • Filesystem reads under /tmp, /etc, /var, or any mounted secret path during the same time window the suspicious requests occurred. Auditd or eBPF-based file-access tracing surfaces these.

A simple grep over reverse-proxy access logs catches most cases:

# Replace path to access log as appropriate
grep -E 'stampExpression|watermarkExpression' /var/log/nginx/access.log \
  | grep -vE '\.pdf"|filename='

The second grep excludes legitimate calls that did upload a file. Anything that remains is a candidate for review.

If your workload does emit stampExpression from trusted internal callers, audit those callers' allowlist to confirm none of them sourced a path from external input. The bug is exploitable through any trusted middleware that passes a user-controlled path through to Gotenberg unfiltered, even if the middleware itself is authenticated.

Timeline of disclosure

DateEvent
Early May 2026Reporter discloses CVE-2026-42593 and adjacent findings to the Gotenberg maintainers
May 6, 2026 (approx.)GitLab advisory published for CVE-2026-42593, listing affected range "up to 8.31.0" and no fixed version (GitLab advisory)
May 6, 2026 (approx.)Companion advisories published: CVE-2026-42589 (ExifTool RCE), CVE-2026-42590, CVE-2026-42591, CVE-2026-42592, CVE-2026-42594, CVE-2026-42596, CVE-2026-42597
May 12, 2026This article published; no patched Gotenberg release available yet
PendingUpstream Gotenberg release with fix

The batch shape, eight CVEs disclosed on the same day against the same project, is typical of a coordinated audit. Operators should plan for one upgrade that addresses the full batch rather than chasing each CVE separately. The advisory pages will cross-link to the fixed version once it ships.

What we are watching next

Three follow-on questions are worth tracking through May and June 2026.

Will the upstream patch ship before mass scanning starts? The eight-CVE disclosure batch is high-signal for opportunistic scanners. Internet-wide scanning for /forms/pdfengines/merge traffic with anomalous parameters is the obvious next step for botnet operators looking for low-hanging file-read primitives. Operators who cannot patch should prioritize the proxy mitigations above.

Will downstream wrappers carry the bug? Several PDF-conversion-as-a-service products are built on Gotenberg. If those vendors did not strip stampExpression and watermarkExpression from their public API surface, their customers inherit the bug. Vendors should publish a brief statement on whether their hosted product is affected. PDF4.dev is not built on Gotenberg and is not affected.

Will pdfcpu add a defense-in-depth path filter? The proximate cause is Gotenberg's missing sanitizer, but the secondary fix would be pdfcpu refusing to open paths outside an allowlist when called from a multipart-bound context. A library-level guardrail would make the next Gotenberg-shaped wrapper safer by default.

For developers shipping PDF pipelines, the durable move is the architectural one: drop server-side filesystem paths from the public API surface, require uploads for all client-supplied source files, and authenticate every route. Each of those is independently cheap, and together they take this entire CVE class off the table.

Free tools mentioned:

Watermark PdfTry it freeRedact PdfTry it free

Start generating PDFs

Build PDF templates with a visual editor. Render them via API from any language in ~300ms.