authorgravatar for julius0216@outlook.comJulius Marminge <julius0216@outlook.com> 2026-09-04 13:33:23-07:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2026-09-04 20:33:23+00:00
log781f41ef1f6b5054e75f77237df9eba3b7f61cc9
tree24df483752ab90091e15a8e024ce2bcf5754ce17
parentb123cbb317ca47ff080a15b44c4bc0c99126f729
signature Signed by PGP key B5690EEEBB952194

fix(server): refuse symlinked theme files on Windows too (#9580)

Co-authored-by: Claude Code <noreply@anthropic.com>

1 files changed, 11 insertions(+), 1 deletions(-)

apps/server/src/environmentTheme.ts+11-1
...@@ -95,13 +95,23 @@ export class EnvironmentThemeService extends Context.Service<...@@ -95,13 +95,23 @@ export class EnvironmentThemeService extends Context.Service<
95 * usable, a symlinked file inside it does not), O_NONBLOCK keeps a FIFO from95 * usable, a symlinked file inside it does not), O_NONBLOCK keeps a FIFO from
96 * blocking the open, and the fstat type and size gate examines the open96 * blocking the open, and the fstat type and size gate examines the open
97 * descriptor. Returns null for anything that is not a small regular file.97 * descriptor. Returns null for anything that is not a small regular file.
98 *
99 * Windows has neither flag (the constants are undefined, and OR-ing them in
100 * is a no-op), so the symlink check there is an lstat before the open. That
101 * leaves a window a swap could slip through, which the descriptor-bound
102 * checks below then narrow to "a regular file at that path".
98 */103 */
99export const readThemeFileGuarded = (filePath: string, maxBytes: number): string | null => {104export const readThemeFileGuarded = (filePath: string, maxBytes: number): string | null => {
100 let fd: number;105 let fd: number;
101 try {106 try {
107 if (NodeFS.constants.O_NOFOLLOW === undefined && NodeFS.lstatSync(filePath).isSymbolicLink()) {
108 return null;
109 }
102 fd = NodeFS.openSync(110 fd = NodeFS.openSync(
103 filePath,111 filePath,
104 NodeFS.constants.O_RDONLY | NodeFS.constants.O_NOFOLLOW | NodeFS.constants.O_NONBLOCK,112 NodeFS.constants.O_RDONLY |
113 (NodeFS.constants.O_NOFOLLOW ?? 0) |
114 (NodeFS.constants.O_NONBLOCK ?? 0),
105 );115 );
106 } catch {116 } catch {
107 return null;117 return null;