From e77637b4cfaf5520b478335190b2ba5c95c9ad3f Mon Sep 17 00:00:00 2001 From: Harsh Singh Date: Sat, 10 Oct 2026 20:17:40 +0530 Subject: [PATCH] Remove a customer's video from disk once its frame is read extractVideoFrame wrote the clip and the frame into a new temp folder and never removed it, so every video a customer attached stayed on the server's disk, up to 8 MB each, whether or not a frame came out. The folder is now removed in a finally block. --- attachments.js | 17 +++++++++++------ test/attachments.test.js | 16 ++++++++++++++++ 2 files changed, 27 insertions(+), 6 deletions(-) diff --git a/attachments.js b/attachments.js index d76e42e..59bc16d 100644 --- a/attachments.js +++ b/attachments.js @@ -178,12 +178,17 @@ function extractVideoFrame(buf) { const os = require('os'); const path = require('path'); const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'vec-')); - const input = path.join(dir, 'clip.bin'); - const output = path.join(dir, 'frame.png'); - fs.writeFileSync(input, buf); - const result = spawnSync(ffmpeg, ['-y', '-ss', '1', '-i', input, '-frames:v', '1', output], { timeout: 8000 }); - if (result.status !== 0 || !fs.existsSync(output)) return null; - return fs.readFileSync(output); + try { + const input = path.join(dir, 'clip.bin'); + const output = path.join(dir, 'frame.png'); + fs.writeFileSync(input, buf); + const result = spawnSync(ffmpeg, ['-y', '-ss', '1', '-i', input, '-frames:v', '1', output], { timeout: 8000 }); + if (result.status !== 0 || !fs.existsSync(output)) return null; + return fs.readFileSync(output); + } finally { + // The clip and its frame are a customer's attachment. They do not stay on disk. + fs.rmSync(dir, { recursive: true, force: true }); + } } async function videoErrorLines(attachments, { fetchImpl, extractFrame, recognize } = {}) { diff --git a/test/attachments.test.js b/test/attachments.test.js index f8c9266..7bc10ca 100644 --- a/test/attachments.test.js +++ b/test/attachments.test.js @@ -315,3 +315,19 @@ test('a video frame keeps the error line and the clip is not sent on', async () { video: true } ), false); }); + +test('a customer video is not left on disk after its frame is read', async () => { + const fs = require('node:fs'); + const os = require('node:os'); + const leftovers = () => fs.readdirSync(os.tmpdir()).filter((name) => name.startsWith('vec-')); + const before = new Set(leftovers()); + const lines = await videoErrorLines( + [{ name: 'clip.mp4', contentType: 'video/mp4', url: 'https://cdn.example/clip.mp4' }], + { + fetchImpl: async () => ({ ok: true, arrayBuffer: async () => Buffer.from('not a real video') }), + recognize: async () => 'server_error 1011', + } + ); + assert.equal(lines, ''); + assert.deepEqual(leftovers().filter((name) => !before.has(name)), []); +});