## MEDICONNECT – Documentação Técnica e de Segurança Aplicação SPA (React + Vite + TypeScript) consumindo Supabase (Auth, PostgREST, Edge Functions). Este documento consolida: variáveis de ambiente, arquitetura de autenticação, modelo de segurança atual, riscos, controles implementados e próximos passos. --- ## 1. Variáveis de Ambiente (`.env` / `.env.local`) | Variável | Obrigatória | Descrição | | ------------------------ | ---------------- | --------------------------------------------------------------- | | `VITE_SUPABASE_URL` | Sim | URL base do projeto Supabase (`https://.supabase.co`) | | `VITE_SUPABASE_ANON_KEY` | Sim | Chave pública (anon) usada para Auth password grant e PostgREST | | `VITE_APP_ENV` | Não | Identifica ambiente (ex: `dev`, `staging`, `prod`) | | `VITE_SERVICE_EMAIL` | Não (desativado) | Email de usuário técnico (não usar em produção no momento) | | `VITE_SERVICE_PASSWORD` | Não (desativado) | Senha do usuário técnico (não usar em produção no momento) | Boas práticas: - Nunca exponha Service Role Key no frontend. - Não comitar `.env` – usar `.env.example` como referência. --- ## 2. Arquitetura de Autenticação Fluxo atual (somente usuários finais): 1. Usuário envia email+senha -> `authService.login` (POST `/auth/v1/token` grant_type=password). 2. Resposta: `access_token` (curto prazo) + `refresh_token` (longo prazo) armazenados em `localStorage` (decisão provisória). 3. Interceptor (`api.ts`) anexa `Authorization: Bearer ` e `apikey: `. 4. Em resposta 401: wrapper tenta Refresh (grant_type=refresh_token). Se falhar, força logout. 5. `GET /auth/v1/user` fornece user base; `GET /functions/v1/user-info` enriquece (roles, profile, permissions). Edge Function de criação de usuário (`/functions/v1/create-user`) é tentada primeiro; fallback manual executa sequência: signup -> profile -> role -> domínio (ex: doctors/patients table). Motivos para não usar (neste momento) TokenManager técnico: - Elimina necessidade de usuário "service" exposto. - RLS controla acesso por `auth.uid()` – fluxo permanece coerente. --- ## 3. Modelo de Autorização & Roles Roles previstas: `admin`, `gestor`, `medico`, `secretaria`, `paciente`, `user`. Camadas: - Supabase Auth: autenticação e identidade (user.id). - PostgREST + RLS: enforcement de linha/coluna (ex: paciente só vê seus próprios registros; médico vê pacientes atribuídos / futuras policies). - Edge Functions: operações privilegiadas (criação de usuário composto; agregações que cruzam tabelas sensíveis). Princípios: - Menor privilégio: roles específicas são anexadas à tabela `user_roles` / claim custom (via função user-info). - Expansão de permissões sempre via backend controlado (Edge ou admin interface separada). --- ## 4. Armazenamento de Tokens Status revisado: Access Token agora em memória (via `tokenStore`), Refresh Token em `sessionStorage` (escopo aba). LocalStorage legado é migrado e limpo. Motivações da mudança: - Reduz superfície de ataque para XSS persistente (access token não persiste após reload se atacante injeta script tardio). - Session scoping limita reutilização indevida do refresh token após fechamento total do navegador. Persistência atual: | Tipo | Local | Expiração Natural | | -------------- | ----------------- | ------------------------------------ | | Access Token | Memória JS | exp claim (curto prazo) | | Refresh Token | sessionStorage | exp claim / revogação backend | | User Snapshot | Memória JS | Limpo em logout / reload opcional | Riscos remanescentes: - XSS ainda pode ler refresh token dentro da mesma aba. - Ataques supply-chain podem capturar tokens em runtime. Mitigações planejadas: 1. CSP + bloqueio de inline script não autorizado. 2. Auditoria de dependências e lockfile imutável. 3. (Opcional) Migrar refresh para cookie httpOnly + rotacionamento curto (exige backend/proxy). Fallback / Migração: - Em primeira utilização o `tokenStore` migra chaves legacy (`authToken`, `refreshToken`, `authUser`) e remove do `localStorage`. Operações: - `tokenStore.setTokens(access, refresh?)` atualiza memória e session. - `tokenStore.clear()` remove tudo (usado em logout e erro crítico de refresh). Fluxo de Refresh: 1. Requisição falha com 401. 2. Wrapper (`http.ts`) obtém refresh do `tokenStore`. 3. Se sucesso, novo par é salvo (access renovado em memória, refresh substituído em session). 4. Se falha, limpeza e redirecionamento esperados pelo layer de UI. Próximos passos (prioridade decrescente): 1. Testes e2e validando não persistência pós reload sem refresh. 2. Detecção de reuse (se Supabase expor sinalização) e invalidação proativa. 3. Adicionar heurística antiflood de refresh (backoff exponencial). --- ## 5. Regras de Segurança no Banco (RLS) Dependemos de Row Level Security para proteger dados. A aplicação pressupõe policies: - Tabelas de domínio (patients, doctors) filtradas por `auth.uid()` (ex: patient.id = auth.uid()). - Tabela de roles apenas legível para o próprio usuário e roles administrativas. - Operações de escrita restritas ao proprietário ou a roles privilegiadas. Checklist a validar (fora do front): [] Policies para SELECT/INSERT/UPDATE/DELETE em cada tabela sensível. [] Policies específicas para evitar enumerar usuários (ex: `profiles`). [] Remoção de permissões públicas redundantes. --- ## 6. Edge Functions Usadas para: - `user-info`: agrega roles + profile + permissões derivadas. - `create-user`: fluxo atômico de criação (signup + role + domínio) quando disponível. Critérios para mover lógica para Edge: - Necessidade de Service Role Key (não pode ir ao front). - Lógica multi-tabela que exige atomicidade e validação adicional. - Redução de round-trips (performance e consistência). --- ## 7. Decisão: Proxy Backend (A Avaliar) Status: NÃO implementado. Quando justificar criar proxy: | Cenário | Benefício do Proxy | |---------|--------------------| | Necessidade de Service Role | Segredo fora do client | | Orquestração complexa >1 função | Transações / consistência | | Rate limiting custom | Proteção anti-abuso | | Auditoria centralizada | Logs correlacionados | Custos de um proxy: - Latência adicional. - Manutenção (deploy, uptime, patches de segurança). - Duplicação parcial de capacidades já cobertas por RLS. Decisão atual: permanecer sem proxy até surgir necessidade concreta (service role / complexidade). Reavaliar trimestralmente. --- ## 8. Hardening do Cliente Implementado: - Interceptor único normaliza erros e tenta 1 refresh controlado. - Remoção de tokens técnicos persistidos. - Remoção de senha do domínio (ex: `MedicoCreate`). Planejado: - Content Security Policy estrita (nonce ou hashes para scripts inline). - Sanitização consistente para HTML dinâmico (não inserir dangerouslySetInnerHTML sem validação). - Substituir localStorage por memória + fallback volátil. - Feature Policy / Permissions Policy (desabilitar sensores não usados). - SRI (Subresource Integrity) para libs CDN (se adotadas no futuro). --- ## 9. Logging & Observabilidade Diretrizes: - Nunca logar tokens ou refresh tokens. - Em produção, anonimizar IDs sensíveis onde possível (hash irreversível). - Separar logs de segurança (auth failures, tentativas repetidas) de logs de aplicação. Próximo passo: Implementar adaptador de log (console wrapper) com níveis + redaction de padrões (regex para JWT / emails). --- ## 10. Tratamento de Erros Wrapper `http` fornece shape padronizado `ApiResponse`. Princípios: - Não propagar stack trace de servidor ao usuário final. - Exibir mensagem genérica em 5xx; detalhada em 4xx previsível (ex: validação). - Em 401 após falha de refresh -> limpar sessão e redirecionar login. --- ## 11. Ameaças Principais & Contramedidas | Ameaça | Vetor | Contramedida Atual | Próximo Passo | | ---------------------- | --------------------------- | -------------------------------------- | ----------------------------------------- | | XSS persistente | Input não sanitizado | Sem campos com HTML arbitrário | CSP + sanitização + remover localStorage | | Token theft | XSS / extensão maliciosa | Sem service role key | Migrar tokens p/ memória | | Enumeração de usuários | Erros detalhados em login | Mensagem genérica | Rate limit + monitorar padrões | | Escalada de privilégio | Manipular roles client-side | Roles derivadas no backend (user-info) | Policies de atualização de roles estritas | | Replay refresh token | Interceptação | TLS + troca de token no refresh | Reduzir lifetime e detectar reuse | --- ## 12. Roadmap de Segurança (Prioridade) 1. (P1) Migrar tokens para memória + session fallback. 2. (P1) Validar/Documentar RLS efetiva para cada tabela. 3. (P2) Implementar logging redaction adapter. 4. (P2) CSP + lint anti `dangerouslySetInnerHTML`. 5. (P3) Mecanismo de invalidação global de sessão (revogar refresh em logout server-side se necessário). 6. (P3) Testes automatizados de rota protegida (e2e smoke). --- ## 13. Serviços Atuais (Resumo) | Domínio | Arquivo | Observações | | --------------- | ------------------------ | ---------------------------------------------------------- | | Autenticação | `authService.ts` | login, logout, refresh, user-info, getCurrentAuthUser | | Médicos | `medicoService.ts` | CRUD + remoção de password do payload | | Pacientes | `pacienteService.ts` | Listagem/CRUD com normalização | | Roles | `userRoleService.ts` | list/assign/delete | | Criação Usuário | `userCreationService.ts` | Edge first fallback manual | | Relatórios | (planejado) | Pendende confirmar implementação real (`reportService.ts`) | | Consultas | (planejado) | Padronizar nome tabela (`consultas` vs `consultations`) | | SMS | `smsService.ts` | Placeholder | Arquivos legados/deprecados destinados a remoção após verificação de ausência de imports: `consultaService.ts`, `relatorioService.ts`, `listarPacientes.*`, `pacientes.js`, `api.js`. --- ## 14. Convenções de Código - DB `snake_case` -> front `camelCase`. - Limpeza de campos `undefined` antes de mutações (evita null overwrites). - Requisições POST/PUT/PATCH com `Prefer: return=representation` quando necessário. - ApiResponse: `{ success: boolean, data?: T, error?: string, message?: string }`. --- ## 15. Scripts Básicos Instalação: ``` pnpm install ``` Dev: ``` pnpm dev ``` Build: ``` pnpm build ``` --- ## 16. Estrutura Simplificada ``` src/ services/ pages/ components/ entities/ ``` --- ## 17. Próximos Passos Técnicos (Geral) - Implementar serviços faltantes (reports/consultas) alinhados ao padrão http wrapper. - Testes unitários dos mapeadores (medico/paciente) e do fluxo de refresh. - Avaliar substituição de localStorage (Roadmap P1). - Revisar necessidade de proxy a cada trimestre (documentar decisão em CHANGELOG/ADR). --- ## 18. Desenvolvimento: Tipagem, Validação e Testes ### 18.1 Geração de Tipos a partir do OpenAPI Arquivo da especificação parcial: `docs/api/openapi.partial.json` Gerar (ou regenerar) os tipos TypeScript: ``` pnpm gen:api-types ``` Resultado: `src/types/api.d.ts` (não editar manualmente). Atualize o spec antes de regenerar. Fluxo para adicionar/alterar endpoints: 1. Editar `openapi.partial.json` (paths / schemas). 2. Rodar `pnpm gen:api-types`. 3. Ajustar services para usar novos tipos (`components["schemas"][""]`). 4. Adicionar/atualizar validação Zod (se aplicável). 5. Criar ou atualizar testes. ### 18.2 Schemas de Validação (Zod) Arquivo central: `src/validation/schemas.ts` Inclui: - `loginSchema` - `patientInputSchema` + mapper `mapPatientFormToApi` - `doctorCreateSchema` / `doctorUpdateSchema` - `reportInputSchema` + mapper `mapReportFormToApi` Boas práticas: - Validar antes de chamar service. - Usar mapper para manter isolamento entre modelo de formulário e payload API (snake_case). - Adicionar novos schemas aqui ou dividir em módulos se crescer (ex: `validation/patient.ts`). ### 18.3 Testes (Vitest) Config: `vitest.config.ts` Scripts: ``` pnpm test # execução única pnpm test:watch # modo watch ``` Suites atuais: - `patient.mapping.test.ts`: mapeamento form -> API - `doctor.schema.test.ts`: normalização de UF, campos obrigatórios - `report.schema.test.ts`: payload mínimo e erros Adicionar novo teste: 1. Criar arquivo em `src/tests/*.test.ts`. 2. Importar schema/service a validar. 3. Cobrir pelo menos 1 caso feliz e 1 caso de erro. ### 18.4 Padrões de Services Cada service deve: - Usar tipos gerados (`components["schemas"]`) para payload/response quando possível. - Encapsular mapeamentos snake_case -> camelCase em funções privadas (ex: `mapReport`). - Limpar chaves com valor `undefined` antes de enviar (já adotado em pacientes/relatórios). - Emitir `{ success, data?, error? }` uniformemente. ### 18.5 Endpoints de Arquivos (Foto / Anexos Paciente) Formalizados na spec com uploads `multipart/form-data`: - `/auth/v1/pacientes/{id}/foto` (POST/DELETE) - `/auth/v1/pacientes/{id}/anexos` (GET/POST) - `/auth/v1/pacientes/{id}/anexos/{anexoId}` (DELETE) Quando backend estabilizar response detalhado (ex: tipos MIME), atualizar schema `PacienteAnexo` e regenerar tipos. ### 18.6 Validação de CPF Endpoint `/pacientes/validar-cpf` retorna schema `ValidacaoCPF`: ``` { "valido": boolean, "existe": boolean, "paciente_id": string | null } ``` Integração: usar antes de criar paciente para alertar duplicidade. ### 18.7 Checklist ao Criar Novo Recurso 1. Definir schema no OpenAPI (entrada + saída). 2. Gerar tipos (`pnpm gen:api-types`). 3. Criar service com wrappers padronizados. 4. Adicionar Zod schema (form/input). 5. Criar testes (mínimo: validação + mapeamento). 6. Atualizar README (se conceito novo). 7. Verificar se precisa RLS/policy nova no backend. ### 18.8 Futuro: Automação CI Pipeline desejado: - Lint → Build → Test → (Gerar tipos e verificar diff do `api.d.ts`) → Deploy. - Falhar se `docs/api/openapi.partial.json` mudou sem `api.d.ts` regenerado. --- ## 19. Referência Rápida | Ação | Comando | | ---------------------- | ---------------------------------- | | Instalar deps | `pnpm install` | | Dev server | `pnpm dev` | | Build | `pnpm build` | | Gerar tipos API | `pnpm gen:api-types` | | Rodar testes | `pnpm test` | | Testes em watch | `pnpm test:watch` | | Atualizar spec + tipos | editar spec → `pnpm gen:api-types` | --- ## 19.1 Acessibilidade (A11y) Recursos implementados para melhorar usabilidade, leitura e inclusão: ### Preferências do Usuário Gerenciadas via hook `useAccessibilityPrefs` (localStorage, chave única `accessibility-prefs`). As opções persistem entre sessões e são aplicadas ao elemento `` como classes utilitárias. | Preferência | Chave interna | Classe aplicada | Efeito Principal | | ------------------ | --------------- | ------------------- | ------------------------------------------------ | | Tamanho da Fonte | `fontSize` | (inline style root) | Escala tipográfica global | | Modo Escuro | `darkMode` | `dark` | Ativa tema dark Tailwind | | Alto Contraste | `highContrast` | `high-contrast` | Contraste forte (cores simplificadas) | | Fonte Disléxica | `dyslexicFont` | `dyslexic-font` | Aplica fonte OpenDyslexic (fallback legível) | | Espaçamento Linhas | `lineSpacing` | `line-spacing` | Aumenta `line-height` em blocos de texto | | Reduzir Movimento | `reducedMotion` | `reduced-motion` | Remove / suaviza animações não essenciais | | Filtro Luz Azul | `lowBlueLight` | `low-blue-light` | Tonalidade quente para conforto visual noturno | | Modo Foco | `focusMode` | `focus-mode` | Atenua elementos fora de foco (leitura seletiva) | | Leitura de Texto | `textToSpeech` | (sem classe) | TTS por hover (limite 180 chars) | Atalho de teclado: `Alt + A` abre/fecha o menu de acessibilidade. `Esc` fecha quando aberto. ### Componente `AccessibilityMenu` - Dialog semântico com `role="dialog"`, `aria-modal="true"`, foco inicial e trap de tab. - Botões toggle com `aria-pressed` e feedback textual auxiliar. - Reset central limpa preferências e cancela síntese de fala ativa. ### Formulários - Todos os campos críticos com `id` + `label` associada. - Atributos `autoComplete` coerentes (ex: `email`, `name`, `postal-code`, `bday`, `new-password`). - Padrões (`pattern`) e `inputMode` para CPF, CEP, telefone, DDD, números. - `aria-invalid` + mensagens condicionais (ex: confirmação de senha divergente). - Normalização para envio (CPF/telefone/cep) realizada no service antes do request (sem formatação). ### Tabela de Pacientes - Usa `scope="col"` nos cabeçalhos, suporte dark mode, indicador VIP com `aria-label`. ### Temas & CSS Classes utilitárias adicionadas em `index.css` permitindo expansão futura sem alterar componentes. O design evita uso de inline style exceto na escala de fonte global, facilitando auditoria e CSP. ### Boas Práticas Futuras 1. Adicionar detecção automática de `prefers-reduced-motion` para estado inicial. 2. Implementar fallback de TTS selecionável por foco + tecla (reduzir leitura acidental). 3. Testes automatizados de acessibilidade (axe-core) e verificação de contraste. 4. Suporte a aumentar espaçamento de letras (letter-spacing) opcional. --- ### 19.2 Testes de Acessibilidade & Fallback de Render (Status Temporário) Resumo do Problema: Durante a criação de testes de interface para o `AccessibilityMenu`, o ambiente de testes (Vitest + jsdom e também `happy-dom`) deixou de materializar a árvore DOM de componentes React – inclusive para um componente mínimo (`
Hello
`). Não houve erros de compilação nem warnings relevantes, apenas `container.innerHTML === ''` após `render(...)`. Hipóteses já investigadas (sem sucesso): - Troca de `@vitejs/plugin-react-swc` por `@vitejs/plugin-react` (padrão Babel) + pin de versão do Vite (5.4.10). - Alternância de ambiente (`jsdom` -> `happy-dom`). - Remoção/isolamento de ícones (`lucide-react`) e libs auxiliares (mock de `@axe-core/react`). - Render manual via `createRoot` e flush de microtasks. - Ajustes de transform / esbuild jsx automatic. Decisão Temporária (para garantir “teste que funciona”): 1. Marcar suites unitárias dependentes de render React como `describe.skip` enquanto a causa raiz é isolada. 2. Introduzir um teste E2E real em browser (Puppeteer) que valida a funcionalidade essencial do menu. Arquivos Impactados: - Skipped (com TODO): - `src/__tests__/accessibilityMenu.semantic.test.tsx` - `src/__tests__/miniRender.test.tsx` - `src/__tests__/manualRootRender.test.tsx` - Novo teste E2E: - `src/__tests__/accessibilityMenu.e2e.test.ts` Script E2E: ``` pnpm test:e2e-menu ``` O teste: 1. Sobe (ou reutiliza) o dev server Vite (porta 5173). 2. Abre a SPA no Chromium headless. 3. Clica no botão do menu de acessibilidade. 4. Verifica presença do diálogo (role="dialog") e depois fecha. Critério de Aceite Provisório: Enquanto o bug de render unitário persistir, a cobertura de comportamento crítico do menu é garantida pelo teste E2E (abre, foca, fecha). As preferências de acessibilidade continuam cobertas por testes unitários puros (sem render React) onde aplicável. Próximos Passos para Retomar Testes Unitários: 1. Criar reprodutor mínimo externo (novo repo) com dependências congeladas para confirmar se é interação específica local. 2. Rodar `pnpm ls --depth 0` e comparar versões de `react`, `react-dom`, `@types/react`, `vitest`, `@vitejs/plugin-react`. 3. Forçar transpile isolado de um teste (`vitest --run --no-threads --dom`) para descartar interferência de thread pool. 4. Se persistir, habilitar logs detalhados de Vite (`DEBUG=vite:*`) e inspecionar saída transformada de um teste simples. 5. Reintroduzir gradativamente (mini -> menu) removendo mocks temporários. Quando Corrigir: - Remover skips (`describe.skip`). - Reativar (opcional) auditoria `axe-core` com `@axe-core/react`. - Documentar causa raiz aqui (ex: conflito de plugin, polyfill global, etc.). Risco Residual: Falhas específicas de acessibilidade sem cobertura E2E mais profunda (ex: foco cíclico em condições de teclado complexas) podem passar. Mitigação: expandir cenários E2E após estabilizar ambiente unitário. Estado Atual: Fallback E2E ativo e validado. (Atualizar este bloco quando o pipeline unitário React estiver normalizado.) --- --- ## 18. ADRs (Decisões Arquiteturais) Resumidas | ID | Decisão | Status | Justificativa | | ------- | --------------------------------------------- | ------ | ------------------------------------------ | | ADR-001 | Sem proxy backend inicial | Ativo | RLS + Edge Functions suficientes agora | | ADR-002 | Tokens em memória + refresh em sessionStorage | Ativo | Redução de risco XSS mantendo simplicidade | | ADR-003 | Criação de usuário via Edge fallback manual | Ativo | Resiliência caso função indisponível | Registrar novas decisões futuras em uma pasta `docs/adr`. --- ## 19. Checklist de Release (Segurança) [] Remover credenciais de desenvolvimento do README / código. [] Validar CSP ativa no ambiente (report-only -> enforce). [] Executar análise de dependências (npm audit / pnpm audit) e corrigir críticas. [] Verificar que nenhum token aparece em logs. [] Confirmar policies RLS completas. --- ## 20. Notas Finais Este documento substitui versões anteriores e consolida segurança + operação. Atualize sempre que fluxos críticos mudarem (auth, roles, storage de tokens, Edge Functions novas). --- Última atualização: (manter manualmente) 2025-10-03. --- ## 21. Logging Centralizado & Redaction Implementado `logger.ts` substituindo gradualmente `console.*`. Características: - Níveis: debug, info, warn, error. - Redação automática de: - Padrões de JWT (três segmentos base64url). - Campos com `token`, `password`, `secret`, `email`. - Emails em strings. - Nível dinâmico: produção => `info+`, demais => `debug`. Uso: ``` import { logger } from 'src/services/logger'; logger.info('login success', { userId }); ``` Práticas recomendadas: - Não logar payloads completos com PII. - Remover valores sensíveis antes de enviar para meta. - Usar `error` somente para falhas não recuperáveis ou que exigem telemetria. Backlog de logging: - Adicionar transporte opcional (Sentry / Logtail). - Exportar métricas (Prometheus / OTEL) para 401s e latência. Status adicional: - Mascaramento de CPF implementado (`***CPF***XX`). - Contador global de 401 consecutivos com limite (3) antes de limpeza forçada de sessão. --- ## 22. Política CSP (Rascunho) Objetivo: mitigar XSS e exfiltração de contexto. Cabeçalho sugerido (Report-Only inicial): ``` Content-Security-Policy-Report-Only: \ default-src 'self'; \ script-src 'self' 'strict-dynamic' 'nonce-' 'unsafe-inline'; \ style-src 'self' 'unsafe-inline'; \ img-src 'self' data: blob:; \ font-src 'self'; \ connect-src 'self' https://*.supabase.co; \ frame-ancestors 'none'; \ base-uri 'self'; \ form-action 'self'; \ object-src 'none'; \ upgrade-insecure-requests; \ report-uri https://example.com/csp-report ``` Adoção: 1. Aplicar em modo report-only (Netlify / edge) e coletar violações. 2. Eliminar dependências inline e remover `'unsafe-inline'`. 3. Adicionar hashes/nonce definitivos. 4. Migrar para modo enforce. Complementos: - Lint contra `dangerouslySetInnerHTML` sem sanitização. - Biblioteca de sanitização (ex: DOMPurify) caso HTML dinâmico seja necessário. --- ## 23. Contador de 401 Consecutivos Mecânica: - Cada resposta final 401 (sem refresh bem-sucedido) incrementa contador global. - Sucesso de requisição ou refresh resetam o contador. - Ao atingir 3, sessão é limpa (`tokenStore.clear()`) e próximo acesso exigirá novo login. Racional: evitar loops silenciosos de requisições falhando e reduzir superfície de brute force de refresh. Parâmetros: - Limite atual: 3 (configurável em `src/services/authConfig.ts`). --- ## 24. Verificação de Drift do OpenAPI Script: `pnpm check:api-drift` Fluxo CI recomendado: 1. Rodar `pnpm check:api-drift`. 2. Se falhar, forçar desenvolvedor a executar `pnpm gen:api-types` e commitar. Implementação: gera tipos em memória via `openapi-typescript` e compara com `src/types/api.d.ts` normalizando quebras de linha. --- ## 25. Mascaramento de CPF no Logger Padrão suportado: 11 dígitos com ou sem formatação (`000.000.000-00`). Saída: `***CPF***00` (mantendo apenas os dois últimos dígitos para correlação mínima). Objetivo: evitar exposição de identificador completo em logs persistentes.