Skip to content

Auditoria — Logs de Transações: cobertura de filtros e fuso horário

Data: 2026-04-27 Escopo: Admin → Monitoramento → Logs de Transações Severidade: Alta (filtros inutilizáveis para 42 ações; janela de data 3h deslocada)

Sintomas reportados

  1. Ações assinatura.*, fatura.*, auth.* (entre outras) não apareciam no dropdown de filtro — impossível filtrar.
  2. Filtrar Data Início = Data Fim = 19/04/2026 não retornava registros após 21:00 BRT desse mesmo dia.

Diagnóstico

Bug 1 — Catálogo SSOT defasado

src/constants/log-actions.ts (LOG_ACTION_OPTIONS) é a fonte única do dropdown de filtro e do label exibido nos badges. Ações registradas via registrarLog() no backend que não estão nesse array ficam invisíveis no filtro embora persistam no DB.

Query agregada em logs_transacoes (últimos 30 dias) revelou 42 ações órfãs, com destaque:

AçãoVolume 30dCategoria
auth.select_role599Troca de papel
login.e2e_bypass325Bypass E2E
assinatura.update_valor_personalizado13Faturamento admin
fatura.recalculate_due_date6Faturamento admin
manutencao.cleanup_*~85 (somatório)CRON granular
header.programar, header.reorder, header.placeholder_*~25Header avançado

Lista completa no commit que adicionou as 42 entradas em LOG_ACTION_OPTIONS (seção "COMPLEMENTOS — auditoria 2026-04-27").

Bug 2 — Janela de data em UTC, sem conversão BRT

supabase/functions/admin-logs/index.ts usava:

ts
query = query.gte('criado_em', filters.dataInicio); // "2026-04-19" → 00:00 UTC

Como o frontend manda YYYY-MM-DD puro, o backend interpretava como 00:00 UTC = 21:00 BRT do dia anterior. Resultado: registros após 21:00 BRT do dia consultado caíam no "dia seguinte UTC" e eram filtrados fora.

Para 19/04/2026 em fuso BRT (UTC−3), a janela correta é:

  • >= 2026-04-19T03:00:00Z (00:00 BRT)
  • < 2026-04-20T03:00:00Z (00:00 BRT do dia seguinte)

Correções aplicadas

1. Catálogo (frontend)

src/constants/log-actions.ts: +42 entradas em seção dedicada "COMPLEMENTOS — auditoria 2026-04-27", organizadas por domínio (Auth, Assinatura, Fatura, Faturamento CRON, Header, Manutenção, Mural, Olimpíada, Pagamento, Importação, Perfil, Usuário). Labels em português seguindo o padrão existente.

2. Fuso horário (backend)

supabase/functions/admin-logs/index.ts (linhas 92–110): aplica BRT_OFFSET_HOURS = 3 antes de .gte() e .lt(). Frontend continua enviando YYYY-MM-DD puro — única fonte de verdade do fuso é o edge function.

ts
const BRT_OFFSET_HOURS = 3;
const inicio = new Date(`${filters.dataInicio}T00:00:00.000Z`);
inicio.setUTCHours(inicio.getUTCHours() + BRT_OFFSET_HOURS);
query = query.gte('criado_em', inicio.toISOString());

3. Guardrail — script de auditoria

scripts/audit/log-actions-coverage.ts: compara SELECT DISTINCT acao FROM logs_transacoes (90d) contra LOG_ACTION_OPTIONS e sai com código 2 se houver órfãs. Roda mensalmente (proposta: integrar ao @audit logs).

Regra preventiva

Toda nova string passada para registrarLog() DEVE ser adicionada a LOG_ACTION_OPTIONS no mesmo PR. Sem entrada no catálogo, a ação é invisível para o operador de monitoramento.

Documentado em docs/security/AUDIT_LOG.md.

Validação

  • ✅ Build do frontend passa.
  • ⏭️ Validação manual: filtrar auth.select_role no admin retorna ≥ 599 registros.
  • ⏭️ Validação manual: filtrar 19/04 → 19/04 em BRT retorna registros até 23:59 BRT.