Skip to content

feat(edit): fail-closed validation before Edit PDF publish (#34) - #54

Merged
McanKul merged 4 commits into
McanKul:developmentfrom
nonamexishere:feat/34-validate-edit-output
Aug 26, 2026
Merged

feat(edit): fail-closed validation before Edit PDF publish (#34)#54
McanKul merged 4 commits into
McanKul:developmentfrom
nonamexishere:feat/34-validate-edit-output

Conversation

@nonamexishere

Copy link
Copy Markdown
Contributor

Summary

Adds a fail-closed publish gate for Edit PDF (#34). After qpdf writes the dest-sibling temp file, and before the atomic rename onto the chosen destination, OffPDF now:

  • runs qpdf --check (exit 2 = fatal, exit 3 = warning recorded and does not block, exit 0 = clean)
  • reopens the staged PDF and checks page count/order, MediaBox / CropBox / TrimBox, /Rotate, /UserUnit
  • requires Outlines, Info, AcroForm, and page annotations when the source had them

A failed gate returns INVALID_OUTPUT, deletes the staging file, and leaves the original and any existing destination untouched. The gate is validate_staged_pdf so later annotation / form / redaction work can reuse it.

Packaged Save uses the same bundled qpdf binary as overlay (resolve_qpdf(app)).

Why

Epic #12’s definition of done calls for output validation before any existing-content mutation ships. Today a damaged overlay result could still replace the destination.

Validation

  • npm run typecheck
  • npm test (179)
  • cargo check --manifest-path src-tauri/Cargo.toml
  • cargo test --manifest-path src-tauri/Cargo.toml --lib (109)
  • Manual Save on Edit PDF (new dest; original unchanged)

Privacy Checklist

  • This keeps OffPDF usable offline.
  • This does not upload, log, or transmit user files.
  • New dependencies or bundled binaries have compatible licenses.

@McanKul McanKul left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for taking this on — the fail-closed staging flow is a solid base. A few things still need tightening before we can merge:

  • Page order is currently inferred from geometry only. Two same-sized pages can be swapped while still passing; please validate page identity/order with a bounded content, text, or render-based check.
  • Please add the issue’s integration coverage for real overlay exports preserving annotations/forms/catalog data, including rotated and cropped fixtures.
  • ValidationResult.warnings is discarded by the caller, so qpdf exit-3 warnings are not actually recorded or surfaced.
  • The branch now conflicts with development; please rebase after the recent HEIC merge.

Once these are addressed, we can take another quick pass.

Validate staged Edit PDF output before the destination rename so a
truncated or structurally wrong file cannot replace the user's dest.
qpdf --check errors are fatal; warnings are recorded and do not block.
Fixes McanKul#34.
Re-check cancel after qpdf --check and before publish so a mid-gate
Cancel returns CANCELLED, deletes staging, and does not replace dest.
Production --check registers on the job handle like overlay qpdf.
…ings

Page identity now uses a per-page Contents digest so two same-geometry
pages cannot be swapped past the publish gate. qpdf --check exit 3 still
publishes; the warning text is kept on the completed job update.
@nonamexishere
nonamexishere force-pushed the feat/34-validate-edit-output branch from f241a20 to 25e1445 Compare August 26, 2026 13:01
@nonamexishere

Copy link
Copy Markdown
Contributor Author

Thanks for the review — all four items should be addressed on 25e1445 (rebased onto current development).

  • Page identity: the gate no longer trusts geometry alone. Each source page gets a bounded Contents digest (FNV-1a + length). After overlay, dest must still contain that digest on the page Contents or a Form XObject. Two same-size pages swapped is INVALID_OUTPUT; dest is left alone and staging is deleted.
  • Integ coverage: real overlay exports now cover Outlines + Info + AcroForm + a page /Annots on /Rotate 90 and on a Trim⊂Crop fixture.
  • Warnings: ValidationResult.warnings is no longer dropped. qpdf --check exit 3 still publishes (unchanged V4). The joined stderr is on the completed JobUpdate.message and the Done card renders that field.
  • Rebase: branch is on current development (includes the HEIC plane-size guard). CHANGELOG Unreleased keeps both the gate Added bullet and the HEIC Fixed bullet.

CI on this head is green (frontend + Rust). Ready for another pass when you have time.

@McanKul McanKul left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for addressing the review. I verified the updated implementation locally: frontend tests/typecheck, all Rust tests, and an extra real-qpdf multi-stream page case passed. The page identity check, integration coverage, warning propagation, and rebase now look good.

@McanKul
McanKul merged commit a437919 into McanKul:development Aug 26, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants