Skip to content

Fix/correcoes seguranca - #719

Merged
karinaperes merged 3 commits into
developfrom
fix/correcoes-seguranca
Aug 18, 2026
Merged

karinaperes merged 3 commits into
developfrom
fix/correcoes-seguranca

Conversation

@karinaperes

@karinaperes karinaperes commented Aug 18, 2026 •

Copy link
Copy Markdown
Contributor

📋 Descrição

Fecha três brechas de segurança encontradas na auditoria técnica do projeto.

  • O hash de senha era devolvido pela API a qualquer usuário autenticado.
  • A verificação de administrador de /api/metrics nunca era acionada.
  • O upload de avatar aceitava arquivos de qualquer pessoa, sem autenticação.

🔗 Issue relacionada

Não há issue pública. Achados de segurança são tratados fora do repositório até
a correção, para não divulgar a falha antes do conserto.


✅ Alterações realizadas

  • Backend
  • Front-end
  • Banco de Dados
  • Testes
  • Documentação

1. Hash de senha deixa de sair do banco

Antes: nenhuma das rotas usava select ou omit, então o objeto User ia
inteiro para o JSON — com o campo password. O caso mais grave era
GET /api/user/[id], liberado para todos os papéis: qualquer pessoa autenticada
lia o hash de qualquer outra informando o id.

Agora: src/lib/prisma.ts declara omit: { user: { password: true } } no
cliente. O campo deixa de ser retornado por padrão em todas as consultas, e a
proteção passa a valer para código futuro sem depender de ninguém lembrar.

A única exceção é explícita e localizada — a validação de login em
src/lib/auth.ts pede omit: { password: false }, com comentário explicando o
porquê.

Hash vazado permite quebra por força bruta offline: sem limite de tentativas,
sem bloqueio de conta e sem registro nos logs.

No mesmo arquivo, o log de queries do Prisma passou a ser condicionado ao
ambiente. Antes log: ['query'] era incondicional, o que em produção significa
registrar toda query com seus valores — custo de I/O, ruído nos logs e dado de
usuário espalhado pelo provedor.

2. Guarda de administrador de /api/metrics

// antes — a condição nunca é verdadeira
const session = await checkAuth({ requireAdmin: true });
if (!session) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });

// agora
const auth = await checkAuth({ requireAdmin: true });
if (!auth.authorized) return auth.response;

checkAuth sempre devolve um objeto — { authorized: false, response } ao
negar e { authorized: true, session } ao permitir. Nunca devolve null. O
teste if (!session) verificava a existência do objeto, não a autorização, e a
rota respondia a qualquer requisição — inclusive anônima — entregando métricas de
negócio: crescimento de usuários por mês e por papel, skills, stacks, projetos e
taxa de uso dos convites.

Verificado que nenhuma outra rota tem o mesmo defeito.

3. Upload de avatar

A rota não chamava checkAuth, não validava tipo nem tamanho e enviava o buffer
direto para o Cloudinary da organização. Qualquer pessoa na internet podia
consumir a cota da conta ou hospedar arquivos sob o domínio do projeto.

Passou a exigir:

  • sessão autenticada;
  • tipo entre image/png, image/jpeg e image/webp;
  • tamanho máximo de 2 MB;
  • resource_type: 'image' no Cloudinary;
  • public_id igual ao id do usuário — cada conta sobrescreve apenas o próprio
    avatar, em vez de poder gravar em qualquer caminho da pasta.

Erros passaram a usar buildResponse e logger, como na rota irmã
/api/upload/ui-assets, que já fazia a verificação corretamente.


🧪 Como testar

  1. Login — entrar normalmente. Valida a exceção do omit em auth.ts; se
    falhar, o hash não está chegando à validação de senha.
  2. Trocar avatar no perfil (/dashboard/profile) e no painel de admin.
    ⚠️ O formato da resposta mudou — ver observações.
  3. Painel de admin → usuários — a listagem deve continuar carregando.
  4. /api/metrics deslogada → deve responder 401:
    curl -i http://localhost:3000/api/metrics
  5. Upload sem sessão → deve responder 401:
    curl -i -X POST http://localhost:3000/api/upload/avatar -F "file=@foto.png"
  6. Arquivo inválido — renomear um .pdf para .png e tentar enviar; deve
    ser recusado com 400.
  7. Nenhuma resposta de API deve conter password:
    curl -s http://localhost:3000/api/user/<id> -H "Cookie: <sessao>" | grep -c password
    # esperado: 0

📷 Evidências

                      antes    depois
erros de tipo          110      110      (sem regressão)
testes             160 + 3s   160 + 3s
build                  ok       ok

Os 110 erros restantes são pré-existentes, concentrados em arquivos de teste, e
não têm relação com estas alterações.


