Apply all CODE-AUDIT.md findings (S1-S5, P1-P3, M1-M3)
Security: - S1 (backup.ts): require explicit confirmation before enabling custom-command templates from a backup file; import templates in a disabled state if declined - S2 (cookies.ts): deny non-http/https popups in cookie login window, matching the main window's setWindowOpenHandler defence - S3 (download.ts): main-process concurrency cap — startDownload now rejects spawns when active.size >= maxConcurrent (defence-in-depth; renderer already enforces this but should not be the only gate) - S4 (settings.ts, validation.ts): reject filenameTemplate values containing path traversal (.. segments or absolute paths); validate outputDir is absolute - S5 (history.ts, errorlog.ts, templates.ts, validation.ts): per-field validation on JSON reads — invalid entries dropped rather than blindly trusted Performance: - P1 (settings.ts): getSettings() now only writes to disk when sanitization actually changed a value; removes synchronous file I/O on every hot-path read - P2 (ipc.ts, download.ts, downloads.ts): renderer forwards its pre-probed metadata (title/channel/duration) via StartDownloadOptions.meta; main uses it directly instead of spawning a redundant second yt-dlp probe Maintainability: - M1 (log.ts, download.ts, probe.ts): extract duplicated cleanError() to src/main/log.ts; both callers import from the shared module - M2 (buildArgs.ts): document parseExtraArgs escape-sequence limitations - M3 (electron-builder.yml): clarify icon TODO as a pre-release action - P3 (downloads.ts): document that lowering maxConcurrent mid-flight does not pause active downloads (intended behaviour, now written down) Tests: - Add test/validation.test.ts covering isSafeFilenameTemplate, isSafeOutputDir, isValidHistoryEntry, isValidErrorLogEntry, isTemplateLike (76 tests pass) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
+60
-2
@@ -51,7 +51,65 @@ export async function importBackup(win: BrowserWindow | undefined): Promise<Back
|
||||
// hand-edited or partial backup file degrades gracefully rather than
|
||||
// corrupting the store.
|
||||
const file = parsed as Partial<BackupFile>
|
||||
if (file.settings && typeof file.settings === 'object') setSettings(file.settings)
|
||||
if (Array.isArray(file.templates)) replaceTemplates(file.templates)
|
||||
const incomingSettings =
|
||||
file.settings && typeof file.settings === 'object'
|
||||
? (file.settings as Partial<Settings>)
|
||||
: undefined
|
||||
const incomingTemplates = Array.isArray(file.templates)
|
||||
? (file.templates as CommandTemplate[])
|
||||
: []
|
||||
|
||||
// A custom-command template's `args` are extra yt-dlp flags that get spawned on
|
||||
// the next download. A backup that arrives with customCommandEnabled + a default
|
||||
// template carrying args would silently enable code execution the moment a
|
||||
// download starts — so require an explicit, informed confirmation before
|
||||
// honouring that, and import the templates in a *disabled* state if declined.
|
||||
const commandTemplates = incomingTemplates.filter(
|
||||
(t) => t && typeof t === 'object' && typeof t.args === 'string' && t.args.trim() !== ''
|
||||
)
|
||||
const wouldEnableCustomCommands =
|
||||
incomingSettings?.customCommandEnabled === true && commandTemplates.length > 0
|
||||
|
||||
let applyCustomCommands = true
|
||||
if (wouldEnableCustomCommands) {
|
||||
const preview = commandTemplates
|
||||
.slice(0, 10)
|
||||
.map((t) => {
|
||||
const name = (t.name ?? '').trim() || 'Untitled command'
|
||||
const args = t.args.length > 100 ? `${t.args.slice(0, 100)}…` : t.args
|
||||
return `• ${name}: ${args}`
|
||||
})
|
||||
.join('\n')
|
||||
const more =
|
||||
commandTemplates.length > 10 ? `\n…and ${commandTemplates.length - 10} more.` : ''
|
||||
const choice = await dialog.showMessageBox(win!, {
|
||||
type: 'warning',
|
||||
buttons: ['Enable custom commands', 'Import without enabling', 'Cancel'],
|
||||
defaultId: 1,
|
||||
cancelId: 2,
|
||||
title: 'Backup contains custom commands',
|
||||
message: `This backup enables ${commandTemplates.length} custom-command template${
|
||||
commandTemplates.length === 1 ? '' : 's'
|
||||
}.`,
|
||||
detail:
|
||||
'Custom commands run extra yt-dlp flags on every download. Only enable them if you trust this backup file.\n\n' +
|
||||
preview +
|
||||
more
|
||||
})
|
||||
if (choice.response === 2) return { ok: false } // Cancel — change nothing
|
||||
applyCustomCommands = choice.response === 0
|
||||
}
|
||||
|
||||
if (incomingSettings) {
|
||||
// When the user declined to enable custom commands (or there were none to
|
||||
// enable), force the toggle off so an imported defaultTemplateId can't auto-run.
|
||||
const safeSettings = applyCustomCommands
|
||||
? incomingSettings
|
||||
: { ...incomingSettings, customCommandEnabled: false }
|
||||
setSettings(safeSettings)
|
||||
}
|
||||
if (incomingTemplates.length > 0 || Array.isArray(file.templates)) {
|
||||
replaceTemplates(incomingTemplates)
|
||||
}
|
||||
return { ok: true }
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user