Skip to content

Commit 48d6dd7

Browse files
fix(auth,dashboard): pr #194 coderabbit remediation (sfU + se1)
sfU (account enumeration): the signUp fallback in `handleFormSubmit` surfaces raw Supabase error messages. When an attacker enters a registered email with a wrong password, signIn fails with "Invalid login credentials" and the code falls back to signUp, which throws "User already registered" — leaking account existence to the caller. Detect that class of message via regex and surface the same generic credential error as a failed login, so the error reveals nothing. se1 (stale dashboard after save): `registrarHistoricoTreinoAction` calls `revalidatePath('/aluno/dashboard')`, which invalidates the cache but does not update this already-mounted client component's `aluno` props. Add `router.refresh()` after a successful save so the RSC payload is re-fetched and XP/level/streak/treinosNoMes reflect the new state without a manual reload. Mock `next/navigation` in `dashboard-client.test.tsx` to satisfy `useRouter`. Tests: 1151 pass, 0 fail. Typecheck + lint clean. Co-Authored-By: Claude <noreply@anthropic.com>
1 parent f07f39f commit 48d6dd7

3 files changed

Lines changed: 17 additions & 1 deletion

File tree

src/app/aluno/dashboard/dashboard-client.test.tsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,10 @@ vi.mock('motion/react', () => ({
2727
},
2828
}));
2929

30+
vi.mock('next/navigation', () => ({
31+
useRouter: () => ({ refresh: vi.fn() }),
32+
}));
33+
3034
vi.mock('@/components/ui/card', () => ({
3135
Card: ({ children, className }: { children: ReactNode; className?: string }) => (
3236
<div className={className}>{children}</div>

src/app/aluno/dashboard/dashboard-client.tsx

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
'use client';
22

33
import React, { useState } from 'react';
4+
import { useRouter } from 'next/navigation';
45
import { motion } from 'motion/react';
56
import { Card } from '@/components/ui/card';
67
import { Trophy, Zap, Target, Award } from 'lucide-react';
@@ -29,6 +30,7 @@ export default function AlunoDashboardClient({
2930
}: Readonly<AlunoDashboardClientProps>) {
3031
const notify = useAppNotification();
3132
const { t } = useI18n();
33+
const router = useRouter();
3234

3335
const [feedback, setFeedback] = useState<{ title: string; message: string } | null>(null);
3436
const [isFeedbackLoading, setIsFeedbackLoading] = useState(false);
@@ -85,6 +87,10 @@ export default function AlunoDashboardClient({
8587
const res = await registrarHistoricoTreinoAction(historico);
8688
if (res.success) {
8789
notify.success('Treino Finalizado!', 'Seu progresso e XP foram salvos!');
90+
// revalidatePath in the action invalidates the cache but this mounted
91+
// client keeps stale `aluno` props (XP/level/streak). router.refresh()
92+
// re-fetches the RSC payload so the dashboard reflects the new state.
93+
router.refresh();
8894
} else {
8995
throw new Error(res.error);
9096
}

src/app/aluno/login/page.tsx

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,12 +96,18 @@ export default function AlunoLoginPage() {
9696
notify.success('Login bem-sucedido!');
9797
router.push('/aluno/dashboard');
9898
} catch (error: unknown) {
99-
const message =
99+
const rawMessage =
100100
error instanceof Error
101101
? error.message
102102
: typeof error === 'object' && error && 'message' in error
103103
? String((error as { message: unknown }).message)
104104
: 'Erro desconhecido';
105+
// Avoid account enumeration: if the signUp fallback discovers the email
106+
// is already registered, surface the same generic credential error as a
107+
// failed login rather than leaking "User already registered" to callers.
108+
const message = /already\s+(been\s+)?registered|user\s+already/i.test(rawMessage)
109+
? 'E-mail ou senha inválidos. Por favor, tente novamente.'
110+
: rawMessage;
105111
setAuthError(message);
106112
} finally {
107113
setIsLoading(false);

0 commit comments

Comments
 (0)