From 3fbc1ad164d2627117e0f8aa04e9b994006252ec Mon Sep 17 00:00:00 2001 From: haelyra <49814733+haelyra@users.noreply.github.com> Date: Fri, 28 Aug 2026 16:47:36 -0400 Subject: [PATCH] fix(plan-canvas): validate opened PDF handle --- scripts/lib/plan-canvas/pdf.js | 4 ++-- tests/scripts/plan-canvas-pdf.test.js | 22 ++++++++++++++++++++++ 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/scripts/lib/plan-canvas/pdf.js b/scripts/lib/plan-canvas/pdf.js index 51e484262..ac3828ea0 100644 --- a/scripts/lib/plan-canvas/pdf.js +++ b/scripts/lib/plan-canvas/pdf.js @@ -115,9 +115,9 @@ function assertLoopbackUrl(value) { function isCompletePdf(file, fsImpl = fs) { let fd; try { - const stat = fsImpl.statSync(file); - if (!stat.isFile() || stat.size < 12) return false; fd = fsImpl.openSync(file, 'r'); + const stat = fsImpl.fstatSync(fd); + if (!stat.isFile() || stat.size < 12) return false; const head = Buffer.alloc(5); fsImpl.readSync(fd, head, 0, head.length, 0); const tailLength = Math.min(2048, stat.size); diff --git a/tests/scripts/plan-canvas-pdf.test.js b/tests/scripts/plan-canvas-pdf.test.js index e2e2b4153..f52100b9e 100644 --- a/tests/scripts/plan-canvas-pdf.test.js +++ b/tests/scripts/plan-canvas-pdf.test.js @@ -102,6 +102,28 @@ async function main() { fs.rmSync(tmp, { recursive: true, force: true }); }); + await test('validates PDF metadata from the opened file handle', () => { + const content = Buffer.from('%PDF-1.4\nlocal plan\n%%EOF\n'); + const fsImpl = { + openSync(file, flags) { + assert.strictEqual(file, '/private/export.pdf'); + assert.strictEqual(flags, 'r'); + return 41; + }, + fstatSync(fd) { + assert.strictEqual(fd, 41); + return { isFile: () => true, size: content.length }; + }, + readSync(fd, target, offset, length, position) { + assert.strictEqual(fd, 41); + return content.copy(target, offset, position, position + length); + }, + closeSync(fd) { assert.strictEqual(fd, 41); }, + statSync() { throw new Error('path metadata must not be checked before opening'); } + }; + assert.strictEqual(isCompletePdf('/private/export.pdf', fsImpl), true); + }); + await test('renders, terminates its private browser, and removes temporary state', async () => { const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'plan-canvas-pdf-test-')); let spawned = null;