Files
Multi-Debrid-Downloader/src/renderer/backup-flow.ts
T
Sucukdeluxe 1cb381fa50 fix(security): require passphrases for local backups
Derive every MDD2 key from a non-empty user passphrase and the per-backup scrypt salt while keeping the embedded application material isolated to read-only MDD1 migration imports. Normalize missing, wrong, and authentication-failure results to the same controlled decryption error.

Add the modal-based export confirmation and format-aware import flow across renderer, preload, IPC, and controller boundaries. Clear transient passphrase state on completion or cancellation, retain pending import data only until consumption, and keep passphrases out of results, snapshots, payloads, and logs.

Cover mismatch and cancellation paths, MDD1 passphrase-free migration, preload forwarding, successful UI/crypto round-trips, one-byte-short ciphertext, authenticated empty ciphertext, and truncated legacy envelopes.
2026-08-11 22:32:58 +02:00

67 lines
1.9 KiB
TypeScript

export interface BackupSelectionResult {
selected: boolean;
requiresPassphrase: boolean;
message?: string;
}
export interface BackupImportResult {
restored: boolean;
relaunch: boolean;
message: string;
}
export interface LocalBackupApi {
exportBackup: (passphrase: string) => Promise<{ saved: boolean }>;
selectBackupImport: () => Promise<BackupSelectionResult>;
importBackup: (passphrase?: string) => Promise<BackupImportResult>;
cancelBackupImport: () => Promise<void>;
}
export type BackupPassphraseMode = "export" | "import";
export type BackupPassphraseRequest = (mode: BackupPassphraseMode) => Promise<string | null>;
export function validateBackupPassphrase(
mode: BackupPassphraseMode,
passphrase: string,
confirmation: string
): string | null {
if (passphrase.trim().length === 0) {
return "Bitte eine Passphrase eingeben";
}
if (mode === "export" && passphrase !== confirmation) {
return "Die Passphrasen stimmen nicht überein";
}
return null;
}
export async function runLocalBackupExport(
api: LocalBackupApi,
requestPassphrase: BackupPassphraseRequest
): Promise<{ saved: boolean }> {
const passphrase = await requestPassphrase("export");
if (passphrase === null) {
return { saved: false };
}
return api.exportBackup(passphrase);
}
export async function runLocalBackupImport(
api: LocalBackupApi,
requestPassphrase: BackupPassphraseRequest
): Promise<BackupImportResult> {
const selection = await api.selectBackupImport();
if (!selection.selected) {
return { restored: false, relaunch: false, message: selection.message || "Abgebrochen" };
}
let passphrase: string | undefined;
if (selection.requiresPassphrase) {
const requested = await requestPassphrase("import");
if (requested === null) {
await api.cancelBackupImport();
return { restored: false, relaunch: false, message: "Abgebrochen" };
}
passphrase = requested;
}
return api.importBackup(passphrase);
}