Skip to content

fix(pdf): identify password-protected PDFs in docling-parse backend - #4833

Open
Kylinny wants to merge 2 commits into
docling-project:mainfrom
Kylinny:fix/docling-parse-encrypted-pdf-4829-forkbase
Open

Kylinny wants to merge 2 commits into
docling-project:mainfrom
Kylinny:fix/docling-parse-encrypted-pdf-4829-forkbase

Conversation

@Kylinny

@Kylinny Kylinny commented Oct 14, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Fixes #4829 by making the default docling-parse backend identify encrypted PDFs instead of reporting the same generic load error used for corrupt files.

When docling-parse fails to load a PDF, the backend now asks PDFium to classify the same load failure and checks PDFium's FPDF_ERR_PASSWORD error code. This is a structural PDF parse, not a byte-pattern or trailer-location heuristic, so it also covers linearized PDFs and cross-reference streams. The original docling-parse exception remains the chained cause.

The probe runs only on docling-parse's failure path. docling installs both PDF backends, while users who install only the format-pdf-docling extra may not have PDFium; in that case the backend preserves the original generic error rather than guessing from the bytes.

Tests

  • Both PDF backends with no password and a wrong password.
  • A valid encrypted PDF with more than 16 KiB appended after its normal trailer, proving detection does not depend on /Encrypt being inside a fixed tail window.
  • The stream-position invariant for BytesIO inputs.
  • Existing corrupt, non-encrypted PDFs retain the original load error.

The parser-native long-term fix is tracked in docling-project/docling-parse#414. Once a released docling-parse exposes PdfPasswordError, this fallback can be replaced directly with that exception type.

Fixes #4829.

@github-actions

Copy link
Copy Markdown
Contributor

✅ DCO Check Passed

Thanks @Kylinny, all your commits are properly signed off. 🎉

@mergify

mergify Bot commented Oct 14, 2026

Copy link
Copy Markdown
Contributor

Merge Protections

🟢 Merge protection satisfied — ready to merge.

Show 1 satisfied protection

🟢 Enforce conventional commit

Make sure that we follow https://www.conventionalcommits.org/en/v1.0.0/

  • title ~= ^(fix|feat|docs|style|refactor|perf|test|build|ci|chore|revert)(?:\(.+\))?(!)?:

@github-actions github-actions Bot added ai:ci-safe AI triage found no CI-safety concern (advisory) bug Something isn't working error-handling pdf PDF issue (except docling-parse) pdf parsing PDF issue related to docling-parse labels Oct 14, 2026
@github-actions

github-actions Bot commented Oct 14, 2026 •

Copy link
Copy Markdown
Contributor

AI triage (advisory)

Checked commit 48156483dd. The analysis read the diff only. It did not run code from this pull request.

Warning

The AI analysis did not finish. Only the deterministic checks are shown.

CI safety: not assessed.

Possibly related pull requests

PR State Signal Assessment
#3757 merged referenced as #3757; also references issue #1920 not assessed

The ai:* labels are advisory. They do not approve CI and they do not add tests:full. A new push runs this check again.

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

AI first review (advisory): no blocker found

This review covers the full diff at 05499893bd. It read the code and did not run it.

The new probe only looks at the last 8 KiB of the file for /Encrypt. Encrypted PDFs where that entry is not in the last 8 KiB will still get the generic load error, and the PR does not say so. The reviewer must decide whether this coverage limit is acceptable, and whether the test PDF really shows the fix.

A maintainer makes the merge decision. This review never approves the pull request.


# Matches an /Encrypt entry in a PDF trailer dictionary, either as an indirect
# reference (`/Encrypt 5 0 R`) or as an inline dictionary (`/Encrypt << ...`).
_ENCRYPT_ENTRY_PATTERN = re.compile(rb"/Encrypt\s*(?:<<|\d+\s+\d+\s+R)")

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

question: Tail-only probe can miss /Encrypt in valid encrypted PDFs

_pdf_trailer_indicates_encryption() reads only the last 8 KiB (_ENCRYPTION_PROBE_TAIL_BYTES). In two valid PDF layouts the /Encrypt entry may be outside that window:

  • Linearized PDFs. The first-page trailer near the start of the file carries /Encrypt. As I recall the spec, the trailer at the end of the file holds only /Size. Please check this against ISO 32000 Annex F.
  • PDFs with cross-reference streams. /Encrypt sits in the stream dictionary, which comes before the stream data. For a large document, the stream data can be bigger than 8 KiB. Then the dictionary is outside the tail window.

In both cases the probe returns False. The user gets the old generic docling-parse could not load document ... message, the same defect as #4829.

Please ask the author two things:

  1. Which PDF layouts did they test? The only fixture is tests/data/pdf_password/sources/2206.01062_pg3.pdf, a single-page file.
  2. If these layouts are out of scope, state the limit in the docstring or the PR description.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I dont think you can identify if it is password protected by a regex, we need to do this more properly.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Agreed. I replaced the regex/trailer scan entirely in 4815648. The failure is now classified by loading the same input with PDFium and checking its structured FPDF_ERR_PASSWORD error code. The probe runs only after docling-parse fails, preserves BytesIO position, and falls back to the original generic error when the optional PDFium backend is unavailable rather than guessing. I also added a regression with more than 16 KiB after the encrypted PDF trailer, which the former tail scan could not detect. The parser-native exception in docling-parse#414 remains the cleaner long-term path once released.

@github-actions github-actions Bot added the ai:review-lgtm AI first review found no blocker or major issue (advisory) label Oct 14, 2026
@codecov

codecov Bot commented Oct 15, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 84.61538% with 4 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
docling/backend/docling_parse_backend.py 84.61% 3 Missing and 1 partial ⚠️

📢 Thoughts on this report? Let us know!


# Matches an /Encrypt entry in a PDF trailer dictionary, either as an indirect
# reference (`/Encrypt 5 0 R`) or as an inline dictionary (`/Encrypt << ...`).
_ENCRYPT_ENTRY_PATTERN = re.compile(rb"/Encrypt\s*(?:<<|\d+\s+\d+\s+R)")

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I dont think you can identify if it is password protected by a regex, we need to do this more properly.

Yao-Junyi and others added 2 commits October 21, 2026 20:51
When docling-parse fails to load a document, probe the PDF trailer for an
/Encrypt entry and raise a DocumentLoadError that explicitly names password
protection, instead of the generic load failure that is indistinguishable
from a corrupt file. Mirrors the iWork backend's password-protected error
and complements the pypdfium2 backend's chained PdfiumError (see docling-project#1920,
path, and leaves corrupt (non-encrypted) files on the original message.

Fixes docling-project#4829.

Signed-off-by: Junyi Yao <junyi.yao.real@outlook.com>
Signed-off-by: Junyi Yao <j.yao@wustl.edu>
@Kylinny
Kylinny force-pushed the fix/docling-parse-encrypted-pdf-4829-forkbase branch from 0549989 to 4815648 Compare October 22, 2026 04:01
@github-actions github-actions Bot removed ai:ci-safe AI triage found no CI-safety concern (advisory) ai:review-lgtm AI first review found no blocker or major issue (advisory) labels Oct 22, 2026

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working error-handling pdf parsing PDF issue related to docling-parse pdf PDF issue (except docling-parse)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

PY-10-docling: encrypted PDF is indistinguishable from a corrupt PDF with the default docling-parse backend (follow-up of #1920 / #3757)

3 participants