diff --git a/frontend-modern/src/components/Settings/Settings.tsx b/frontend-modern/src/components/Settings/Settings.tsx index 48a9efe..b5e2438 100644 --- a/frontend-modern/src/components/Settings/Settings.tsx +++ b/frontend-modern/src/components/Settings/Settings.tsx @@ -1902,10 +1902,14 @@ const Settings: Component = (props) => { return; } - // Just require a passphrase to be entered - const hasAuth = securityStatus()?.hasAuthentication; - if ((!hasAuth || useCustomPassphrase()) && !exportPassphrase()) { - showError('Please enter a passphrase'); + // Backend requires at least 12 characters for encryption security + if (exportPassphrase().length < 12) { + const hasAuth = securityStatus()?.hasAuthentication; + showError( + hasAuth && !useCustomPassphrase() + ? 'Your password must be at least 12 characters. Please use a custom passphrase instead.' + : 'Passphrase must be at least 12 characters long', + ); return; } @@ -2029,6 +2033,21 @@ const Settings: Component = (props) => { return; } + // Support both formats: + // 1. New format: {status: "success", data: "base64string"} + // 2. Legacy/CLI format: raw base64 string or {data: "base64string"} + let encryptedData: string; + if (typeof exportData === 'string') { + // Raw base64 string from CLI export + encryptedData = exportData; + } else if (exportData.data) { + // Standard format with data field + encryptedData = exportData.data; + } else { + showError('Invalid backup file format. Expected encrypted data in "data" field.'); + return; + } + // Get CSRF token from cookie const csrfToken = document.cookie .split('; ') @@ -2056,7 +2075,7 @@ const Settings: Component = (props) => { credentials: 'include', // Include cookies for session auth body: JSON.stringify({ passphrase: importPassphrase(), - data: exportData.data, + data: encryptedData, }), }); diff --git a/internal/api/config_handlers.go b/internal/api/config_handlers.go index 2c94434..08d13ce 100644 --- a/internal/api/config_handlers.go +++ b/internal/api/config_handlers.go @@ -2910,12 +2910,14 @@ func (h *ConfigHandlers) HandleExportConfig(w http.ResponseWriter, r *http.Reque } if req.Passphrase == "" { + log.Warn().Msg("Export rejected: passphrase is required") http.Error(w, "Passphrase is required", http.StatusBadRequest) return } // Require strong passphrase (at least 12 characters) if len(req.Passphrase) < 12 { + log.Warn().Int("length", len(req.Passphrase)).Msg("Export rejected: passphrase too short (minimum 12 characters)") http.Error(w, "Passphrase must be at least 12 characters long", http.StatusBadRequest) return } @@ -2947,11 +2949,13 @@ func (h *ConfigHandlers) HandleImportConfig(w http.ResponseWriter, r *http.Reque } if req.Passphrase == "" { + log.Warn().Msg("Import rejected: passphrase is required") http.Error(w, "Passphrase is required", http.StatusBadRequest) return } if req.Data == "" { + log.Warn().Msg("Import rejected: encrypted data is required (ensure backup file has 'data' field)") http.Error(w, "Import data is required", http.StatusBadRequest) return }