Repository navigation
Signatures: defer validation without weakening signed-file guards - #1465
cyfung1031 wants to merge 4 commits into
Conversation
as the benchmark target was not specified, synthetic data were used to present that measurement table. Now replaced the measurement with three actual PDF files. Three Actual PDFs:
|
…aration Consolidate the reviewed change and its follow-ups on current upstream. Original change notes: M1: Defer document-open scans and share inspection state Integrate deferred marker/link indexes, on-demand signature details and shared inspection parsing to reduce large-document open work. Preserve signed-file rewrite guards independently of signature validation. Keep inspection caches independent, retain stricter stream limits, and fall back to source parsing after object, trailer or encryption edits. Add regression coverage for source isolation, lazy cache invalidation, pre-validation rewrite refusal and signature panel behavior. Based on @cyfung1031's proposals: - storytold#1434 (a4cdc2d) - storytold#1465 (b9cd81f) - storytold#1527 (eaa0c27) Refs storytold#307. Background desktop opening and compact xref storage remain separate; this does not claim to fix cold-cache first rendering. M1: Keep desktop responsive throughout document opening Build on @cyfung1031's background-open proposal storytold#1493 at 355d4c5. Split engine preparation from registration and keep CLI/MCP opening synchronous. Explicitly enable the native worker before startup files, carry tool and navigation continuations through password retries, serialize modal completion, and preserve pending session paths on quit. Reprepare stale security-policy snapshots before registration. Keep the bounded worker and UI completion state machine separate. Keep startup view options attached to their original request through retries, failures and cancellation. Add deterministic responsiveness and opening-flow regressions. Seven regressions were observed failing before their fixes. Pin the existing reload test's English labels to an explicit English test locale. Refs storytold#307 Based-on: storytold#1493 M1: Keep startup bytes queued during password preparation Preserve the local password interaction until background preparation finishes. Add a deterministic barrier regression and exercise deferred tools, wrong passwords, cancellation, and text/image conversion success and failure. Cover the public open-then-tool flow in both synchronous and background hosts. test(ui): wait for interactive recovery password fields (storytold#1634) M1: apply backpressure to native document preparation Keep queued paths unread until the UI can consume their result, so a password or tool dialog does not retain every prepared PDF in a batch. Use the UI pending order for both password retries and stale-policy repreparation instead of independently ordering the worker queue. Add regressions for held results, newly queued files behind a password prompt, and security repreparation ahead of later reads. All three fail on the preceding PR head; 1,130 UI tests pass after the change. Refs storytold#307
…aration Consolidate the reviewed change and its follow-ups on current upstream. Original change notes: M1: Defer document-open scans and share inspection state Integrate deferred marker/link indexes, on-demand signature details and shared inspection parsing to reduce large-document open work. Preserve signed-file rewrite guards independently of signature validation. Keep inspection caches independent, retain stricter stream limits, and fall back to source parsing after object, trailer or encryption edits. Add regression coverage for source isolation, lazy cache invalidation, pre-validation rewrite refusal and signature panel behavior. Based on @cyfung1031's proposals: - storytold#1434 (a4cdc2d) - storytold#1465 (b9cd81f) - storytold#1527 (eaa0c27) Refs storytold#307. Background desktop opening and compact xref storage remain separate; this does not claim to fix cold-cache first rendering. M1: Keep desktop responsive throughout document opening Build on @cyfung1031's background-open proposal storytold#1493 at 355d4c5. Split engine preparation from registration and keep CLI/MCP opening synchronous. Explicitly enable the native worker before startup files, carry tool and navigation continuations through password retries, serialize modal completion, and preserve pending session paths on quit. Reprepare stale security-policy snapshots before registration. Keep the bounded worker and UI completion state machine separate. Keep startup view options attached to their original request through retries, failures and cancellation. Add deterministic responsiveness and opening-flow regressions. Seven regressions were observed failing before their fixes. Pin the existing reload test's English labels to an explicit English test locale. Refs storytold#307 Based-on: storytold#1493 M1: Keep startup bytes queued during password preparation Preserve the local password interaction until background preparation finishes. Add a deterministic barrier regression and exercise deferred tools, wrong passwords, cancellation, and text/image conversion success and failure. Cover the public open-then-tool flow in both synchronous and background hosts. test(ui): wait for interactive recovery password fields (storytold#1634) M1: apply backpressure to native document preparation Keep queued paths unread until the UI can consume their result, so a password or tool dialog does not retain every prepared PDF in a batch. Use the UI pending order for both password retries and stale-policy repreparation instead of independently ordering the worker queue. Add regressions for held results, newly queued files behind a password prompt, and security repreparation ahead of later reads. All three fail on the preceding PR head; 1,130 UI tests pass after the change. Refs storytold#307
FredApps
left a comment
There was a problem hiding this comment.
Changes requested. I did not run tests (branch is CONFLICTING/DIRTY, so no host run was queued); hosted CI is green on this head (ubuntu, windows, wasm32, licences), and I reviewed the full diff by reading.
The engine side looks right: is_signed() is now an exact structural check (signature fields with a dict /V, adopted page-only fields, standalone DocTimeStamp), the SignedRewrite guard still runs before any rewrite, and validation caches are invalidated on every refresh and on trust changes.
Problem: opening a signed file no longer shows that it is signed.
canvas.rsnow builds the banner fromdoc.validated_signatures().unwrap_or_default(), andchrome.rscomputeshas_signaturesfromvalidated_signatures()too. Nothing on the open or render path callssignatures(), so on a freshly opened signed PDF the "Signed ..." / "signatures have problems" banner and the Signatures rail button are absent until something else happens to trigger validation. The rail button is also how the user opens the panel that would trigger it.- The tests mask this: in
ui-egui/tests/signing.rsthe code callsdoc.signatures()(which fills the cache) beforeget_by_label("At least one signature has problems."), and the engine test only asserts the accessors.
Fix: use the new cheap doc.is_signed() for has_signatures in right_rail, and in document_area compute the banner from doc.signatures() only when doc.is_signed() is true (unsigned files keep the fast open the PR is after). Add a UI test that opens a signed fixture without touching signatures() and asserts the banner text and the Signatures rail button.
Also rebase onto main (conflicts).
d42ad9c to
d2cb046
Compare
|
Thanks for catching the fresh-open regression. I rebased onto current
Added UI coverage for signed, unsigned, and malformed fresh-open cases. Local verification on the rebased head: the 14 UI signing tests pass; both |
FredApps
left a comment
There was a problem hiding this comment.
Reviewed d2cb046. The fresh-open blocker from my previous review is addressed: structurally signed documents populate the banner on first render, unsigned documents retain deferred validation, and malformed signed documents keep their visible signature warning/rail presence without inventing validated details. Page badges consume only cached validation results.
Hosted CI run 39082116328 is successful for Ubuntu, Windows, wasm32 and licences; fuzz was skipped. I verified that both OS logs pass the three new fresh-open regressions, both signed_state_ tests, and signing_saving_trusting_and_commenting_afterwards. Those jobs tested synthetic merge 44f2ec5, combining this head with main 3f5a784, rather than a literal head checkout.
No remaining blocking finding from this review. I did not run a separate local desktop test or independently reproduce the performance benchmarks.
Keep the signed-document first-render candidate documented alongside the latest upstream roadmap updates. AI assistance: OpenAI Codex for @cyfung1031.