⚠️ Observações

  • O contrato do upload de avatar mudou. A rota passou a responder no formato
    padrão do projeto — { success, message, data: { url } } — em vez de
    { url }. Os três componentes que consomem foram atualizados:
    UserEdit.tsx, EditUserAdminForm.tsx e UserAdminForm.tsx. Vale testar a
    troca de avatar nos dois lugares antes do merge.

  • O omit global é a mudança de maior alcance. Ele afeta todas as consultas
    a User no projeto. A suíte passa e o build compila, mas os testes mockam o
    Prisma — por isso os passos 1, 2 e 3 do roteiro acima importam.

  • Encontrado no caminho: ao escrever a validação do upload, o TypeScript
    acusou auth.session como possivelmente indefinido mesmo após
    if (!auth.authorized) return. É o comportamento que a tarefa REF-04 do
    backlog descreve: o tipo de retorno de checkAuth não estreita, e permite
    acessar session sem verificar authorized — foi exatamente assim que o
    defeito de /api/metrics surgiu. Aqui foi seguido o padrão já usado pelas
    demais rotas; a correção de fundo fica para a tarefa específica.

  • Fora do escopo deste PR: as demais brechas mapeadas na auditoria — IDOR em
    project-stack e project-skill, e injeção de HTML no e-mail de contato.
    Serão tratadas separadamente.


✔️ Checklist

  • O PR está relacionado a uma Issue. (achado de segurança, tratado fora do repositório)
  • O código foi testado.
  • Os testes existentes continuam passando.
  • A documentação foi atualizada (quando necessário). (não aplicável)
  • Este PR resolve apenas um objetivo. (fechar as brechas de acesso indevido)
    ARQ
    echo "escrito: $SP/pr-correcoes-seguranca.md" && wc -l "$SP/pr-correcoes-seguranca.md"

Summary by CodeRabbit

  • New Features

    • Added secure avatar uploads with image validation, a 2 MB size limit, thumbnail processing, and user-specific replacement.
    • Improved avatar updates across user profile and administration forms.
  • Bug Fixes

    • Fixed authorization checks for metrics and avatar upload requests.
    • Corrected avatar URL handling after successful uploads.
    • Restored password validation during sign-in.
    • Improved production error logging while protecting user password data by default.

@karinaperes karinaperes self-assigned this Aug 18, 2026
@vercel

vercel Bot commented Aug 18, 2026

Copy link
Copy Markdown
Contributor

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
trycatch Ready Ready Preview Aug 18, 2026 10:30pm
trycatch-prod Ready Ready Preview Aug 18, 2026 10:30pm

@coderabbitai

coderabbitai Bot commented Aug 18, 2026 •

Copy link
Copy Markdown

Review Change Stack

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 55f1b3f5-f4f0-4b98-8669-f110fd9b2e87

📥 Commits

Reviewing files that changed from the base of the PR and between 49dad34 and 305a580.

📒 Files selected for processing (7)
  • src/app/api/metrics/route.ts
  • src/app/api/upload/avatar/route.ts
  • src/components/Dashboard/User/UserEdit.tsx
  • src/components/Dashboard/UserAdmin/EditUserAdminForm.tsx
  • src/components/Dashboard/UserAdmin/UserAdminForm.tsx
  • src/lib/auth.ts
  • src/lib/prisma.ts

📝 Walkthrough

Walkthrough

Changes

Authentication and avatar flow

Layer / File(s) Summary
Prisma and credential authentication
src/lib/prisma.ts, src/lib/auth.ts
Prisma omits User.password by default. Credential validation explicitly includes the password field. Logging differs between development and production.
Authenticated avatar upload
src/app/api/upload/avatar/route.ts, src/components/Dashboard/User/*.tsx, src/components/Dashboard/UserAdmin/*.tsx
The route validates authorization and image files, uploads user-owned images to Cloudinary, and returns data.url. Dashboard forms now read the nested URL.
Metrics authorization
src/app/api/metrics/route.ts
The metrics route returns the authorization response when auth.authorized is false.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
  participant DashboardForm
  participant AvatarRoute
  participant checkAuth
  participant Cloudinary
  DashboardForm->>AvatarRoute: POST multipart avatar
  AvatarRoute->>checkAuth: Authenticate with ROLE_GROUPS.ALL
  checkAuth-->>AvatarRoute: Return authorization result
  AvatarRoute->>Cloudinary: Upload validated image
  Cloudinary-->>AvatarRoute: Return secure URL
  AvatarRoute-->>DashboardForm: Return data.url
  DashboardForm->>DashboardForm: Update form state
Loading

Possibly related PRs

Suggested reviewers: dev-mauricioab

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/correcoes-seguranca

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@karinaperes
karinaperes merged commit 95252a9 into develop Aug 18, 2026
7 of 8 checks passed
karinaperes added a commit that referenced this pull request Aug 19, 2026

This branch was successfully deployed

2 active deployments
Preview – trycatch — 305a5803 Deployed Aug 18, 2026 by vercel[bot]
Preview – trycatch-prod — 305a5803 Deployed Aug 18, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant