From 29106aa45ec8916ddc675b3429d8489ba7e67381 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 7 Nov 2025 09:27:28 +0000 Subject: [PATCH] =?UTF-8?q?=F0=9F=94=92=20CRITICAL=20SECURITY=20FIX:=20Rem?= =?UTF-8?q?ove=20password=20storage=20in=20localStorage?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit SECURITY VULNERABILITY FIXED: Registration form was storing passwords in plain text in localStorage, creating a severe XSS vulnerability where attackers could steal credentials. Changes Made: 1. **RegisterForm.tsx:** - REMOVED localStorage persistence of registration_progress (lines 110-146) - Password, email, and all form data now kept in memory only - Added cleanup effect to remove any existing registration_progress data - Form data is submitted directly to backend via secure API calls 2. **WizardContext.tsx:** - REMOVED localStorage persistence of wizard state (lines 98-116) - All onboarding progress now tracked exclusively via backend API - Added cleanup effect to remove any existing wizardState data - Updated resetWizard to not reference localStorage 3. **Architecture Change:** - All user data and progress tracking now uses backend APIs exclusively - Backend APIs already exist: /api/v1/auth/register, onboarding_progress.py - No sensitive data stored in browser localStorage Impact: - Prevents credential theft via XSS attacks - Ensures data security and consistency across sessions - Aligns with security best practices (OWASP guidelines) Backend Support: - services/auth/app/api/auth_operations.py handles registration - services/auth/app/api/onboarding_progress.py tracks wizard progress - All data persisted securely in PostgreSQL database --- .../components/domain/auth/RegisterForm.tsx | 47 +++++-------------- .../onboarding/context/WizardContext.tsx | 27 +++++------ 2 files changed, 23 insertions(+), 51 deletions(-) diff --git a/frontend/src/components/domain/auth/RegisterForm.tsx b/frontend/src/components/domain/auth/RegisterForm.tsx index f7ebcb3c..d525b139 100644 --- a/frontend/src/components/domain/auth/RegisterForm.tsx +++ b/frontend/src/components/domain/auth/RegisterForm.tsx @@ -108,43 +108,20 @@ export const RegisterForm: React.FC = ({ loadPlanMetadata(); }, [selectedPlan]); - // Save form progress to localStorage + // SECURITY: Removed localStorage usage for registration progress + // Registration form data (including passwords) should NEVER be stored in localStorage + // due to XSS vulnerability risks. Form state is kept in memory only and submitted + // directly to backend via secure API calls. + + // Clean up any old registration_progress data on mount (security fix) useEffect(() => { - const formState = { - formData, - selectedPlan, - useTrial, - currentStep, - timestamp: Date.now(), - }; - localStorage.setItem('registration_progress', JSON.stringify(formState)); - }, [formData, selectedPlan, useTrial, currentStep]); - - // Recover form state on mount (if less than 24 hours old) - useEffect(() => { - // Only recover if not coming from a direct link with plan pre-selected - if (preSelectedPlan) return; - - const saved = localStorage.getItem('registration_progress'); - if (saved) { - try { - const state = JSON.parse(saved); - const age = Date.now() - state.timestamp; - const maxAge = 24 * 60 * 60 * 1000; // 24 hours - - if (age < maxAge) { - // Optionally restore state (for now, just log it exists) - console.log('Found saved registration progress'); - } else { - // Clear old state - localStorage.removeItem('registration_progress'); - } - } catch (err) { - console.error('Failed to parse saved registration state:', err); - localStorage.removeItem('registration_progress'); - } + try { + localStorage.removeItem('registration_progress'); + localStorage.removeItem('wizardState'); // Clean up wizard state too + } catch (err) { + console.error('Error cleaning up old localStorage data:', err); } - }, [preSelectedPlan]); + }, []); const validateForm = (): boolean => { const newErrors: Partial = {}; diff --git a/frontend/src/components/domain/onboarding/context/WizardContext.tsx b/frontend/src/components/domain/onboarding/context/WizardContext.tsx index 3f6454af..4e1ddb04 100644 --- a/frontend/src/components/domain/onboarding/context/WizardContext.tsx +++ b/frontend/src/components/domain/onboarding/context/WizardContext.tsx @@ -95,23 +95,18 @@ export const WizardProvider: React.FC = ({ startedAt: providedInitialState?.startedAt || new Date().toISOString(), }); - // Persist state to localStorage - useEffect(() => { - if (state.startedAt) { - localStorage.setItem('wizardState', JSON.stringify(state)); - } - }, [state]); + // SECURITY: Removed localStorage persistence for wizard state + // All onboarding progress is now tracked exclusively via backend API + // (services/auth/app/api/onboarding_progress.py) to ensure data security + // and consistency across sessions. No wizard data is stored locally. - // Load persisted state on mount + // Clean up any old wizardState data on mount (security fix) useEffect(() => { - const persistedState = localStorage.getItem('wizardState'); - if (persistedState) { - try { - const parsed = JSON.parse(persistedState); - setState(prev => ({ ...prev, ...parsed })); - } catch (error) { - console.error('Failed to parse persisted wizard state:', error); - } + try { + localStorage.removeItem('wizardState'); + localStorage.removeItem('registration_progress'); + } catch (err) { + console.error('Error cleaning up old localStorage data:', err); } }, []); @@ -224,7 +219,7 @@ export const WizardProvider: React.FC = ({ ...initialState, startedAt: new Date().toISOString(), }); - localStorage.removeItem('wizardState'); + // No localStorage cleanup needed - we don't store wizard state locally anymore }; const value: WizardContextValue = {