From f527cfd03a0eb83c610cdd84dfe520024b0cb1c7 Mon Sep 17 00:00:00 2001 From: Sanaei Date: Fri, 4 Sep 2026 17:47:43 +0200 Subject: [PATCH] fix: harden getStoragePath and keep CRLF endings - Normalize the returned path to forward slashes so it matches what Storage.list() (glob) returns, instead of leaking `\` on Windows. - Treat the storage root itself as "not inside", so passing a directory no longer yields an empty path that Storage.load() would open as a dir. - Extract the containment check into isInsideDirectory() and drop the redundant isAbsolute(filepath) branch (path.resolve already handles it). - Restore the repository's CRLF line endings in the nested fixture. --- scripts/utils.ts | 29 +++++++++++-------- .../input/playlist_format/nested/nested.m3u | 10 +++---- 2 files changed, 22 insertions(+), 17 deletions(-) diff --git a/scripts/utils.ts b/scripts/utils.ts index 20dc5dd402..4674abaea0 100644 --- a/scripts/utils.ts +++ b/scripts/utils.ts @@ -53,24 +53,29 @@ export function normalizeURL(url: string): string { } } +function isInsideDirectory(rootDir: string, target: string): boolean { + const relative = path.relative(rootDir, target) + + return ( + relative !== '' && + relative !== '..' && + !relative.startsWith(`..${path.sep}`) && + !path.isAbsolute(relative) + ) +} + export function getStoragePath(filepath: string, rootDir: string): string { const root = path.resolve(rootDir) - const candidate = path.resolve(filepath) - const candidateRelative = path.relative(root, candidate) - const target = - path.isAbsolute(filepath) || - (candidateRelative !== '..' && - !candidateRelative.startsWith(`..${path.sep}`) && - !path.isAbsolute(candidateRelative)) - ? candidate - : path.resolve(root, filepath) - const relative = path.relative(root, target) + const fromWorkspace = path.resolve(filepath) + const target = isInsideDirectory(root, fromWorkspace) + ? fromWorkspace + : path.resolve(root, filepath) - if (relative === '..' || relative.startsWith(`..${path.sep}`) || path.isAbsolute(relative)) { + if (!isInsideDirectory(root, target)) { throw new Error(`Filepath "${filepath}" is outside the storage directory`) } - return relative + return path.relative(root, target).split(path.sep).join('/') } export function truncate(string: string, limit: number = 100) { diff --git a/tests/__data__/input/playlist_format/nested/nested.m3u b/tests/__data__/input/playlist_format/nested/nested.m3u index 9e54864def..34fe6f1cce 100644 --- a/tests/__data__/input/playlist_format/nested/nested.m3u +++ b/tests/__data__/input/playlist_format/nested/nested.m3u @@ -1,5 +1,5 @@ -#EXTM3U -#EXTINF:-1 tvg-id="",Zulu (720p) -https://example.com/zulu.m3u8 -#EXTINF:-1 tvg-id="",Alpha (720p) -https://example.com/alpha.m3u8 +#EXTM3U +#EXTINF:-1 tvg-id="",Zulu (720p) +https://example.com/zulu.m3u8 +#EXTINF:-1 tvg-id="",Alpha (720p) +https://example.com/alpha.m3u8