fix: accept owner-password protected pdfs (#3396)

Strip encryption from PDFs that open with an empty user password via
libpdf's ignorePermissions, still rejecting user-password PDFs.

Upgrade @libpdf/core to 0.5.1, which also keeps overlapping and layered
text intact during text extraction.

Resolves #3303
This commit is contained in:
Lucas Smith
2026-09-26 11:23:33 +10:00
committed by GitHub
parent 39ae85483a
commit a1d4bec143
5 changed files with 15 additions and 11 deletions
@@ -122,7 +122,7 @@ export const EnvelopeItemEditDialog = ({
toast({ toast({
title: t`Failed to read file`, title: t`Failed to read file`,
description: t`The file is not a valid PDF.`, description: t`The file is not a valid PDF or is password protected.`,
variant: 'destructive', variant: 'destructive',
}); });
} }
+1 -1
View File
@@ -92,7 +92,7 @@ export const getUploadErrorMessage = (code: string): ToastMessageDescriptor => {
.with(AppErrorCode.TOO_MANY_REQUESTS, () => FAIR_USE_LIMIT_EXCEEDED_ERROR_MESSAGE) .with(AppErrorCode.TOO_MANY_REQUESTS, () => FAIR_USE_LIMIT_EXCEEDED_ERROR_MESSAGE)
.with('INVALID_DOCUMENT_FILE', () => ({ .with('INVALID_DOCUMENT_FILE', () => ({
title: msg`Error`, title: msg`Error`,
description: msg`You cannot upload encrypted PDFs.`, description: msg`The file is not a valid PDF or is password protected.`,
})) }))
.with(AppErrorCode.LIMIT_EXCEEDED, () => ({ .with(AppErrorCode.LIMIT_EXCEEDED, () => ({
title: msg`Error`, title: msg`Error`,
+4 -4
View File
@@ -15,7 +15,7 @@
"dependencies": { "dependencies": {
"@ai-sdk/google-vertex": "5.0.48", "@ai-sdk/google-vertex": "5.0.48",
"@documenso/prisma": "*", "@documenso/prisma": "*",
"@libpdf/core": "^0.4.2", "@libpdf/core": "^0.5.1",
"@lingui/conf": "^5.6.0", "@lingui/conf": "^5.6.0",
"@lingui/core": "^5.6.0", "@lingui/core": "^5.6.0",
"@marsidev/react-turnstile": "^1.5.0", "@marsidev/react-turnstile": "^1.5.0",
@@ -4480,9 +4480,9 @@
"license": "MIT" "license": "MIT"
}, },
"node_modules/@libpdf/core": { "node_modules/@libpdf/core": {
"version": "0.4.2", "version": "0.5.1",
"resolved": "https://registry.npmjs.org/@libpdf/core/-/core-0.4.2.tgz", "resolved": "https://registry.npmjs.org/@libpdf/core/-/core-0.5.1.tgz",
"integrity": "sha512-lbkIqLDZCCxjLpiC+8/Xvaru/ME7iVVoihl9tLqbp/CDUWZNF0q3u7s2tBJB9wRW/SzUWID6YPFvBWws770hrQ==", "integrity": "sha512-q+y4AEk9ngqyC1pdX/hNScnfh0ROr4vSiqX4C9CmuBzhtuVukbfTesXCaGvHZ3pksi8AJYDPGQ/0HzSeHZfi3A==",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@noble/ciphers": "^2.2.0", "@noble/ciphers": "^2.2.0",
+1 -1
View File
@@ -106,7 +106,7 @@
"dependencies": { "dependencies": {
"@ai-sdk/google-vertex": "5.0.48", "@ai-sdk/google-vertex": "5.0.48",
"@documenso/prisma": "*", "@documenso/prisma": "*",
"@libpdf/core": "^0.4.2", "@libpdf/core": "^0.5.1",
"@lingui/conf": "^5.6.0", "@lingui/conf": "^5.6.0",
"@lingui/core": "^5.6.0", "@lingui/core": "^5.6.0",
"@prisma/extension-read-replicas": "^0.4.1", "@prisma/extension-read-replicas": "^0.4.1",
@@ -9,14 +9,18 @@ export const normalizePdf = async (pdf: Buffer, options: { flattenForm?: boolean
console.error(`PDF normalization error: ${e.message}`); console.error(`PDF normalization error: ${e.message}`);
throw new AppError('INVALID_DOCUMENT_FILE', { throw new AppError('INVALID_DOCUMENT_FILE', {
message: 'The document is not a valid PDF', message: 'The document is not a valid PDF or is password protected',
}); });
}); });
if (pdfDoc.isEncrypted) { if (pdfDoc.isEncrypted) {
throw new AppError('INVALID_DOCUMENT_FILE', { if (!pdfDoc.isAuthenticated) {
message: 'The document is encrypted', throw new AppError('INVALID_DOCUMENT_FILE', {
}); message: 'The document is password protected',
});
}
pdfDoc.removeProtection({ ignorePermissions: true });
} }
pdfDoc.flattenLayers(); pdfDoc.flattenLayers();