From f0877932f60999778ed5919bc807ab10f541ca78 Mon Sep 17 00:00:00 2001 From: Matthew Jackson Date: Sat, 7 Mar 2026 08:33:09 -0800 Subject: [PATCH] =?UTF-8?q?Fix=20getS3Key=20to=20normalize=20paths=20?= =?UTF-8?q?=E2=80=94=20resolve=20../=20to=20prevent=20duplicate=20S3=20key?= =?UTF-8?q?s?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 4.6 --- lib/storage.mjs | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/lib/storage.mjs b/lib/storage.mjs index 223a8aa..8253a0f 100644 --- a/lib/storage.mjs +++ b/lib/storage.mjs @@ -11,9 +11,8 @@ * storage: { type: "local" } (default) */ import { readFileSync, writeFileSync, existsSync, mkdirSync } from 'fs'; -import { basename, dirname } from 'path'; +import { basename, dirname, join, resolve as resolvePath } from 'path'; import { tmpdir } from 'os'; -import { join } from 'path'; let _s3Client = null; let _config = { type: 'local' }; @@ -27,15 +26,15 @@ export function storageType() { } function getS3Key(filePath) { - // Extract relative path from project root (e.g. config/foo.json or data/bar.json) + // Normalize and extract relative path from project root (e.g. data/jobs_queue.json) const storageUrl = new URL(import.meta.url); const projectRoot = dirname(dirname(storageUrl.pathname)); - const abs = filePath.startsWith('/') ? filePath : join(projectRoot, filePath); + const abs = resolvePath(filePath.startsWith('/') ? filePath : join(projectRoot, filePath)); if (abs.startsWith(projectRoot + '/')) { return abs.slice(projectRoot.length + 1); } - // Fallback: use last two path segments (e.g. data/jobs_queue.json) - const parts = filePath.split('/'); + // Fallback: use last two path segments + const parts = abs.split('/'); return parts.slice(-2).join('/'); }