| ... | @@ -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 from | 95 | * 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 open | 96 | * 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 | */ |
| 99 | export const readThemeFileGuarded = (filePath: string, maxBytes: number): string | null => { | 104 | export 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; |