diff --git a/src/commands/persist-command/backup-rotation.ts b/src/commands/persist-command/backup-rotation.ts index deba4449..2561ff0a 100644 --- a/src/commands/persist-command/backup-rotation.ts +++ b/src/commands/persist-command/backup-rotation.ts @@ -11,6 +11,7 @@ import * as path from 'path'; import { initUI, header, color, dim, ok, fail, warn, info } from '../../utils/ui'; import { InteractivePrompt } from '../../utils/prompt'; import { getClaudeSettingsPath } from '../../utils/claude-config-path'; +import { ConfigError } from '../../errors'; import { formatDisplayPath, getClaudeSettingsDisplayPath, @@ -86,6 +87,14 @@ export async function createBackup(): Promise { throw new Error('No settings.json to backup'); } + // Guard: refuse to read a symlinked settings.json (TOCTOU mitigation on backup read). + if (await isSymlinkAsync(settingsPath)) { + throw new ConfigError( + 'settings.json is a symlink - refusing to read for backup for security', + settingsPath + ); + } + const settingsContent = await readFileUtf8NoFollow(settingsPath); const now = new Date();