Skip to content

Avoid password prompt on attachment-only encryption with no attachments #20140

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

Aditi-1400
Copy link
Contributor

Fixes: #20049
PDF.js wrongly prompted for passwords when a PDF had no attachments but had an encryption enabled that was limited to only attachments leaving all page, image and metadata streams in clear text.

The encryption dictionary is now discarded for this case. This prevents unnecessary prompts for PDFs with no protected content beyond attachments.

Related issues: #20139

@nicolo-ribaudo
Copy link
Contributor

The PR title and test name are wrong, right? It should be more like "Avoid password prompt when encryption only affects attachments and there are none"

@Aditi-1400
Copy link
Contributor Author

The PR title and test name are wrong, right? It should be more like "Avoid password prompt when encryption only affects attachments and there are none"

Hmm, let me frame it better.

src/core/xref.js Outdated
stmF === "Identity" &&
strF === "Identity" &&
eff !== "Identity" &&
cryptFilters.get(eff)?.get("CFM") !== "None"
Copy link
Contributor

Choose a reason for hiding this comment

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

If cryptFilters.get(eff) is undefined, this last line will result in true and not false. Is it intended?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

No, it wasn't, fixed it

const names = root.get("Names");
if (names instanceof Dict && names.has("EmbeddedFiles")) {
const nameTree = new NameTree(names.getRaw("EmbeddedFiles"), this);
const attachments = nameTree.getAll();
Copy link
Contributor

Choose a reason for hiding this comment

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

Not sure about how much performance matters in this case, but we could have a nameTree.isEmpty() method to avoid having to extract all the entries just to check if any exists.

@Aditi-1400 Aditi-1400 changed the title Avoid password prompt when only encrypted attachments exist Avoid password prompt on attachment-only encryption when there are no… Aug 1, 2025
@Aditi-1400 Aditi-1400 changed the title Avoid password prompt on attachment-only encryption when there are no… Avoid password prompt on attachment-only encryption with no attachments Aug 1, 2025
@@ -143,6 +128,52 @@ class XRef {
}
warn(`XRef.parse - Invalid "Root" reference: "${ex}".`);
}

if (encrypt instanceof Dict) {
Copy link
Contributor

Choose a reason for hiding this comment

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

As far as I can tell all this stuff is useless and only the AuthEvent entry should be checked:
https://christianhaider.de/dokuwiki/lib/exe/fetch.php?media=pdf:pdf32000_2008.pdf#page=76&zoom=auto,-60,624

Copy link
Contributor Author

Choose a reason for hiding this comment

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

From what I understand, It looks like AuthEvent checks for when to check for encryption but doesn't really tell us what parts are encrypted, which is what we need to check if attachments are the only thing that's encrypted

Copy link
Contributor

Choose a reason for hiding this comment

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

According to the AuthEvent value we should only ask for the password when the user wants to access to the attachments (EFOpen) or when the doc is open (DocOpen).
So if the value is EFOpen and there are no attachments, then the user won't be prompted.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I spent some time digging into this today, and based on what the spec says, I agree we can simplify things here. For PDFs with encrypted attachments, there appear to be three relevant cases:

  1. The PDF has /AuthEvent /EFOpen, and includes attachments: (Issue [Bug]: File Attachments do not open when they are encrypted #20139)
  2. It has /AuthEvent /EFOpen but no attachments, so we still need to check if any are actually present.
  3. There's No /AuthEvent, but it uses /StmF /Identity, /StrF /Identity, and /EFF /StdCF, this is a very specific case and not something we see in practice, I tried to generate a PDF that hits this edge-case, but I ended up with a PDF where the entries were /StmF /Identity, /StrF /Identity, and /EFF /StdCF and there was no /AuthEvent, but the content of the attachments was not actually encrypted.

So now, I’ve removed the extra checks for /StmF, /StrF, and /EFF. We're only checking for /AuthEvent and whether the file actually has attachments.

@Aditi-1400 Aditi-1400 force-pushed the fix-password branch 4 times, most recently from dc57108 to 0f1a284 Compare August 6, 2025 21:33
… attachments

PDF.js wrongly prompted for passwords when a PDF had no attachments but
had an encryption enabled that was limited to only attachments
leaving all page, image and metadata streams in clear text.

The encryption dictionary is now discarded for this case.
This prevents unnecessary prompts for PDFs with no protected
content beyond attachments
@Aditi-1400
Copy link
Contributor Author

Added the test PDF separately, since the CI wasn't downloading the PDF from the link for some reason?

) {
let hasEncryptedAttachments = false;
if (root instanceof Dict) {
const names = root.get("Names");
Copy link
Contributor

Choose a reason for hiding this comment

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

I don't understand why you're doing that.
If there's no attachment then the user cannot click on them and the encryption stuff will never be used.
And if there are attachments then we've to only decrypt them when the user request to read them.
There is a bug (we talked about with @timvandermeij) about having to only load (and decrypt) the attachments when the user requests them, because right now we get them all the time.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[Bug]: PDF.js wrongly prompts for a password on attachment-only encrypted PDFs.
4 participants