Trevor PaughSystems engineer
Trevor PaughProject Chronicles

Chronicles / Eboda Gymnast

Six holes in the change checker

Decision

The checker that says whether a signed or certified PDF has been changed since signing was reviewed as an attacker would review it. Two rounds found five ways a forged change could pass as untouched; the fix round found a sixth. Every one was closed with the reviewer's own attack tests landed, and none was written down as a known limit. The same day a redaction review found a font measurement that could let a letter survive inside a repeating pattern, and that path now refuses rather than guesses.

The Signatures panel on a test document a reviewer had altered after certification: the badge reads 'Signed, then changed' where an earlier build would have said 'Valid'. The signer and the page text are test fixtures.
The Signatures panel on a test document a reviewer had altered after certification: the badge reads 'Signed, then changed' where an earlier build would have said 'Valid'. The signer and the page text are test fixtures.

Certificate signing landed on 14 October with a checker beside it: open a signed or certified document and the app says whether anything has changed since the signature was applied. The review follow-ups on that branch turned the checker into the main event, because a checker is only worth having if it cannot be talked around.

What the reviewers found

Two review rounds approached the checker the way a forger would, by building altered documents and seeing which ones it waved through. Five got past it. In each case the trick was the same in spirit: present a real change as something the checker had been told to ignore. A change labelled as cosmetic that could in fact rewrite any drawing on the page. A change labelled as a comment that could rewrite any object in the file. A piece of page content given a label that made the checker treat it as bookkeeping. A redirect in the file’s own index. And an object the signed version referred to without actually containing, which a later revision could then supply.

The manager’s rule for every one of them was the same rule the redaction review had set the week before: a documented hole is a hole. Each was fixed, the reviewer’s attack test was kept in the suite, and nothing was written up as a limitation.

The sixth

Fixing the five turned up a sixth, this time found by the builder. The library that reads a page’s drawing instructions stops quietly at the first token it cannot parse. A forger could therefore place a stray character in a later signature’s appearance and hide a whole drawing layer behind it: the checker, reading up to the stray character, saw nothing, while an ordinary viewer that recovers from the error would draw the layer. Appearances are now read strictly to the end, and anything unreadable is a refusal, not a shrug.

Redaction, again

The same day’s redaction review found a quieter version of the same principle. Where a font embedded in a file carries no widths for some of its characters, the app falls back to measuring with an installed copy of the font. That is fine for ordinary page text, because the rendering engine checks the result independently. Inside a repeating fill pattern nothing does, and a width that was slightly off could leave the tail of a letter outside the box. The fallback now stays for page text and is refused inside pattern cells, with the toast naming the font so the owner knows why.

The line the manager wrote under both fixes: a check that is not independent of the thing it checks is not a check.