From 781f41ef1f6b5054e75f77237df9eba3b7f61cc9 Mon Sep 17 00:00:00 2001 From: Julius Marminge Date: Fri, 4 Sep 2026 13:33:23 -0700 Subject: [PATCH] fix(server): refuse symlinked theme files on Windows too (#9580) Co-authored-by: Claude Code --- apps/server/src/environmentTheme.ts | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/apps/server/src/environmentTheme.ts b/apps/server/src/environmentTheme.ts index c038af065bdc5baa751fb7ffc24b70dc394f8bc2..b7eb11940cea83b1b68422a3f1c5273546319c61 100644 --- a/apps/server/src/environmentTheme.ts +++ b/apps/server/src/environmentTheme.ts @@ -95,13 +95,23 @@ export class EnvironmentThemeService extends Context.Service< * usable, a symlinked file inside it does not), O_NONBLOCK keeps a FIFO from * blocking the open, and the fstat type and size gate examines the open * descriptor. Returns null for anything that is not a small regular file. + * + * Windows has neither flag (the constants are undefined, and OR-ing them in + * is a no-op), so the symlink check there is an lstat before the open. That + * leaves a window a swap could slip through, which the descriptor-bound + * checks below then narrow to "a regular file at that path". */ export const readThemeFileGuarded = (filePath: string, maxBytes: number): string | null => { let fd: number; try { + if (NodeFS.constants.O_NOFOLLOW === undefined && NodeFS.lstatSync(filePath).isSymbolicLink()) { + return null; + } fd = NodeFS.openSync( filePath, - NodeFS.constants.O_RDONLY | NodeFS.constants.O_NOFOLLOW | NodeFS.constants.O_NONBLOCK, + NodeFS.constants.O_RDONLY | + (NodeFS.constants.O_NOFOLLOW ?? 0) | + (NodeFS.constants.O_NONBLOCK ?? 0), ); } catch { return null; -- 2.54.0