(filePath: string, content: string)
| 159 | } |
| 160 | |
| 161 | export async function writeFileContent(filePath: string, content: string): Promise<void> { |
| 162 | try { |
| 163 | // Security: 'wx' flag ensures exclusive creation - fails if file/symlink exists, |
| 164 | // preventing writes through pre-existing symlinks |
| 165 | await fs.writeFile(filePath, content, { encoding: "utf-8", flag: 'wx' }); |
| 166 | } catch (error) { |
| 167 | if ((error as NodeJS.ErrnoException).code === 'EEXIST') { |
| 168 | // Security: Use atomic rename to prevent race conditions where symlinks |
| 169 | // could be created between validation and write. Rename operations |
| 170 | // replace the target file atomically and don't follow symlinks. |
| 171 | const tempPath = `${filePath}.${randomBytes(16).toString('hex')}.tmp`; |
| 172 | try { |
| 173 | await fs.writeFile(tempPath, content, 'utf-8'); |
| 174 | await fs.rename(tempPath, filePath); |
| 175 | } catch (renameError) { |
| 176 | try { |
| 177 | await fs.unlink(tempPath); |
| 178 | } catch {} |
| 179 | throw renameError; |
| 180 | } |
| 181 | } else { |
| 182 | throw error; |
| 183 | } |
| 184 | } |
| 185 | } |
| 186 | |
| 187 | |
| 188 | // File Editing Functions |
no outgoing calls
no test coverage detected