Review direction
Review the exact signed-presence and rewrite-safety behavior first. The timing and RSS figures are performance context; assess correctness from the pre-validation guard tests and the matching signature-list outputs.
What changed
Document::is_signed()uses an exact structural presence check before signature validation. It covers adopted page-only signature fields and standalone document timestamps, including malformed signature dictionaries that must still block a full rewrite.Session::opendefers full signature discovery and cryptographic validation.Document::signatures()validates on first detail use and reuses the result until a document edit or trust-store change invalidates it.Refs #307.
Validation
cargo test --locked --profile low-disk -p pdfcraft-engine signed_state_— 2 passed.cargo test --locked --profile low-disk -p pdfcraft-engine signing_saving_trusting_and_commenting_afterwards— passed; covers trust changes, incremental saves, and full-rewrite refusal.cargo check --locked --profile low-disk --tests -p pdfcraft-engine -p pdfcraft-ui-egui -p pdfcraft-automation— passed.-A clippy::collapsible_matchfor the existing main-branch warning.cargo fmt --allandgit diff --checkpassed.storytold/pdfcraft:mainat87b448b664e1b3c960b479cc0f2c2e7a82c7ac71, the merge base of this branch. Head:cyfung1031/pdfcraft:codex/open-scale-pr-03atb9cd81fad8d67d30dd8b45c2ec28b26df48c7260. Bothpdfcraft-clibinaries use thelow-diskprofile. Inputs: demo_heavy_engineering_A1.pdf, vmware-cloud-foundation-9-1.pdf, and pdf_engine_test.pdf. Each row has one unrecorded warm-up and then 15 measured rounds, with base and head alternating order. CPU was at least 60% idle before each round. Stdout is compared by SHA-256 fordoc_openanddoc_open + sign_list. Base and head run back to back in each round, so each round gives one paired comparison; a row is called consistent only when one side wins at least 12 of 15 pairs (two-sided exact sign test, p < 0.05).None of the three files is signed (
sign_listcount 0 on each), so the signed-file validation path is not measured here. The tables cover the unsigned open path and the first signature-list call.Inputs
Elapsed time
open5c2a942361c4)open6f510de5299a)open7684c4060a9d)open + sign_listda1b19944370)open + sign_list548eadb755f4)open + sign_listbd2494beb80a)Peak memory footprint and max RSS
openopenopenopen + sign_listopen + sign_listopen + sign_listThe one consistent large change is
doc_openonvmware-cloud-foundation-9-1.pdf(9,156 pages, unsigned): faster in 15 of 15 paired rounds, median 8,041 ms to 2,702 ms. The PR'sopen + sign_liston that file (8,399 ms) matches base'sopen + sign_list(8,374 ms; faster in 6 of 15 rounds, no consistent difference), so the saving applies only to sessions that never request signature details.pdf_engine_test.pdfalso opens faster in 15 of 15 rounds (17.4 ms to 13.8 ms, a 3.6 ms gap). Its max RSS is lower in 14 of 15 rounds onopen(14.2 to 13.8 MiB), and its peak footprint is lower in 12 of 15 (8.3 to 8.1 MiB). Onopen + sign_list, its footprint and max RSS are higher in 12 of 15 rounds each (about 0.1 MiB).demo_heavy_engineering_A1.pdfmax RSS is lower in 13 of 15 rounds onopen(48.0 to 47.9 MiB), with no consistent footprint change. The consistent memory changes are all under 0.5 MiB. The benchmark does not profile where the open-time saving comes from.Hosted CI is not used as a validation gate.
Related work
This change overlaps signature and trust paths in #63, #622, and #760. It is based directly on
mainand is not stacked on those branches.Summary by CodeRabbit