From a3b54e834cd56805247f22d3f06f3e1fbac7f6cd Mon Sep 17 00:00:00 2001 From: BOHEUS <56270748+BOHEUS@users.noreply.github.com> Date: Wed, 29 Jul 2026 11:16:29 +0200 Subject: [PATCH] PDF upload fix (#23473) Sometimes uploading PDF files resulted in "Non-whitespace before first tag." error, updating parsing library fixes the error Review in cubic --- packages/twenty-server/package.json | 2 +- .../extract-file-info-or-throw.utils.spec.ts | 25 +++++++++++++ yarn.lock | 36 +++++++++++-------- 3 files changed, 48 insertions(+), 15 deletions(-) diff --git a/packages/twenty-server/package.json b/packages/twenty-server/package.json index 6f8df049bb..834801f893 100644 --- a/packages/twenty-server/package.json +++ b/packages/twenty-server/package.json @@ -37,7 +37,7 @@ "@e2b/code-interpreter": "^2.6.0", "@envelop/core": "4.0.3", "@faker-js/faker": "9.8.0", - "@file-type/pdf": "^0.2.0", + "@file-type/pdf": "^0.3.0", "@graphql-tools/schema": "10.0.4", "@graphql-tools/utils": "9.2.1", "@graphql-yoga/nestjs": "2.1.0", diff --git a/packages/twenty-server/src/engine/core-modules/file/utils/__tests__/extract-file-info-or-throw.utils.spec.ts b/packages/twenty-server/src/engine/core-modules/file/utils/__tests__/extract-file-info-or-throw.utils.spec.ts index 2132b6e284..b07c8721e3 100644 --- a/packages/twenty-server/src/engine/core-modules/file/utils/__tests__/extract-file-info-or-throw.utils.spec.ts +++ b/packages/twenty-server/src/engine/core-modules/file/utils/__tests__/extract-file-info-or-throw.utils.spec.ts @@ -8,6 +8,22 @@ const pdfBuffer = Buffer.from('%PDF-1.4\n', 'utf-8'); const textBuffer = Buffer.from('Hello, world!', 'utf-8'); const zipBuffer = Buffer.from([0x50, 0x4b, 0x03, 0x04]); +const pdfWithIndirectLengthXmpBuffer = Buffer.from( + `%PDF-1.4 +1 0 obj +<< /Type /Metadata /Subtype /XML /Length 5 0 R >> +stream +this is not valid xml, just padding so the (wrongly parsed) length is plausible +endstream +endobj +5 0 obj +250 +endobj +%%EOF +`, + 'utf-8', +); + describe('extractFileInfoOrThrow', () => { it.each([ { @@ -105,6 +121,15 @@ describe('extractFileInfoOrThrow', () => { }, ); + it('should detect PDF when an XMP metadata stream has an indirect /Length reference', async () => { + const result = await extractFileInfoOrThrow({ + file: pdfWithIndirectLengthXmpBuffer, + filename: 'document.pdf', + }); + + expect(result).toEqual({ mimeType: 'application/pdf', ext: 'pdf' }); + }); + describe('TWENTY_MIME_POLICY (Twenty deviates from IANA)', () => { it.each([ { filename: 'src/index.ts', expectedMime: 'application/typescript' }, diff --git a/yarn.lock b/yarn.lock index 28b406ab0c..ab6babcfd4 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4826,6 +4826,13 @@ __metadata: languageName: node linkType: hard +"@borewit/text-codec@npm:^0.2.2": + version: 0.2.2 + resolution: "@borewit/text-codec@npm:0.2.2" + checksum: 10c0/2d3fb132bc6a132914a8fbf8e9ff2fa1ead210ecc395b28bb7355bd7719548a5e351ffe39f21c3bee8048f6cabd99eabd404bb5cc809cad9cba25abed19d271f + languageName: node + linkType: hard + "@bramus/specificity@npm:^2.4.2": version: 2.4.2 resolution: "@bramus/specificity@npm:2.4.2" @@ -6202,14 +6209,15 @@ __metadata: languageName: node linkType: hard -"@file-type/pdf@npm:^0.2.0": - version: 0.2.0 - resolution: "@file-type/pdf@npm:0.2.0" +"@file-type/pdf@npm:^0.3.0": + version: 0.3.0 + resolution: "@file-type/pdf@npm:0.3.0" dependencies: - "@borewit/text-codec": "npm:^0.2.1" + "@borewit/text-codec": "npm:^0.2.2" + debug: "npm:^4.4.3" read-next-line: "npm:^0.5.0" - sax: "npm:^1.4.1" - checksum: 10c0/9f2f446a04ca2fff4f5a02aeb6065d72f4fc95b6bce5efc6a8ec77530c5b36192f9aa718b65e00398921cf5a0d505cda5e9208d3ac81aad63f7d60a535212a89 + sax: "npm:^1.6.0" + checksum: 10c0/ad54724cf90aea1a60f3419a4bf5120b18f8b58ab802d49a0a0e5233fe44f784cbe19a3f68bf757d4d9d6d40cdb5fa2a09ab07bf0887183cab20deb3265b80e2 languageName: node linkType: hard @@ -45644,13 +45652,6 @@ __metadata: languageName: node linkType: hard -"sax@npm:^1.4.1": - version: 1.5.0 - resolution: "sax@npm:1.5.0" - checksum: 10c0/bc3b60a7bfecd40b18256596e96b32df2488339ae1e00a77f842b568f0831228a16c3bd357ec500241ec0b9dc7a475a1286427795c4a8c50bb8e8878f3435dd8 - languageName: node - linkType: hard - "sax@npm:^1.5.0": version: 1.6.0 resolution: "sax@npm:1.6.0" @@ -45658,6 +45659,13 @@ __metadata: languageName: node linkType: hard +"sax@npm:^1.6.0": + version: 1.6.1 + resolution: "sax@npm:1.6.1" + checksum: 10c0/c91a71043d60da50fdf1e2cee936fc7ad4c9376afb8d1022aef5655f279433640859ec06b3b49abfcbe578fc7da2828cc1661e45aaedf835f5393496193437fa + languageName: node + linkType: hard + "saxes@npm:^6.0.0": version: 6.0.0 resolution: "saxes@npm:6.0.0" @@ -49206,7 +49214,7 @@ __metadata: "@e2b/code-interpreter": "npm:^2.6.0" "@envelop/core": "npm:4.0.3" "@faker-js/faker": "npm:9.8.0" - "@file-type/pdf": "npm:^0.2.0" + "@file-type/pdf": "npm:^0.3.0" "@graphql-tools/schema": "npm:10.0.4" "@graphql-tools/utils": "npm:9.2.1" "@graphql-yoga/nestjs": "npm:2.1.0"