Skip to content

feat(edit-pdf): secure redaction with fail-closed verify - #98

Merged
McanKul merged 8 commits into
McanKul:developmentfrom
nonamexishere:feat/10-secure-redaction
Sep 10, 2026
Merged

McanKul merged 8 commits into
McanKul:developmentfrom
nonamexishere:feat/10-secure-redaction

Conversation

@nonamexishere

@nonamexishere nonamexishere commented Sep 5, 2026

Copy link
Copy Markdown
Contributor

Summary

Adds a distinct Redact tool in Edit PDF (not a black rectangle stamp). On Save, only pages with a redaction region are rasterized; fill/label are burned into the page image; leftover annotations, form values, and attachments are warned, not stripped. Verification fail-closes if source page-content probes remain. The original file is never overwritten.

Fixes #10.

How it works

  • New kind: "redact" object + canvas tool (separate from Rectangle).
  • Preview is a see-through hatch so you can see what you are covering. Save still burns a solid fill.
  • Affected pages: pdftoppm → paint fill/label on the bitmap → replace page contents with one image XObject. /Rotate pages rasterize a sibling with rotate forced to 0 so the fill lands on the covered words.
  • Missing pdftoppm fails closed (RENDERER_MISSING); no white-page fallback.
  • #34 validate_staged_pdf / qpdf --check still gates dest publish. Redacted pages get a rebuilt content digest.
  • Mixed jobs: unredacted pages keep selectable text. Verify probes only the redacted page’s original stream.

Test plan

Owner-review run (2026-09-10) on feat/10-secure-redaction @ c7fbb06. Command: /Users/mustafa/AllTogether/offpdf-local-pipeline/tools/gates.sh. Local pdftoppm 26.08.0 at /opt/homebrew/bin/pdftoppm (qpdf 12.3.2).

development was not merged into this branch (maintainer owns merge). PR is still CONFLICTING / DIRTY. Conflict remains src-tauri/Cargo.toml only: keep both tauri-plugin-single-instance (from development) and [dev-dependencies] flate2 (this branch) when that merge happens. Cargo.lock and pdf_engine/mod.rs auto-merge.

This pass did not drive the Tauri desktop UI and did not open manual-qa/ files in a viewer. Cargo stream-absence is not claimed as “cannot be selected/searched in a viewer.” Checkboxes below are only for what this run actually executed.

  • npm test / npm run typecheck / cargo test --lib

    • npm test: 35 files, 288 passed. tsc --noEmit clean.
    • cargo test --manifest-path src-tauri/Cargo.toml --lib: 275 passed, 0 failed, 0 ignored.
    • Focused filter cargo test --manifest-path src-tauri/Cargo.toml --lib edit_redact -- --test-threads=1 --nocapture: 34 passed, 0 failed; 0 skip: pdftoppm not available lines. Redact integ ran (not skipped).
    • Keep-green that actually executed: R-TEXT / R-VECTOR / R-IMAGE / R-OCR / R-ANNOT / R-FORM / R-GEOM / R-VERIFY / mixed KEEPME / R-UU / R-MIXDUP / R-FLAT / R-NOROT / R-EMPTY / R-INHERIT / R-FORMV / R-FATT / R-SHARE / R-ZIP / R-GEOM-PX / R-UI (serde_roundtrip_redact_kind) / OVERWRITE (export_rejects_hard_linked_destination). Frontend: redactObject.test.ts, ObjectList.test.tsx, redactRotate.test.ts.
  • Redact vs Rectangle distinct in toolbar and object list

    • Not run in a desktop session. Cargo/frontend already locks kind: "redact""rect" and the object-list label Redaction. Live toolbar click / hatch vs solid rect still needs a human in the app.
  • Draw a redaction over selectable text, Save to a new file; covered text cannot be selected/searched; original unchanged

    • Not run in a viewer (no Preview / Acrobat / in-app Find). Engine this pass: R-TEXT / R-OCR dest decoded streams lack SECRET / OCRPROBE / corpus Hi; R-VERIFY fail-closed leaves source + existing dest; OVERWRITE hard-linked dest is refused and source bytes stay. That is stream-absence, not viewer Find.
  • Two-page PDF: redact page 1 only; page 2 text still selects

    • Not run in a viewer. Cargo this pass: unredacted_page_on_mixed_redact_job_keeps_source_stream (page 2 KEEPME) and R-MIXDUP (identical sibling SECRET stays; no fail-close). “Page 2 still selects” is desktop.
  • Rotated QA file (qa-rotate90-trim-inside-crop.pdf): black patch sits on the word you covered

    • Not run on the named QA file / Tauri preview. Cargo this pass: R-GEOM + R-GEOM-PX on corpus geom-rotate-90 / geom-crop-offset (dest /ImR interior is the fill in unrotated MediaBox pixel space). Combined rotate+Trim⊂Crop visual is still desktop.
  • Form/notes leftover: warning on the job; values not auto-stripped

    • Not run on qa-form-fill.pdf / job card. Cargo this pass: R-ANNOT / R-FORM / R-FORMV / R-FATT — warn, do not strip. Job-card JobUpdate.message after Save is desktop.
  • Control: black Rectangle over the same word — text still selectable under the box

    • Not run in a desktop session. Overlay still stamps rect and does not rasterize; that is not the same as “text still selectable in a viewer.”
  • Redact-tool banner only while Redact is selected; Output warning once a redaction exists

    • Not run in a desktop session. Code still gates the hatch banner on tool === "redact" and the Output alert on objects.some(kind === "redact").

…erify

Add a distinct redact object and tool in Edit PDF. Save rasterizes only
pages that have regions, burns fill/label into the page image, and
fail-closes if source page-content probes remain. Leftover annotations,
form values, and attachments are warned, not stripped.

Checkpoint before review-bug fixes: rotate fill mapping, missing
pdftoppm must not publish, and mixed-job verify probes.
Rasterize a /Rotate-zero sibling so fill lands on the covered words, fail
closed when pdftoppm is missing, and probe only the redacted page stream.
Preview uses a see-through hatch; the tradeoff banner shows only while
the Redact tool is selected.
Address PR McanKul#98 review: scale the raster/fill by /UserUnit, search
verify probes only on the redacted page, flatten widgets/annots before
the burn, and ignore objectRotate on redact objects.

@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 the substantial work here. This now conflicts with the current development branch, so please resolve those conflicts first. Because redaction is security-sensitive, please also run and report the unchecked manual cases in the PR description—especially text recovery/search, rotated pages, mixed redacted/unredacted pages, forms/annotations, and original-file preservation.

nonamexishere and others added 2 commits September 10, 2026 03:47
…ripping Resources

Redact inspector no longer shows Opacity (Save already burns opaque).
applyUpdate ignores opacity on redact objects, matching objectRotate.
collect_pages_tree_nodes only walks /Type /Pages so a typeless Kids
leaf keeps its /Resources. Drop the narrating OVERWRITE keepGreen line.

@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.

Conflict resolved against the current development branch. The merged result passes the production build, 306 frontend tests, and 288 Rust tests locally.

@McanKul
McanKul merged commit bc9e239 into McanKul:development Sep 10, 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