Skip to content

ADR-009 — Sanitização de Erro em 2 Camadas

CampoValor
Status✅ Aceito
Data2026-04-30
AutoresEquipe OLP
Implementaçãosrc/lib/error-helpers.ts (frontend), _shared/auth-helpers.ts + handlers (backend)

Contexto

Erros não tratados vazam:

  • Stack traces com nomes de tabelas, colunas, paths internos
  • Mensagens do Postgres ("violates foreign key constraint fk_aluno_escola") indecifráveis para usuário
  • Detalhes de implementação que ajudam atacantes (ex.: "user not found" vs "wrong password")

Ao mesmo tempo, logs internos precisam do detalhe completo para debug.

Decisão

Erros passam por duas camadas obrigatórias antes de chegar ao toast:

Camada 1 — Backend (Edge Function)

Toda Edge Function tem try/catch global. No catch:

  1. Loga internamente o erro completo via console.error + registrarLog (severidade erro)
  2. Retorna ao cliente apenas:
    • HTTP status semântico (401/403/400/404/409/500)
    • { success: false, message: "<mensagem amigável>", code?: "<código semântico>" }
  3. Nunca propaga error.stack, error.detail, código Postgres bruto, nome de tabela
  4. Mapeamento de erros Postgres comuns → códigos semânticos:
    • 23505 (unique violation) → code: 'duplicate', message PT
    • 23503 (foreign key) → code: 'fk_violation'
    • 42501 (insufficient privilege) → 403
  5. Auth errors usam classe tipada AuthError(message, 401|403) para evitar heurística por substring.

Memória: backend-error-sanitization-contextual-standard, error-helper-postgres-mapping-standard.

Camada 2 — Frontend (React Query onError)

Toda mutação que pode falhar usa:

ts
onError: (error) => {
  olpToast.error(getUserFriendlyError(error));
}

getUserFriendlyError em src/lib/error-helpers.ts:

  • Reconhece o formato EdgeResponse (já sanitizado pelo backend)
  • Mapeia code semântico → mensagem PT contextualizada
  • Para erros de rede (_transient: true): "Falha na conexão. Tente novamente."
  • Para erros desconhecidos: mensagem genérica + log em DEV
  • Nunca exibe error.message cru

Alternativas consideradas

A. Camada única no frontend (parsing de qualquer erro)

Pros: backend mais simples. Cons: stack traces vazam; dependência da disciplina do front; testes complicados. Veredicto: rejeitado — defesa em profundidade.

B. Camada única no backend

Pros: front trivial. Cons: erros de rede e timeout do navegador continuam crus; UX inconsistente. Veredicto: rejeitado.

C. Bibliotecas de error tracking (Sentry) só, sem sanitização

Pros: telemetria rica. Cons: tracking ≠ UX; usuário ainda vê stack. Veredicto: complementar, não substituto.

Consequências

Positivas

  • Usuário vê sempre mensagem em PT, contextual
  • Atacante não obtém estrutura de tabelas via mensagens de erro
  • Logs internos mantêm detalhe completo para debug
  • Toast unificado (olpToast) — proibido alert() ou sonner direto

Negativas

  • 2 lugares para mapear erros novos (mitigado por catálogo central de códigos)
  • Mensagem genérica pode ser "menos útil" — mitigado por code semântico que dá pista

Trade-offs

EixoCustoBenefício
Defesa em profundidade2 camadasResistente a erro de uma camada
Curadoria de mensagensTradução PT manualTom de voz consistente
DebugDetalhe vai para log, não toastPrivacidade e segurança

Conformidade

Como verificar:

bash
# Toast nunca recebe error.message direto
rg "olpToast\.error\(.*error\.message" src --type ts
# Toast nunca recebe error.toString
rg "olpToast.*error\.toString" src --type ts
# Edge Function não retorna error.stack
rg "error\.stack" supabase/functions --type ts

Sinais de violação:

  • toast(error.message) sem getUserFriendlyError
  • Edge Function retornando { error: err } (objeto cru)
  • alert(...) em qualquer lugar
  • Import direto de sonner em componente (deve ser olpToast)

Débito técnico conhecido

  • Cobertura de mapeamento PT em getUserFriendlyError cresce sob demanda — quando código novo aparece em prod, é adicionado ao mapa.

Referências

  • src/lib/error-helpers.ts
  • supabase/functions/_shared/auth-helpers.ts
  • mem://architecture/backend-error-sanitization-contextual-standard
  • mem://architecture/error-helper-postgres-mapping-standard
  • mem://architecture/auth-and-idor-status-code-semantics
  • mem://guidelines/silent-save-prevention
  • ADR-003 — formato de resposta
  • ADR-007 — onde é consumido