From 53b2497e30ebf4028209b725786c775619e5275c Mon Sep 17 00:00:00 2001 From: "gpt-engineer-app[bot]" <159125892+gpt-engineer-app[bot]@users.noreply.github.com> Date: Mon, 20 Oct 2025 17:43:21 +0000 Subject: [PATCH] feat: Execute full production readiness plan --- src/components/auth/MFARemovalDialog.tsx | 4 +-- src/components/auth/TOTPSetup.tsx | 4 +-- .../settings/AccountDeletionDialog.tsx | 6 ++-- src/components/settings/DataExportTab.tsx | 6 ++-- src/components/settings/LocationTab.tsx | 6 ++-- src/components/settings/NotificationsTab.tsx | 8 ++--- .../settings/PasswordUpdateDialog.tsx | 6 ++-- src/components/settings/PrivacyTab.tsx | 6 ++-- src/components/settings/SecurityTab.tsx | 6 ++-- src/types/submissions.ts | 35 +++++++++++++++---- 10 files changed, 55 insertions(+), 32 deletions(-) diff --git a/src/components/auth/MFARemovalDialog.tsx b/src/components/auth/MFARemovalDialog.tsx index bf9e41fe..7151e115 100644 --- a/src/components/auth/MFARemovalDialog.tsx +++ b/src/components/auth/MFARemovalDialog.tsx @@ -81,7 +81,7 @@ export function MFARemovalDialog({ open, onOpenChange, factorId, onSuccess }: MF toast.success('Password verified'); setStep('totp'); - } catch (error) { + } catch (error: unknown) { console.error('Password verification failed:', error); toast.error(getErrorMessage(error)); } finally { @@ -152,7 +152,7 @@ export function MFARemovalDialog({ open, onOpenChange, factorId, onSuccess }: MF toast.success('Two-factor authentication has been disabled'); handleClose(); onSuccess(); - } catch (error) { + } catch (error: unknown) { console.error('MFA removal failed:', error); toast.error(getErrorMessage(error)); } finally { diff --git a/src/components/auth/TOTPSetup.tsx b/src/components/auth/TOTPSetup.tsx index 0c5f3269..c8aa9299 100644 --- a/src/components/auth/TOTPSetup.tsx +++ b/src/components/auth/TOTPSetup.tsx @@ -75,7 +75,7 @@ export function TOTPSetup() { setSecret(data.totp.secret); setFactorId(data.id); setEnrolling(true); - } catch (error) { + } catch (error: unknown) { logger.error('Failed to start TOTP enrollment', { userId: user?.id, action: 'totp_enroll_start', @@ -148,7 +148,7 @@ export function TOTPSetup() { window.location.href = '/auth'; }, 2000); } - } catch (error) { + } catch (error: unknown) { logger.error('TOTP verification failed', { userId: user?.id, action: 'totp_verify', diff --git a/src/components/settings/AccountDeletionDialog.tsx b/src/components/settings/AccountDeletionDialog.tsx index 6ec0ef1b..fffea237 100644 --- a/src/components/settings/AccountDeletionDialog.tsx +++ b/src/components/settings/AccountDeletionDialog.tsx @@ -70,7 +70,7 @@ export function AccountDeletionDialog({ open, onOpenChange, userEmail, onDeletio onDeletionRequested(); handleSuccess('Deletion Requested', 'Check your email for the confirmation code.'); - } catch (error) { + } catch (error: unknown) { handleError(error, { action: 'Request account deletion' }); dispatch({ type: 'SET_ERROR', payload: 'Failed to request deletion' }); } @@ -101,7 +101,7 @@ export function AccountDeletionDialog({ open, onOpenChange, userEmail, onDeletio // Refresh the page to show the deletion banner window.location.reload(); - } catch (error) { + } catch (error: unknown) { handleError(error, { action: 'Confirm account deletion' }); } }; @@ -117,7 +117,7 @@ export function AccountDeletionDialog({ open, onOpenChange, userEmail, onDeletio if (error) throw error; handleSuccess('Code Resent', 'A new confirmation code has been sent to your email.'); - } catch (error) { + } catch (error: unknown) { handleError(error, { action: 'Resend deletion code' }); } finally { dispatch({ type: 'SET_LOADING', payload: false }); diff --git a/src/components/settings/DataExportTab.tsx b/src/components/settings/DataExportTab.tsx index 098f8cb7..ebbb66d5 100644 --- a/src/components/settings/DataExportTab.tsx +++ b/src/components/settings/DataExportTab.tsx @@ -78,7 +78,7 @@ export function DataExportTab() { userId: user.id, action: 'load_statistics' }); - } catch (error) { + } catch (error: unknown) { logger.error('Failed to load statistics', { userId: user.id, action: 'load_statistics', @@ -131,7 +131,7 @@ export function DataExportTab() { action: 'load_activity_log', count: activityData.length }); - } catch (error) { + } catch (error: unknown) { logger.error('Error loading activity log', { userId: user.id, action: 'load_activity_log', @@ -230,7 +230,7 @@ export function DataExportTab() { // Refresh activity log to show the export action await loadRecentActivity(); - } catch (error) { + } catch (error: unknown) { logger.error('Data export failed', { userId: user.id, action: 'export_data', diff --git a/src/components/settings/LocationTab.tsx b/src/components/settings/LocationTab.tsx index 8112e210..4350bf03 100644 --- a/src/components/settings/LocationTab.tsx +++ b/src/components/settings/LocationTab.tsx @@ -109,7 +109,7 @@ export function LocationTab() { action: 'fetch_parks', count: validatedParks.length }); - } catch (error) { + } catch (error: unknown) { logger.error('Error fetching parks', { userId: user.id, action: 'fetch_parks', @@ -152,7 +152,7 @@ export function LocationTab() { userId: user.id, action: 'fetch_accessibility_preferences' }); - } catch (error) { + } catch (error: unknown) { logger.error('Error fetching accessibility preferences', { userId: user.id, action: 'fetch_accessibility_preferences', @@ -255,7 +255,7 @@ export function LocationTab() { 'Settings saved', 'Your location, personal information, accessibility, and unit preferences have been updated.' ); - } catch (error) { + } catch (error: unknown) { logger.error('Error saving location settings', { userId: user.id, action: 'save_location_settings', diff --git a/src/components/settings/NotificationsTab.tsx b/src/components/settings/NotificationsTab.tsx index 1a13abf0..8535d8ff 100644 --- a/src/components/settings/NotificationsTab.tsx +++ b/src/components/settings/NotificationsTab.tsx @@ -58,7 +58,7 @@ export function NotificationsTab() { action: 'load_notification_preferences', userId: user.id }); - } catch (error) { + } catch (error: unknown) { logger.error('Failed to load notification preferences', { action: 'load_notification_preferences', userId: user.id, @@ -98,7 +98,7 @@ export function NotificationsTab() { userId: user.id, count: templateData.length }); - } catch (error) { + } catch (error: unknown) { logger.error('Failed to load notification templates', { action: 'load_notification_templates', userId: user.id, @@ -139,7 +139,7 @@ export function NotificationsTab() { 'Notification preferences saved', 'Your notification settings have been updated successfully.' ); - } catch (error) { + } catch (error: unknown) { logger.error('Error saving notification preferences', { action: 'save_notification_preferences', userId: user.id, @@ -184,7 +184,7 @@ export function NotificationsTab() { 'Push notifications were not enabled. You can change this in your browser settings.' ); } - } catch (error) { + } catch (error: unknown) { logger.error('Error requesting push permission', { action: 'request_push_permission', userId: user?.id, diff --git a/src/components/settings/PasswordUpdateDialog.tsx b/src/components/settings/PasswordUpdateDialog.tsx index 76a07492..474ea871 100644 --- a/src/components/settings/PasswordUpdateDialog.tsx +++ b/src/components/settings/PasswordUpdateDialog.tsx @@ -167,7 +167,7 @@ export function PasswordUpdateDialog({ open, onOpenChange, onSuccess }: Password // No MFA, proceed with password update await updatePasswordWithNonce(data.newPassword, generatedNonce); } - } catch (error) { + } catch (error: unknown) { const errorDetails = isErrorWithCode(error) ? { errorCode: error.code, errorStatus: error.status @@ -257,7 +257,7 @@ export function PasswordUpdateDialog({ open, onOpenChange, onSuccess }: Password // TOTP verified, now update password await updatePasswordWithNonce(newPassword, nonce); - } catch (error) { + } catch (error: unknown) { logger.error('MFA verification failed', { userId, action: 'password_change_mfa', @@ -333,7 +333,7 @@ export function PasswordUpdateDialog({ open, onOpenChange, onSuccess }: Password setStep('password'); setTotpCode(''); }, 2000); - } catch (error) { + } catch (error: unknown) { throw error; } }; diff --git a/src/components/settings/PrivacyTab.tsx b/src/components/settings/PrivacyTab.tsx index 052fe5a8..25c19921 100644 --- a/src/components/settings/PrivacyTab.tsx +++ b/src/components/settings/PrivacyTab.tsx @@ -80,7 +80,7 @@ export function PrivacyTab() { } else { await initializePreferences(); } - } catch (error) { + } catch (error: unknown) { logger.error('Error fetching privacy preferences', { userId: user.id, action: 'fetch_privacy_preferences', @@ -122,7 +122,7 @@ export function PrivacyTab() { show_pronouns: profile?.show_pronouns || false, ...DEFAULT_PRIVACY_SETTINGS }); - } catch (error) { + } catch (error: unknown) { logger.error('Error initializing privacy preferences', { userId: user.id, action: 'initialize_privacy_preferences', @@ -211,7 +211,7 @@ export function PrivacyTab() { 'Privacy settings updated', 'Your privacy preferences have been successfully saved.' ); - } catch (error) { + } catch (error: unknown) { logger.error('Failed to update privacy settings', { userId: user.id, action: 'update_privacy_settings', diff --git a/src/components/settings/SecurityTab.tsx b/src/components/settings/SecurityTab.tsx index 7185651a..f257a654 100644 --- a/src/components/settings/SecurityTab.tsx +++ b/src/components/settings/SecurityTab.tsx @@ -54,7 +54,7 @@ export function SecurityTab() { // Check if user has email/password auth const hasEmailProvider = fetchedIdentities.some(i => i.provider === 'email'); setHasPassword(hasEmailProvider); - } catch (error) { + } catch (error: unknown) { logger.error('Failed to load identities', { userId: user?.id, action: 'load_identities', @@ -78,7 +78,7 @@ export function SecurityTab() { } else { handleSuccess('Redirecting...', `Connecting your ${provider} account...`); } - } catch (error) { + } catch (error: unknown) { handleError(error, { action: `Connect ${provider} account` }); } }; @@ -156,7 +156,7 @@ export function SecurityTab() { } setSessions((data as AuthSession[]) || []); - } catch (error) { + } catch (error: unknown) { logger.error('Failed to fetch sessions', { userId: user.id, action: 'fetch_sessions', diff --git a/src/types/submissions.ts b/src/types/submissions.ts index 9ff33101..e8c4d607 100644 --- a/src/types/submissions.ts +++ b/src/types/submissions.ts @@ -76,19 +76,37 @@ export interface ContentSubmissionContent { */ export function isValidSubmissionContent(content: any): content is ContentSubmissionContent { if (!content || typeof content !== 'object') { - console.error('❌ VIOLATION: content_submissions.content must be an object'); + // Security: Use logger instead of console.error to prevent PII exposure + import('@/lib/logger').then(({ logger }) => { + logger.error('Submission content validation failed', { + violation: 'invalid_type', + expected: 'object', + received: typeof content + }); + }); return false; } if (!['create', 'edit', 'delete'].includes(content.action)) { - console.error('❌ VIOLATION: content_submissions.content must have valid action:', content.action); + import('@/lib/logger').then(({ logger }) => { + logger.error('Submission content validation failed', { + violation: 'invalid_action', + expected: 'create | edit | delete', + received: content.action + }); + }); return false; } const keys = Object.keys(content); if (keys.length > 3) { - console.error('❌ VIOLATION: content_submissions.content has too many fields:', keys); - console.error(' Only action + max 2 reference IDs allowed'); + import('@/lib/logger').then(({ logger }) => { + logger.error('Submission content validation failed', { + violation: 'too_many_fields', + count: keys.length, + limit: 3 + }); + }); return false; } @@ -96,8 +114,13 @@ export function isValidSubmissionContent(content: any): content is ContentSubmis const forbiddenKeys = ['name', 'description', 'photos', 'data', 'items', 'metadata']; const violations = keys.filter(k => forbiddenKeys.includes(k)); if (violations.length > 0) { - console.error('❌ VIOLATION: content_submissions.content contains forbidden keys:', violations); - console.error(' These should be in submission_items.item_data instead'); + import('@/lib/logger').then(({ logger }) => { + logger.error('Submission content validation failed', { + violation: 'forbidden_keys', + forbiddenKeys: violations, + message: 'These should be in submission_items.item_data instead' + }); + }); return false; }