Release note · August 6, 2026
A PDF action can keep its type and still change behavior
A PDF review can correctly say that an action is still a URI, sound, form, or RichMedia action and still miss what that action will ask a viewer to do. The action subtype is only part of its stored behavior. A same-type rewrite can change a destination selector, a coordinate-appending flag, a selected field set, a transition, a command, or another behavior-bearing member while a public type count stays fixed.
PDFFence 1.13.0 adds a bounded, private comparison of selected standard action fields. It turns those same-inventory rewrites into the generic active_content_payload_changed result and the existing PFP001 policy finding, without placing an action value or digest in a normal report.
Count the type; retain the behavior
The release covers direct document-open Thread, URI, Sound, Movie, Hide, Named, SubmitForm, ResetForm, Rendition, Trans, and RichMediaExecute actions. The selected fields include the Thread destination, URI IsMap flag, sound stream, movie and Hide selectors, named viewer command, form character set and fields, rendition JavaScript fallback, transition dictionary, and RichMedia command.
This is deliberately a narrow stored-data boundary, not a claim about a particular viewer. PDFFence never renders the file, runs an action, follows a URI, opens an attachment, or predicts whether a viewer permits a command. It records a static difference that deserves review.
Raw evidence without payload execution
Top-level JavaScript and Sound streams are compared as raw stored bytes plus bounded raw Filter and DecodeParms representation. PDFFence does not call a payload decoder to obtain that evidence. Nested streams remain outside this behavior-field coverage, which keeps the boundary explicit instead of treating arbitrary recursive data as action semantics.
Public output remains generic across JSON, Markdown, and SARIF: it contains categories, counts, and the policy finding, not a script, URI, command, field name, stream bytes, or private fingerprint. The precise policy and threat-model boundaries are part of the tagged policy reference and threat model.
Make the claim scoreable
The companion PDF Change Assurance Benchmark 1.13.0 now contains 128 deterministic paired PDFs. Eleven new pairs hold action type and public inventory fixed while changing exactly one selected behavior field. PDFFence 1.13.0 passes all 128 pairs through its public CLI; the held 1.12.0 source candidate passes the prior 117 and misses exactly the eleven new rewrites.
The release passed 196 tests, Ruff, fixed-timestamp reproducible wheel and source-archive builds, Twine metadata checks, and fresh wheel installs on Python 3.12 and 3.13 plus a Python 3.12 source-archive install. Each clean install verified and scored the full 128-pair benchmark. The validation record states the exact scope and evidence.
Use the release
python -m pip install https://github.com/SybilGambleyyu/pdffence/releases/download/v1.13.0/pdffence-1.13.0-py3-none-any.whl
pdffence init pdffence.yml
pdffence check before.pdf after.pdf --policy pdffence.yml --format sarif
PDFFence is MIT-licensed and available on GitHub. It is a local, static review gate—not a renderer, malware sandbox, signature validator, or proof that two PDFs are safe or visually identical.