<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: SybilGambleyyu</title>
    <description>The latest articles on DEV Community by SybilGambleyyu (@sybilgambleyyu).</description>
    <link>https://dev.to/sybilgambleyyu</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4038443%2Fb0ab471f-16c7-4071-ba32-ada903d2e634.png</url>
      <title>DEV Community: SybilGambleyyu</title>
      <link>https://dev.to/sybilgambleyyu</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/sybilgambleyyu"/>
    <language>en</language>
    <item>
      <title>A batch manifest must not double-count its sources</title>
      <dc:creator>SybilGambleyyu</dc:creator>
      <pubDate>Fri, 07 Aug 2026 12:34:13 +0000</pubDate>
      <link>https://dev.to/sybilgambleyyu/a-batch-manifest-must-not-double-count-its-sources-d4p</link>
      <guid>https://dev.to/sybilgambleyyu/a-batch-manifest-must-not-double-count-its-sources-d4p</guid>
      <description>&lt;p&gt;Liquid syntax error: 'raw' tag was never closed&lt;/p&gt;
</description>
      <category>pdf</category>
      <category>security</category>
      <category>opensource</category>
      <category>productivity</category>
    </item>
    <item>
      <title>A batch manifest should be generated, not hand-written</title>
      <dc:creator>SybilGambleyyu</dc:creator>
      <pubDate>Fri, 07 Aug 2026 12:15:57 +0000</pubDate>
      <link>https://dev.to/sybilgambleyyu/a-batch-manifest-should-be-generated-not-hand-written-118f</link>
      <guid>https://dev.to/sybilgambleyyu/a-batch-manifest-should-be-generated-not-hand-written-118f</guid>
      <description>&lt;p&gt;Liquid syntax error: 'raw' tag was never closed&lt;/p&gt;
</description>
      <category>pdf</category>
      <category>security</category>
      <category>opensource</category>
      <category>productivity</category>
    </item>
    <item>
      <title>A batch report should know less than its manifest</title>
      <dc:creator>SybilGambleyyu</dc:creator>
      <pubDate>Fri, 07 Aug 2026 11:52:20 +0000</pubDate>
      <link>https://dev.to/sybilgambleyyu/a-batch-report-should-know-less-than-its-manifest-413a</link>
      <guid>https://dev.to/sybilgambleyyu/a-batch-report-should-know-less-than-its-manifest-413a</guid>
      <description>&lt;p&gt;Liquid syntax error: 'raw' tag was never closed&lt;/p&gt;
</description>
      <category>pdf</category>
      <category>security</category>
      <category>opensource</category>
      <category>productivity</category>
    </item>
    <item>
      <title>A PDF's terminal footer is a review boundary</title>
      <dc:creator>SybilGambleyyu</dc:creator>
      <pubDate>Fri, 07 Aug 2026 11:17:46 +0000</pubDate>
      <link>https://dev.to/sybilgambleyyu/a-pdfs-terminal-footer-is-a-review-boundary-2obd</link>
      <guid>https://dev.to/sybilgambleyyu/a-pdfs-terminal-footer-is-a-review-boundary-2obd</guid>
      <description>&lt;h1&gt;
  
  
  A PDF's terminal footer is a review boundary
&lt;/h1&gt;

&lt;p&gt;A parser accepting a PDF is not the same as a review system having positive structural evidence about it. A parser can tolerate raw bytes after a final &lt;code&gt;%%EOF&lt;/code&gt; marker; those bytes are not automatically a valid PDF revision.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/SybilGambleyyu/pdffence/releases/tag/v1.24.0" rel="noopener noreferrer"&gt;PDFFence 1.24.0&lt;/a&gt; adds PFP015, &lt;code&gt;require_terminal_revision_footer&lt;/code&gt;: a privacy-safe, fail-closed policy for that condition. It is stored-layout review evidence, not PDF conformance or signature validation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Three redacted states
&lt;/h2&gt;

&lt;p&gt;The &lt;a href="https://pdf-issues.pdfa.org/32000-2-2020/clause07.html" rel="noopener noreferrer"&gt;PDF 2.0 syntax errata&lt;/a&gt; describe the last line of a file as &lt;code&gt;%%EOF&lt;/code&gt;. PDFFence follows a confirmed &lt;code&gt;/Prev&lt;/code&gt;-linked cross-reference chain and maps one bounded, unambiguous footer for each linked revision. It reports only:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;terminal&lt;/code&gt;: the map is established and the final linked footer ends the physical source apart from its optional EOL;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;nonterminal&lt;/code&gt;: the map is established, but bytes follow that footer; or&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;unavailable&lt;/code&gt;: the bounded map cannot be established.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The new &lt;code&gt;revision_terminal_footer_changed&lt;/code&gt; event carries only those labels—never an offset, footer location, object reference, source bytes, or hash.&lt;/p&gt;

&lt;h2&gt;
  
  
  Separate from PFP006
&lt;/h2&gt;

&lt;p&gt;PFP015 does not reject ordinary incremental history. A valid linked later update can have several revisions and still be terminal. Conversely, a one-revision PDF can have unlinked trailing bytes. That is why PFP015 is separate from &lt;code&gt;require_single_revision&lt;/code&gt; (PFP006).&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;
&lt;span class="na"&gt;rules&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;require_terminal_revision_footer&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;span class="s"&gt;~~~&lt;/span&gt;

