| 1 |
High |
compliance |
unguarded-cast |
src/compliance/pdf_x/validator.rs:1121 |
The PDF/X page-tree walk casts a file-derived /Count i64 to usize without a sign check, so a negative /Count makes *current_index + count overflow a… |
| 2 |
High |
compliance |
silent-wrong |
src/compliance/validators.rs:356 |
validate_transparency never emits ErrorCode::TransparencyNotAllowed, so PDF/A-1 documents that use transparency validate as compliant and the converte… |
| 3 |
High |
document |
forked-dup |
src/document.rs:20471 |
to_markdown_with_ocr renders through the deprecated MarkdownConverter while to_markdown uses the pipeline MarkdownOutputConverter, so the same text-la… |
| 4 |
High |
editor |
non-determinism |
src/editor/document_editor.rs:3332 |
write_full_to_writer serializes page-resource Font/XObject/ExtGState objects in fdict |
| 5 |
High |
editor |
silent-wrong |
src/editor/document_editor.rs:1546 |
write_incremental consumes only modified_objects + Info, silently dropping destructive redactions (redacted_content/redacted_orphan_ids), page removal… |
| 6 |
High |
editor |
forked-dup |
src/editor/document_editor.rs:7786 |
get_page_info indexes the raw source page list (page_refs[index], :7796) with bounds checked against the raw list (:7788), never mapping through page_… |
| 7 |
High |
ffi |
forked-dup |
src/ffi.rs:2094 |
pdf_document_search_page passes (page, page+1) to the INCLUSIVE SearchOptions::with_page_range (text_search |
| 8 |
High |
pipeline |
forked-dup |
src/pipeline/page_order.rs:80 |
Article-thread (/Threads) bead order is wired only into page_reading_order (extract_words/extract_text_lines); extract_text, to_markdown and to_html b… |
| 9 |
High |
python |
silent-wrong |
src/python.rs:357 |
Python remove_headers/remove_footers/remove_artifacts (python |
| 10 |
High |
redaction |
silent-wrong |
src/redaction/text_engine.rs:480 |
redact_text_stream passes Do (form/image XObject) and inline-image operators through unchanged and never sets unsupported_font, so destructive redac… |
| 11 |
High |
signatures |
unguarded-cast |
src/signatures/byterange.rs:100 |
ByteRangeCalculator::extract_signed_bytes casts the four file-derived /ByteRange i64 values straight to usize and adds them, so a negative element pan… |
| 12 |
High |
signatures |
panic |
src/signatures/enumerate.rs:91 |
walk_field recurses into /Kids with no depth limit and no visited-set, so a self-referential AcroForm field tree makes enumerate_signatures overflow t… |
| 13 |
High |
signatures |
silent-wrong |
src/signatures/cms.rs:43 |
extract_signer_certificate_der returns the FIRST certificate in the CMS certificates SET and never consults SignerInfo |
| 14 |
High |
wasm |
silent-wrong |
src/wasm.rs:545 |
WASM eraseHeader/eraseFooter/eraseArtifacts (wasm |
| 15 |
High |
writer |
non-determinism |
src/writer/pdf_writer.rs:1973 |
PdfWriter::finish() emits the 12 base-14 font objects by iterating self |
| 16 |
Medium |
compliance |
silent-wrong |
src/compliance/pdf_ua.rs:673 |
validate_figures, validate_tables and validate_annotations are stubs that only add PartialCheck warnings, yet PdfUaValidator::validate sets is_complia… |
| 17 |
Medium |
document |
silent-wrong |
src/document.rs:20673 |
to_html drops the preserve_input_order flag whenever the document is trustworthy-tagged (build_context branch never sets it), while to_markdown always… |
| 18 |
Medium |
editor |
non-determinism |
src/editor/document_editor.rs:1571 |
write_incremental iterates `for (&obj_id, obj) in &self |
| 19 |
Medium |
editor |
panic |
src/editor/document_editor.rs:1064 |
DocumentEditor::collect_page_refs recurses through /Pages Kids with no cycle detection (its twin PdfDocument::collect_page_refs at document |
| 20 |
Medium |
editor |
forked-dup |
src/editor/document_editor.rs:1071 |
The editor page walker requires /Type == /Page exactly (:1069-1078, typeless leaves silently dropped), while the document walker (document |
| 21 |
Medium |
editor |
silent-wrong |
src/editor/document_editor.rs:4583 |
get_page_media_box and get_page_info never implement MediaBox inheritance (ISO 32000-1 §7 |
| 22 |
Medium |
ffi |
silent-wrong |
src/ffi.rs:7711 |
pdf_validate_pdf_x_level maps ANY unknown level int to PdfXLevel::X4 (_ => PdfXLevel::X4) and returns a verdict with ERR_SUCCESS; wasm validatePdfX … |
| 23 |
Medium |
redaction |
silent-wrong |
src/redaction/options.rs:48 |
RedactionOptions::optional_content (default OcgPolicy::StripHidden, documented as protecting against the "classic redaction leak") is never read by an… |
| 24 |
Medium |
writer |
forked-dup |
src/writer/font_manager.rs:374 |
Writer-side base-14 width table (get_base14_widths) disagrees with the Adobe AFM table used by the extraction side (fonts/font_dict |
| 25 |
Medium |
writer |
non-determinism |
src/writer/font_manager.rs:1249 |
generate_tounicode_cmap stable-sorts (gid, unicode) pairs by gid only, so when two used codepoints share a GID the bfchar emission order comes from Ha… |
| 26 |
Medium |
writer |
non-determinism |
src/writer/xmp_metadata.rs:273 |
XmpWriter::to_xml emits metadata |
| 27 |
Medium |
writer |
silent-wrong |
src/writer/xmp_metadata.rs:302 |
escape_xml escapes only the 5 XML entities and never strips XML-1 |
| 28 |
Medium |
writer |
forked-dup |
src/writer/watermark.rs:268 |
WatermarkAnnotation's appearance stream pushes raw UTF-8 chars into a PDF literal string shown with a simple Helvetica base font (escape_pdf_string at… |
| 29 |
Low |
editor |
non-determinism |
src/editor/document_editor.rs:3554 |
Overlay Base-14 font objects are written by iterating for (name, font_id) in &overlay_font_ids (HashMap<String,u32>, :2479) directly into the output… |
| 30 |
Low |
editor |
non-determinism |
src/editor/document_editor.rs:4106 |
write_full_to_writer's trailing block collects not-yet-written objects via self |
| 31 |
Low |
editor |
non-determinism |
src/editor/document_editor.rs:5233 |
get_form_fields appends newly added (is_new) fields by iterating modified_form_fields (HashMap<String, FormFieldWrapper>, :509) with no sort (:5233-52… |
| 32 |
Low |
ffi |
silent-wrong |
src/ffi.rs:4717 |
pdf_estimate_render_time (ffi |
| 33 |
Low |
python |
poison |
src/python.rs:388 |
sync_editor_erasures iterates `self |
| 34 |
Low |
signatures |
non-determinism |
src/signatures/pades/dss_read.rs:69 |
parse_dss iterates the /VRI dictionary (a std HashMap-backed Object::Dictionary) and pushes entries into DocumentSecurityStore |
| 35 |
Low |
writer |
forked-dup |
src/writer/form_fields/form_appearance.rs:369 |
Push-button caption rendering duplicates the same broken escape fork: escape_pdf_string keeps non-ASCII verbatim into a WinAnsi literal string, and th… |
Nondeterministic output, silent data loss, and malformed-input panics, in the write / edit / render / sign / redact / compliance / bindings subsystems. Each has a runnable reproducer.
Important
Runnable reproducers for all 35 bugs are in this gist: https://gist.github.com/tobocop2/5279a7f5b5df615e43a6dc5b18b08a63 — one test suite (
cargo test --test audit_994_reproducers, plus--features rendering), verified to compile and run against v0.3.77. The table below is the index; the gist has the full tests and their observed output.Note
One issue as an epic, sorry for the format — every row is an independent bug that can be split out. These were all reproduced (twin-run byte-diffs for non-determinism, crafted malformed bytes for panics, direct API/binding calls for the rest), and all blame to a commit at or before v0.3.77.
Severity: 14 high, 14 medium, 7 low.
Seven rendering, xref-reconstruction and structure-tree rows that sit on the read path were split out into #997.
Findings
Sorted by severity. Each row's runnable reproducer (with its observed output) is in the gist above, keyed by
Location.src/compliance/pdf_x/validator.rs:1121*current_index + countoverflow a…src/compliance/validators.rs:356src/document.rs:20471src/editor/document_editor.rs:3332src/editor/document_editor.rs:1546src/editor/document_editor.rs:7786src/ffi.rs:2094src/pipeline/page_order.rs:80src/python.rs:357src/redaction/text_engine.rs:480Do(form/image XObject) and inline-image operators through unchanged and never sets unsupported_font, so destructive redac…src/signatures/byterange.rs:100src/signatures/enumerate.rs:91src/signatures/cms.rs:43src/wasm.rs:545src/writer/pdf_writer.rs:1973src/compliance/pdf_ua.rs:673src/document.rs:20673src/editor/document_editor.rs:1571src/editor/document_editor.rs:1064src/editor/document_editor.rs:1071src/editor/document_editor.rs:4583src/ffi.rs:7711_ => PdfXLevel::X4) and returns a verdict with ERR_SUCCESS; wasm validatePdfX …src/redaction/options.rs:48src/writer/font_manager.rs:374src/writer/font_manager.rs:1249src/writer/xmp_metadata.rs:273src/writer/xmp_metadata.rs:302src/writer/watermark.rs:268src/editor/document_editor.rs:3554for (name, font_id) in &overlay_font_ids(HashMap<String,u32>, :2479) directly into the output…src/editor/document_editor.rs:4106src/editor/document_editor.rs:5233src/ffi.rs:4717src/python.rs:388src/signatures/pades/dss_read.rs:69src/writer/form_fields/form_appearance.rs:369The two recurring patterns
Warning
Same input produces different output bytes. Dictionaries, font tables, and metadata are serialized in
HashMapiteration order, which is randomized per run — so building the same document twice yields different bytes. Breaks reproducible builds and byte-diff caching; on the signature path it changes hashed bytes. The same class was already fixed in redaction and compliance; these are the sites that were missed.Caution
Wrong-but-plausible output, or a crash on a hostile file. Redaction that leaves content in place, validators that never emit the error they exist to raise, and bindings whose erase calls are silent no-ops — no exception, no warning.
Flagged but not executed
Structurally sound, but not reproduced safely (they need real concurrency or would be undefined behavior to trigger):
src/ffi.rs:986*constmanufacture&mutby casting away const: editor_get_string_field! macro (ffisrc/ffi.rs:140src/ffi.rs:7521A separate set of malformed-input parser/decoder hardening findings from the same audit is tracked elsewhere and not included here. Full reproducers: https://gist.github.com/tobocop2/5279a7f5b5df615e43a6dc5b18b08a63