Skip to content

Security: beleon/pdfed

Security

SECURITY.md

Security policy

pdfed opens files that arrive from other people. This page says what is defended, what is not, and where to send a problem.

Reporting

Open a private security advisory on GitHub. If that is not available to you, open a normal issue on either host (GitHub, Codefloe) saying only that you have a security report and how to reach you. Keep the details out of the public issue.

This is a personal project maintained by one person. There is no guaranteed response time and no bounty. Reports are read and acted on, but treat that as a best effort rather than a commitment.

What is in scope

  • A crafted PDF that causes code execution, file disclosure, or unbounded resource use.
  • A crafted sidecar (<name>.pdf.pdfed.json) that does the same. Sidecars are loaded automatically when the PDF beside them is opened, so they are untrusted input even though they are usually written by the app itself.
  • An exported PDF that leaks content the app showed as removed — a redaction that survives extraction, or cropped-away pixels recoverable from the exported file.
  • The packaging: anything in the deb, rpm or Flatpak that lets a local file or environment variable change what the app executes.

Out of scope

These are known and documented. A report about them will be closed unless it shows something worse than described.

  • Un-redacted content remains outside the export. Redaction changes only the exported file. The content you painted over is still in:

    • the opened PDF, for a page that came from it. The sidecar holds only the recipe;
    • the sidecar, for a page made from an image (Insert images, or a pasted clipboard image). It holds the full original image bytes, which is what makes cropping reversible;
    • the other PDF, for a page inserted from one. The sidecar only references it by path;
    • the sidecar, for any image object you placed on any page, as the annotation's image_b64;
    • the sidecar's set-aside copies (.bak-<stamp>-<hex>, .discarded-<stamp>-<hex>) beside it, and the fallback location ~/.local/share/pdfed/sidecars/ used when the document's folder is not writable. An ordinary undo back to the original state, or one "start over", creates these.

    Cropping applies to image objects, not pages, and the sidecar keeps the full image behind every crop for as long as you are editing.

    The user-facing statement of this, repeated verbatim on every shipping surface:

    Your original PDF is never modified, so it still holds whatever you painted over. For a page you made from an image, your edits file holds it too, and for a page you inserted from another PDF it stays in that PDF. Send the export, and treat every file it was built from as you would the original.

  • The log can contain document details. Unhandled errors are written to pdfed.log in the app's data directory (Help ▸ Open the log folder), and a traceback can carry file paths, and occasionally text from the document that triggered it. It is local and never sent anywhere, and it is created mode 0600 so other accounts on a shared machine cannot read it (an existing log keeps whatever mode you gave it) — but read it before pasting it into a public issue.

  • Active content is stripped through an allowlist. On export the raw /Annots array of every page is walked: link actions and annotation /A and /AA dictionaries are removed, the document's /AcroForm is dropped, and an annotation whose /Subtype is not literally on a short allowlist is deleted from the page. An unrecognised or missing subtype is dropped, not kept.

    Form fields. Pages are imported with widgets=0, so no widget annotation is copied in and the field tree is never re-attached. Since a field does not have to carry the /Widget subtype, the field-identity keys (/FT /T /TU /V /DV, and /Parent where present) are also nulled on every annotation that survives.

    What protects you is that list of subtype names plus the field-key strip. An annotation whose subtype is on the allowlist keeps its other content, including /RC rich text.

  • Rasterizing a page draws whatever is on it, including a form's filled-in values. The form field object is removed from every export, but Page > Rasterize renders the page with its annotations drawn, so a filled field's visible text becomes pixels like anything else on the page.

  • A hostile document can make the app slow. Resource exhaustion that ends in a hang or an out-of-memory kill, with no data loss and no disclosure, is a bug rather than a vulnerability here.

  • No sandbox is claimed for the deb, rpm or source install. The Flatpak runs in the Flatpak sandbox with --filesystem=home, plus /media, /mnt, /run/media and read-only xdg-run/gvfs, because removable media and network mounts are where a document you have been asked to sign usually lives.

  • Concurrent use is not supported. Two windows open on one PDF will refuse to overwrite each other rather than merging: the second window notices that the edits changed on disk and asks, offering to save anyway, which moves the other window's copy aside next to it rather than overwriting it. Nothing merges the two, so the losing window's later edits are only in its export.

Dependencies

The PDF parsing that most matters — MuPDF, via PyMuPDF — is a third-party component with its own advisory history. The packages bundle it rather than using the distribution's copy, which means a fix requires a new release here rather than a system update. Dependency bumps are shipped as ordinary releases.

There aren't any published security advisories