&lt;span class="s"&gt;The check is independent of signatures and uses structural information available before an encrypted PDF is left uninspected; it never decrypts the source.&lt;/span&gt;

&lt;span class="c1"&gt;## Signature evidence remains narrower&lt;/span&gt;

&lt;span class="s"&gt;The [PDF 2.0 signature errata](https://pdf-issues.pdfa.org/32000-2-2020/clause12.html) describe a signature ByteRange ending at the footer of the update that added its signature dictionary. Existing PFP013 and PFP014 positive evidence consumes the same terminal footer map. PFP015 now makes that prerequisite independently visible for unsigned PDFs too.&lt;/span&gt;

&lt;span class="s"&gt;An older correctly bounded signature can pass after a valid later incremental update. An arbitrary tail after the final linked footer cannot keep earning own-revision credit merely because a tolerant parser accepts it.&lt;/span&gt;

&lt;span class="c1"&gt;## A 161-pair public contract&lt;/span&gt;

&lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;PDFCAB 1.24.0&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;&lt;span class="s"&gt;(https://github.com/SybilGambleyyu/pdf-change-benchmark/releases/tag/v1.24.0) adds an unsigned terminal-footer control. Its baseline is a normal one-revision PDF; only the candidate appends unlinked bytes after its final footer. Both revision counts stay confirmed at one, so the expected result is `revision_terminal_footer_changed`, stored-byte evidence, and PFP015—not PFP006.&lt;/span&gt;

&lt;span class="s"&gt;The existing semantic-signature tail control now expects that general event alongside its own-revision coverage event, while keeping PFP013 as its focused policy contract.&lt;/span&gt;

&lt;span class="s"&gt;PDFFence passed 264 tests, Ruff, and bytecode compilation; PDFCAB passed 91 tests and Ruff. The public process-bound score is 161/161. Both distributions built reproducibly, passed Twine checks, and passed fresh wheel installs on Python 3.12 and 3.13 plus a Python 3.12 source-archive install. Re-downloaded GitHub release wheels also scored 161/161 in a fresh Python 3.13 environment.&lt;/span&gt;

&lt;span class="s"&gt;PFP015 does not authenticate a revision, validate a signature or digest, inspect `/Contents`, validate certificates, trust, transforms, permissions, or PDF conformance, or predict viewer behavior. It does not decide whether an update is authorized, safe, or malicious.&lt;/span&gt;

&lt;span class="s"&gt;Read the canonical [PDFFence 1.24 release note](https://sybilgambleyyu.github.io/posts/pdffence-124.html) for the supported boundary, validation record, and release links.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

</description>
      <category>pdf</category>
      <category>security</category>
      <category>opensource</category>
      <category>productivity</category>
    </item>
    <item>
      <title>A signature revision footer must be the PDF's terminal footer</title>
      <dc:creator>SybilGambleyyu</dc:creator>
      <pubDate>Fri, 07 Aug 2026 10:40:40 +0000</pubDate>
      <link>https://dev.to/sybilgambleyyu/a-signature-revision-footer-must-be-the-pdfs-terminal-footer-554p</link>
      <guid>https://dev.to/sybilgambleyyu/a-signature-revision-footer-must-be-the-pdfs-terminal-footer-554p</guid>
      <description>&lt;h1&gt;
  
  
  A signature revision footer must be the PDF's terminal footer
&lt;/h1&gt;

&lt;p&gt;A historical PDF signature is not automatically stale after a later incremental update: its &lt;code&gt;/ByteRange&lt;/code&gt; should normally stop at the &lt;code&gt;%%EOF&lt;/code&gt; that closed its own revision. But that footer must itself be terminal. A tolerant parser ignoring arbitrary bytes after it is not evidence of a valid later PDF revision.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/SybilGambleyyu/pdffence/releases/tag/v1.23.0" rel="noopener noreferrer"&gt;PDFFence 1.23.0&lt;/a&gt; hardens existing PFP013 and PFP014 static signature-boundary evidence around that condition. It remains a review aid, not a signature validator.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://pdf-issues.pdfa.org/32000-2-2020/clause07.html" rel="noopener noreferrer"&gt;PDF 2.0 syntax errata&lt;/a&gt; say the last line of a file contains &lt;code&gt;%%EOF&lt;/code&gt;, while the &lt;a href="https://pdf-issues.pdfa.org/32000-2-2020/clause12.html" rel="noopener noreferrer"&gt;signature errata&lt;/a&gt; describe a multiple-signature ByteRange as ending at the footer of the incremental update that adds its signature dictionary, with a possible optional EOL. A valid later incremental update has linked cross-reference data and a terminal footer; unlinked raw tail bytes do not.&lt;/p&gt;

&lt;h2&gt;
  
  
  The tightened positive evidence
&lt;/h2&gt;

&lt;p&gt;PDFFence follows a confirmed &lt;code&gt;/Prev&lt;/code&gt;-linked xref chain for catalog-reachable AcroForm and catalog &lt;code&gt;/Perms&lt;/code&gt; signature roots. It identifies one bounded, unambiguous footer per linked revision, then requires the final linked footer to be at the physical end of the source (apart from its optional EOL) before a signature receives own-revision credit.&lt;/p&gt;

&lt;p&gt;This preserves normal history: an older correctly bounded signature can pass after a valid later update. It fails closed for unavailable, ambiguous, malformed, over-limit, or unlinked-tail evidence. PFP013 remains the rule for a well-formed ByteRange ending at its own revision; PFP014 additionally requires the conventional two-pair range to omit exactly its own direct hexadecimal &lt;code&gt;/Contents&lt;/code&gt; token.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;
&lt;span class="na"&gt;rules&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;require_signature_byte_range_own_revision_coverage&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
  &lt;span class="na"&gt;require_contents_bound_own_revision_signature_coverage&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;span class="s"&gt;~~~&lt;/span&gt;

&lt;span class="s"&gt;Reports retain aggregate counts and generic findings only—never offsets, object references, revision boundaries, footer locations, ranges, signature bytes, digests, certificates, transforms, or trust decisions.&lt;/span&gt;

&lt;span class="c1"&gt;## A focused terminal-footer control&lt;/span&gt;

&lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;PDFCAB 1.23.0&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;&lt;span class="s"&gt;(https://github.com/SybilGambleyyu/pdf-change-benchmark/releases/tag/v1.23.0) adds the 160th deterministic pair. Both sides begin with a semantic signature and a valid later incremental update, making each ByteRange historically bounded but not current-file coverage. The candidate alone appends unlinked bytes after the final PDF footer.&lt;/span&gt;

&lt;span class="s"&gt;Only `signature_own_revision_coverage_inventory_changed`, PFP013, and stored byte evidence are expected. PFP014 has no redundant separate event because the own-revision endpoint prerequisite changed. The held 1.22 release reports stored bytes alone for this pair.&lt;/span&gt;

&lt;span class="s"&gt;PDFFence passed 260 tests, Ruff, and bytecode compilation; PDFCAB passed 90 tests and Ruff. The public CLI score is 160/160. Both distributions built reproducibly, passed Twine checks, and passed fresh wheel installs on Python 3.12 and 3.13 plus a Python 3.12 source-archive install. Re-downloaded GitHub release wheels also scored 160/160 in a fresh Python 3.13 environment.&lt;/span&gt;

&lt;span class="s"&gt;This does not validate a digest, `/Contents`, certificate, transform, permission, or trust decision, and it does not decide whether an update is authentic, permitted, safe, or malicious.&lt;/span&gt;

&lt;span class="s"&gt;Read the canonical [PDFFence 1.23 release note](https://sybilgambleyyu.github.io/posts/pdffence-123.html) for the supported boundary, validation record, and release links.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

</description>
      <category>pdf</category>
      <category>security</category>
      <category>opensource</category>
      <category>productivity</category>
    </item>
    <item>
      <title>A PDF signature ByteRange must exclude its own Contents</title>
      <dc:creator>SybilGambleyyu</dc:creator>
      <pubDate>Fri, 07 Aug 2026 10:07:50 +0000</pubDate>
      <link>https://dev.to/sybilgambleyyu/a-pdf-signature-byterange-must-exclude-its-own-contents-19pp</link>
      <guid>https://dev.to/sybilgambleyyu/a-pdf-signature-byterange-must-exclude-its-own-contents-19pp</guid>
      <description>&lt;h1&gt;
  
  
  A PDF signature ByteRange must exclude its own Contents
&lt;/h1&gt;

&lt;p&gt;An older PDF signature is not automatically stale evidence. After a later&lt;br&gt;
incremental update, its &lt;code&gt;/ByteRange&lt;/code&gt; should normally stop at the revision that&lt;br&gt;
introduced it. But an old endpoint alone is not enough: the range should omit&lt;br&gt;
exactly that signature's own &lt;code&gt;/Contents&lt;/code&gt; value.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/SybilGambleyyu/pdffence/releases/tag/v1.22.0" rel="noopener noreferrer"&gt;PDFFence 1.22.0&lt;/a&gt;&lt;br&gt;
adds PFP014, &lt;code&gt;require_contents_bound_own_revision_signature_coverage&lt;/code&gt;, to&lt;br&gt;
make that stored-layout boundary reviewable without treating it as signature&lt;br&gt;
validation.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://pdf-issues.pdfa.org/32000-2-2020/clause12.html" rel="noopener noreferrer"&gt;PDF 2.0 signature errata&lt;/a&gt;&lt;br&gt;
describe a multi-signature ByteRange as ending at the &lt;code&gt;%%EOF&lt;/code&gt; that terminates&lt;br&gt;
the incremental update adding that signature dictionary, with a possible&lt;br&gt;
optional EOL. &lt;a href="https://datatracker.ietf.org/doc/html/rfc3778#section-5" rel="noopener noreferrer"&gt;RFC 3778&lt;/a&gt;&lt;br&gt;
describes the conventional two-pair range around the excluded signature value,&lt;br&gt;
whose &lt;code&gt;/Contents&lt;/code&gt; is normally hexadecimal.&lt;/p&gt;

&lt;h2&gt;
  
  
  The new review gate
&lt;/h2&gt;

&lt;p&gt;PFP014 requires every semantic signature dictionary with &lt;code&gt;/ByteRange&lt;/code&gt; to have&lt;br&gt;
all three pieces of bounded static evidence:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a well-formed direct two-pair range beginning at byte zero;&lt;/li&gt;
&lt;li&gt;one excluded span that matches its direct hexadecimal &lt;code&gt;/Contents&lt;/code&gt; token
exactly; and&lt;/li&gt;
&lt;li&gt;a final endpoint at the footer of that same signature's revision.&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;
&lt;span class="na"&gt;rules&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;require_contents_bound_own_revision_signature_coverage&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;span class="s"&gt;~~~&lt;/span&gt;

&lt;span class="s"&gt;PDFFence only evaluates catalog-reachable AcroForm and catalog `/Perms`&lt;/span&gt;
&lt;span class="s"&gt;signature roots. It follows a confirmed `/Prev`-linked xref chain and uses&lt;/span&gt;
&lt;span class="s"&gt;bounded raw and footer scans. Unavailable, ambiguous, malformed, indirect,&lt;/span&gt;
&lt;span class="s"&gt;compressed, or over-limit evidence fails closed. Reports contain aggregate&lt;/span&gt;
&lt;span class="s"&gt;counts only, never offsets, object references, revision boundaries, delimiter&lt;/span&gt;
&lt;span class="s"&gt;positions, signature bytes, digests, certificates, transforms, or trust data.&lt;/span&gt;

&lt;span class="s"&gt;This is intentionally distinct from PFP011 (current-file exact Contents&lt;/span&gt;
&lt;span class="s"&gt;coverage) and PFP013 (own-revision endpoint coverage). A correctly bounded&lt;/span&gt;
&lt;span class="s"&gt;older signature can pass PFP014 after a later incremental update. The new diff&lt;/span&gt;
&lt;span class="s"&gt;event appears only when the existing generic coverage and own-revision&lt;/span&gt;
&lt;span class="s"&gt;aggregates are stable, avoiding duplicate signals for changes already owned by&lt;/span&gt;
&lt;span class="s"&gt;PFP011 or PFP013.&lt;/span&gt;

&lt;span class="c1"&gt;## A focused 159th benchmark pair&lt;/span&gt;

&lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;PDFCAB 1.22.0&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;&lt;span class="s"&gt;(https://github.com/SybilGambleyyu/pdf-change-benchmark/releases/tag/v1.22.0)&lt;/span&gt;
&lt;span class="s"&gt;adds a pair where both signatures are historically stale relative to a later&lt;/span&gt;
&lt;span class="s"&gt;update but still reach their own signing-revision footers. The baseline's gap&lt;/span&gt;
&lt;span class="s"&gt;is exactly direct `/Contents`; the candidate starts the same gap one byte&lt;/span&gt;
&lt;span class="s"&gt;earlier. Only the historical Contents event and PFP014 are expected. Held&lt;/span&gt;
&lt;span class="s"&gt;PDFFence 1.21 reports changed bytes alone, proving the pair exercises the new&lt;/span&gt;
&lt;span class="s"&gt;boundary.&lt;/span&gt;

&lt;span class="s"&gt;PDFFence passed 256 tests and Ruff; PDFCAB passed 89 tests and Ruff. The public&lt;/span&gt;
&lt;span class="s"&gt;score is 159/159, builds are reproducible, and clean wheel/source-archive&lt;/span&gt;
&lt;span class="s"&gt;installs passed on Python 3.12 and 3.13. A pyHanko exercise also confirmed the&lt;/span&gt;
&lt;span class="s"&gt;stored layout for both an older signature after an update and a two-signature&lt;/span&gt;
&lt;span class="s"&gt;file.&lt;/span&gt;

&lt;span class="s"&gt;This does not validate a digest, `/Contents`, certificate, transform,&lt;/span&gt;
&lt;span class="s"&gt;permission, or trust decision, and it does not decide whether an update is&lt;/span&gt;
&lt;span class="s"&gt;authentic, permitted, safe, or malicious.&lt;/span&gt;

&lt;span class="s"&gt;Read the canonical [PDFFence 1.22 release note](https://sybilgambleyyu.github.io/posts/pdffence-122.html)&lt;/span&gt;
&lt;span class="s"&gt;for the supported boundary, validation record, and release links.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

</description>
      <category>pdf</category>
      <category>security</category>
      <category>opensource</category>
      <category>productivity</category>
    </item>
    <item>
      <title>A PDF signature ByteRange belongs to its own revision</title>
      <dc:creator>SybilGambleyyu</dc:creator>
      <pubDate>Fri, 07 Aug 2026 09:26:09 +0000</pubDate>
      <link>https://dev.to/sybilgambleyyu/a-pdf-signature-byterange-belongs-to-its-own-revision-578i</link>
      <guid>https://dev.to/sybilgambleyyu/a-pdf-signature-byterange-belongs-to-its-own-revision-578i</guid>
      <description>&lt;h1&gt;
  
  
  A PDF signature ByteRange belongs to its own revision
&lt;/h1&gt;

&lt;p&gt;PDF signatures are commonly added as incremental updates. That makes one&lt;br&gt;
distinction important in review: an earlier signature's &lt;code&gt;/ByteRange&lt;/code&gt; should end&lt;br&gt;
at the end of the revision that added that signature, not necessarily at the&lt;br&gt;
end of the current file.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/SybilGambleyyu/pdffence/releases/tag/v1.21.0" rel="noopener noreferrer"&gt;PDFFence 1.21.0&lt;/a&gt;&lt;br&gt;
adds PFP013, &lt;code&gt;require_signature_byte_range_own_revision_coverage&lt;/code&gt;, to make&lt;br&gt;
that static boundary reviewable. The &lt;a href="https://pdf-issues.pdfa.org/32000-2-2020/clause12.html" rel="noopener noreferrer"&gt;PDF 2.0 signature&lt;br&gt;
errata&lt;/a&gt; describe the&lt;br&gt;
multiple-signature range as ending at the &lt;code&gt;%%EOF&lt;/code&gt; that terminates the&lt;br&gt;
incremental update adding the signature dictionary, with a possible optional&lt;br&gt;
EOL.&lt;/p&gt;

&lt;h2&gt;
  
  
  A different question from current-file coverage
&lt;/h2&gt;

&lt;p&gt;Current-file coverage is useful when the latest stored file must be covered.&lt;br&gt;
But it classifies any older signature as non-current after a later incremental&lt;br&gt;
update. PFP013 instead asks whether each semantic signature's well-formed&lt;br&gt;
range ends at its own revision footer.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;
&lt;span class="na"&gt;rules&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;require_signature_byte_range_own_revision_coverage&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;span class="s"&gt;~~~&lt;/span&gt;

&lt;span class="s"&gt;PDFFence uses catalog-reachable AcroForm signature fields and catalog `/Perms`&lt;/span&gt;
&lt;span class="s"&gt;signature roots only. For an xref-addressable indirect signature dictionary, it&lt;/span&gt;
&lt;span class="s"&gt;follows a bounded confirmed `/Prev` chain and requires an unambiguous revision&lt;/span&gt;
&lt;span class="na"&gt;footer. It reports aggregate counts only&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;no object references, offsets,&lt;/span&gt;
&lt;span class="s"&gt;revision boundaries, signature bytes, digests, certificates, transforms, or&lt;/span&gt;
&lt;span class="s"&gt;trust decisions.&lt;/span&gt;

&lt;span class="s"&gt;Unavailable, ambiguous, malformed, direct, compressed, or over-limit evidence&lt;/span&gt;
&lt;span class="s"&gt;does not receive a positive count. This is static review evidence, not&lt;/span&gt;
&lt;span class="s"&gt;signature, digest, certificate, trust, permission, or incremental-update&lt;/span&gt;
&lt;span class="s"&gt;validation.&lt;/span&gt;

&lt;span class="c1"&gt;## A control for historical signatures&lt;/span&gt;

&lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;PDFCAB 1.21.0&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;&lt;span class="s"&gt;(https://github.com/SybilGambleyyu/pdf-change-benchmark/releases/tag/v1.21.0)&lt;/span&gt;
&lt;span class="s"&gt;adds a 158th deterministic pair where both sides have a signature followed by&lt;/span&gt;
&lt;span class="s"&gt;a later incremental update. Neither range reaches the current file end. The&lt;/span&gt;
&lt;span class="s"&gt;baseline reaches its signing revision footer; the candidate stops short of it.&lt;/span&gt;
&lt;span class="s"&gt;Only the new own-revision inventory event and PFP013 are expected. PDFFence&lt;/span&gt;
&lt;span class="s"&gt;1.20 reports changed bytes alone on this pair.&lt;/span&gt;

&lt;span class="s"&gt;PDFFence 1.21 passed 254 tests and Ruff; PDFCAB passed 88 tests and Ruff. The&lt;/span&gt;
&lt;span class="s"&gt;public process-bound score was exact at 158 of 158 pairs, with reproducible&lt;/span&gt;
&lt;span class="s"&gt;wheel/source builds and clean Python 3.12/3.13 installs. A pyHanko&lt;/span&gt;
&lt;span class="s"&gt;two-signature exercise counted both signatures at their own revisions while&lt;/span&gt;
&lt;span class="s"&gt;only the newest reached current EOF.&lt;/span&gt;

&lt;span class="s"&gt;Read the canonical [PDFFence 1.21 release note](https://sybilgambleyyu.github.io/posts/pdffence-121.html)&lt;/span&gt;
&lt;span class="s"&gt;for the supported boundary, validation record, and release links.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

</description>
      <category>pdf</category>
      <category>security</category>
      <category>opensource</category>
      <category>productivity</category>
    </item>
    <item>
      <title>A signature ByteRange cannot hide an indirect signature value</title>
      <dc:creator>SybilGambleyyu</dc:creator>
      <pubDate>Fri, 07 Aug 2026 08:42:35 +0000</pubDate>
      <link>https://dev.to/sybilgambleyyu/a-signature-byterange-cannot-hide-an-indirect-signature-value-20eb</link>
      <guid>https://dev.to/sybilgambleyyu/a-signature-byterange-cannot-hide-an-indirect-signature-value-20eb</guid>
      <description>&lt;p&gt;Liquid syntax error: Unknown tag 'endraw'&lt;/p&gt;
</description>
      <category>pdf</category>
      <category>security</category>
      <category>opensource</category>
      <category>productivity</category>
    </item>
    <item>
      <title>A current signature boundary must bound the signature contents</title>
      <dc:creator>SybilGambleyyu</dc:creator>
      <pubDate>Fri, 07 Aug 2026 07:58:55 +0000</pubDate>
      <link>https://dev.to/sybilgambleyyu/a-current-signature-boundary-must-bound-the-signature-contents-44i7</link>
      <guid>https://dev.to/sybilgambleyyu/a-current-signature-boundary-must-bound-the-signature-contents-44i7</guid>
      <description>&lt;h1&gt;
  
  
  A current signature boundary must bound the signature contents
&lt;/h1&gt;

&lt;p&gt;A PDF signature's &lt;code&gt;/ByteRange&lt;/code&gt; normally leaves one gap for the signature value&lt;br&gt;
in &lt;code&gt;/Contents&lt;/code&gt;. PDFFence 1.18 made a useful static question review-visible: does&lt;br&gt;
a semantic signature range reach the current physical file end? But an&lt;br&gt;
EOF-reaching range alone does not establish what its omitted span represents.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/SybilGambleyyu/pdffence/releases/tag/v1.19.0" rel="noopener noreferrer"&gt;PDFFence 1.19.0&lt;/a&gt;&lt;br&gt;
adds an opt-in structural review gate for the missing connection.&lt;/p&gt;

&lt;h2&gt;
  
  
  The narrower evidence
&lt;/h2&gt;

&lt;p&gt;The new aggregate count is positive only for a semantic signature root when:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;its &lt;code&gt;/ByteRange&lt;/code&gt; has exactly two pairs and reaches current EOF;&lt;/li&gt;
&lt;li&gt;the sole omitted gap exactly spans a direct hexadecimal &lt;code&gt;/Contents&lt;/code&gt; token;&lt;/li&gt;
&lt;li&gt;that token is found in an xref-addressable direct signature dictionary by a
bounded raw lexical scan.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;PDFFence establishes a signature root only through a catalog-reachable AcroForm&lt;br&gt;
signature field, including inherited &lt;code&gt;/FT&lt;/code&gt;, or catalog &lt;code&gt;/Perms&lt;/code&gt; DocMDP, UR, or&lt;br&gt;
UR3. A private &lt;code&gt;/PieceInfo&lt;/code&gt; lookalike is not signature evidence.&lt;/p&gt;

&lt;h2&gt;
  
  
  PFP011: require a Contents-bound current range
&lt;/h2&gt;

&lt;p&gt;The opt-in &lt;code&gt;require_contents_bound_current_signature_coverage&lt;/code&gt; rule (&lt;code&gt;PFP011&lt;/code&gt;)&lt;br&gt;
fails only when an inspected PDF has semantic signature roots but none has that&lt;br&gt;
positive Contents-bound current-file evidence. Unsigned PDFs pass.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;
&lt;span class="na"&gt;rules&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;require_contents_bound_current_signature_coverage&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;span class="s"&gt;~~~&lt;/span&gt;

&lt;span class="s"&gt;PFP011 complements PFP009 and PFP010. PFP009 asks whether current-file&lt;/span&gt;
&lt;span class="s"&gt;coverage fell. PFP010 asks whether any current-file boundary exists now. PFP011&lt;/span&gt;
&lt;span class="s"&gt;asks whether one of those current boundaries has an omitted gap exactly bound&lt;/span&gt;
&lt;span class="s"&gt;to direct `/Contents`.&lt;/span&gt;

&lt;span class="c1"&gt;## A control that preserves EOF coverage&lt;/span&gt;

&lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;PDF Change Assurance Benchmark&lt;/span&gt;
&lt;span class="nv"&gt;1.19.0&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;&lt;span class="s"&gt;(https://github.com/SybilGambleyyu/pdf-change-benchmark/releases/tag/v1.19.0)&lt;/span&gt;
&lt;span class="s"&gt;adds a deterministic pair where both semantic ByteRanges remain well-formed and&lt;/span&gt;
&lt;span class="s"&gt;reach EOF. The candidate widens its gap by one byte before direct `/Contents`.&lt;/span&gt;
&lt;span class="s"&gt;The generic coverage inventory changes and PFP011 fires; PFP009 and PFP010&lt;/span&gt;
&lt;span class="s"&gt;correctly remain quiet.&lt;/span&gt;

&lt;span class="na"&gt;The scan is intentionally conservative&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;default limits are 1 MiB per&lt;/span&gt;
&lt;span class="s"&gt;dictionary and 16 MiB per source. Unavailable, indirect, malformed, or&lt;/span&gt;
&lt;span class="s"&gt;over-limit objects do not earn positive evidence.&lt;/span&gt;

&lt;span class="s"&gt;This is not signature validation. The [PDF 2.0 signature errata](https://pdf-issues.pdfa.org/32000-2-2020/clause12.html)&lt;/span&gt;
&lt;span class="s"&gt;and the [PDF Association advisory](https://pdfa.org/recently-identified-pdf-digital-signature-vulnerabilities/)&lt;/span&gt;
&lt;span class="s"&gt;motivate explicit review while making its limits clear. PDFFence does not&lt;/span&gt;
&lt;span class="s"&gt;calculate a digest, validate `/Contents`, certificates, trust chains,&lt;/span&gt;
&lt;span class="s"&gt;transforms, or permissions, or decide whether an update is valid, permitted,&lt;/span&gt;
&lt;span class="s"&gt;safe, or malicious. It emits no delimiter positions or signature bytes.&lt;/span&gt;

&lt;span class="s"&gt;PDFFence passed 245 tests and Ruff; PDFCAB passed 86 tests and Ruff. The source&lt;/span&gt;
&lt;span class="s"&gt;candidates scored all 156 pairs. Both packages built reproducibly and passed&lt;/span&gt;
&lt;span class="s"&gt;Twine checks; clean Python 3.12/3.13 wheel installs and a Python &lt;/span&gt;&lt;span class="m"&gt;3.12&lt;/span&gt;
&lt;span class="s"&gt;source-archive install passed dependency checks, fixture verification, and the&lt;/span&gt;
&lt;span class="s"&gt;complete score.&lt;/span&gt;

&lt;span class="s"&gt;~~~bash&lt;/span&gt;
&lt;span class="s"&gt;python -m pip install https://github.com/SybilGambleyyu/pdffence/releases/download/v1.19.0/pdffence-1.19.0-py3-none-any.whl&lt;/span&gt;

&lt;span class="s"&gt;pdffence check before.pdf after.pdf --policy pdffence.yml --format sarif&lt;/span&gt;
&lt;span class="s"&gt;~~~&lt;/span&gt;

&lt;span class="s"&gt;Read the canonical [PDFFence 1.19 release note](https://sybilgambleyyu.github.io/posts/pdffence-119.html)&lt;/span&gt;
&lt;span class="s"&gt;for the precise static-review boundary and validation record.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

</description>
      <category>pdf</category>
      <category>security</category>
      <category>opensource</category>
      <category>productivity</category>
    </item>
    <item>
      <title>A regression gate cannot see an already stale signature boundary</title>
      <dc:creator>SybilGambleyyu</dc:creator>
      <pubDate>Fri, 07 Aug 2026 07:02:35 +0000</pubDate>
      <link>https://dev.to/sybilgambleyyu/a-regression-gate-cannot-see-an-already-stale-signature-boundary-cga</link>
      <guid>https://dev.to/sybilgambleyyu/a-regression-gate-cannot-see-an-already-stale-signature-boundary-cga</guid>
      <description>&lt;h1&gt;
  
  
  A regression gate cannot see an already stale signature boundary
&lt;/h1&gt;

&lt;p&gt;PDFFence 1.17 could make one important condition review-visible: a semantic&lt;br&gt;
PDF signature's &lt;code&gt;/ByteRange&lt;/code&gt; reached the file end before a comparison and does&lt;br&gt;
not now. That is a regression question. But review workflows often start after&lt;br&gt;
history has accumulated. If both sides already have a range behind their&lt;br&gt;
current physical file end, a regression-only rule correctly sees no new drop.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/SybilGambleyyu/pdffence/releases/tag/v1.18.0" rel="noopener noreferrer"&gt;PDFFence 1.18.0&lt;/a&gt;&lt;br&gt;
adds the complementary static gate.&lt;/p&gt;

&lt;h2&gt;
  
  
  A baseline cannot answer every review question
&lt;/h2&gt;

&lt;p&gt;The narrow question is: if a PDF has a standard semantic signature root, is&lt;br&gt;
there at least one corresponding well-formed ByteRange that reaches the current&lt;br&gt;
stored file end?&lt;/p&gt;

&lt;p&gt;PDFFence establishes a signature root only through a catalog-reachable AcroForm&lt;br&gt;
signature field, including inherited &lt;code&gt;/FT&lt;/code&gt;, or catalog &lt;code&gt;/Perms&lt;/code&gt; DocMDP, UR, or&lt;br&gt;
UR3. A private &lt;code&gt;/PieceInfo&lt;/code&gt; dictionary that resembles &lt;code&gt;/Type /Sig&lt;/code&gt; does not&lt;br&gt;
become signature evidence merely because of local key names.&lt;/p&gt;

&lt;h2&gt;
  
  
  PFP010: require a current boundary when signatures exist
&lt;/h2&gt;

&lt;p&gt;The opt-in &lt;code&gt;require_current_file_signature_coverage&lt;/code&gt; rule (&lt;code&gt;PFP010&lt;/code&gt;) fails only&lt;br&gt;
when an inspected PDF has semantic signature roots and none of their ByteRanges&lt;br&gt;
reaches the current physical file end. Unsigned PDFs pass; this does not demand&lt;br&gt;
that every document be signed.&lt;/p&gt;

&lt;p&gt;PFP010 complements &lt;code&gt;no_signature_coverage_regressions&lt;/code&gt; (&lt;code&gt;PFP009&lt;/code&gt;): PFP009 asks&lt;br&gt;
whether current coverage fell, while PFP010 asks whether any current coverage&lt;br&gt;
exists now. The latter gives a generic review gate for a document received&lt;br&gt;
without a known-good predecessor.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;
&lt;span class="na"&gt;rules&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;require_current_file_signature_coverage&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;span class="s"&gt;~~~&lt;/span&gt;

&lt;span class="c1"&gt;## A control with no coverage delta&lt;/span&gt;

&lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;PDF Change Assurance Benchmark&lt;/span&gt;
&lt;span class="nv"&gt;1.18.0&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;&lt;span class="s"&gt;(https://github.com/SybilGambleyyu/pdf-change-benchmark/releases/tag/v1.18.0)&lt;/span&gt;
&lt;span class="s"&gt;adds a pair where the baseline already has a field-root range before current&lt;/span&gt;
&lt;span class="s"&gt;EOF. The candidate appends a second distinct incremental revision, yet aggregate&lt;/span&gt;
&lt;span class="s"&gt;coverage counts remain zero. The expected public changes are only the revision&lt;/span&gt;
&lt;span class="s"&gt;chain and stored bytes. PFP009 has no new regression to report; PFP010 is the&lt;/span&gt;
&lt;span class="s"&gt;single policy result.&lt;/span&gt;

&lt;span class="s"&gt;The [PDF 2.0 signature errata](https://pdf-issues.pdfa.org/32000-2-2020/clause12.html)&lt;/span&gt;
&lt;span class="s"&gt;and the [PDF Association advisory](https://pdfa.org/recently-identified-pdf-digital-signature-vulnerabilities/)&lt;/span&gt;
&lt;span class="s"&gt;explain why byte-range boundaries deserve explicit review—and why they cannot&lt;/span&gt;
&lt;span class="s"&gt;replace signature validation.&lt;/span&gt;

&lt;span class="s"&gt;PFP010 can flag a later update that a conforming validator may consider&lt;/span&gt;
&lt;span class="s"&gt;acceptable. PDFFence does not locate or validate `/Contents`, calculate a&lt;/span&gt;
&lt;span class="s"&gt;signed digest, validate a certificate or trust chain, inspect transforms or&lt;/span&gt;
&lt;span class="s"&gt;permissions, or decide whether an update is valid, permitted, safe, or&lt;/span&gt;
&lt;span class="s"&gt;malicious. It reports fixed aggregate counts and generic findings only.&lt;/span&gt;

&lt;span class="s"&gt;PDFFence passed 235 tests and Ruff; PDFCAB passed 85 tests and Ruff. The source&lt;/span&gt;
&lt;span class="s"&gt;candidate scored all 155 pairs. Both packages built reproducibly and passed&lt;/span&gt;
&lt;span class="s"&gt;Twine checks; clean Python 3.12/3.13 wheel installs and a Python &lt;/span&gt;&lt;span class="m"&gt;3.12&lt;/span&gt;
&lt;span class="s"&gt;source-archive install passed dependency checks, fixture verification, and the&lt;/span&gt;
&lt;span class="s"&gt;complete score.&lt;/span&gt;

&lt;span class="s"&gt;~~~bash&lt;/span&gt;
&lt;span class="s"&gt;python -m pip install https://github.com/SybilGambleyyu/pdffence/releases/download/v1.18.0/pdffence-1.18.0-py3-none-any.whl&lt;/span&gt;

&lt;span class="s"&gt;pdffence check before.pdf after.pdf --policy pdffence.yml --format sarif&lt;/span&gt;
&lt;span class="s"&gt;~~~&lt;/span&gt;

&lt;span class="s"&gt;Read the canonical [PDFFence 1.18 release note](https://sybilgambleyyu.github.io/posts/pdffence-118.html)&lt;/span&gt;
&lt;span class="s"&gt;for the precise static-review boundary and validation record.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

</description>
      <category>pdf</category>
      <category>security</category>
      <category>opensource</category>
      <category>productivity</category>
    </item>
    <item>
      <title>A signature range needs a current file boundary</title>
      <dc:creator>SybilGambleyyu</dc:creator>
      <pubDate>Fri, 07 Aug 2026 06:34:46 +0000</pubDate>
      <link>https://dev.to/sybilgambleyyu/a-signature-range-needs-a-current-file-boundary-3ib5</link>
      <guid>https://dev.to/sybilgambleyyu/a-signature-range-needs-a-current-file-boundary-3ib5</guid>
      <description>&lt;p&gt;Liquid syntax error: 'raw' tag was never closed&lt;/p&gt;
</description>
      <category>pdf</category>
      <category>security</category>
      <category>opensource</category>
      <category>productivity</category>
    </item>
    <item>
      <title>An action inventory needs an execution path too</title>
      <dc:creator>SybilGambleyyu</dc:creator>
      <pubDate>Fri, 07 Aug 2026 05:49:04 +0000</pubDate>
      <link>https://dev.to/sybilgambleyyu/an-action-inventory-needs-an-execution-path-too-5fe4</link>
      <guid>https://dev.to/sybilgambleyyu/an-action-inventory-needs-an-execution-path-too-5fe4</guid>
      <description>&lt;p&gt;Liquid syntax error: 'raw' tag was never closed&lt;/p&gt;
</description>
      <category>pdf</category>
      <category>security</category>
      <category>opensource</category>
      <category>productivity</category>
    </item>
  </channel>
</rss>
