Auditoria de Permissão das Rotas de Front-end (OPAII-2489)

Validação de todas as rotas de tela do front-end (suite/front-end/opasuite/app/(core)/) contra o mapeamento de permissões do middleware havePermission (frontEndRoutes/backEndRoutes em suite/src/routes/middleware/index.js). Referência: @Luan Bagio Compagnoni · Abrangência: rotas top-level de tela.

Como a proteção realmente funciona

A tela só é protegida por permissão se a requisição passar pelo middleware havePermission. Onde isso acontece:

  • Catch-all do Nextsuite/src/setup/Next.js:196: app.all('*', isAuthenticated, havePermission, pageViewCapture, ...). Toda tela servida pelo Next passa por aqui, então frontEndRoutes é o gate efetivo da navegação.
  • Handlers dedicados ANTES do catch-all (setup/Next.js) escapam do havePermission:
    • /audit (l184) → tem havePermission
    • /calendar* (l188) → calendarProtect (gate próprio, sem havePermission)
    • /ai-environment/* (l192) → aiEnvironmentProtect (gate próprio; checa viewAIEnvironment, redireciona p/ /ai-environment/no-permission)
    • /dashboards/:type(manager|attendant) (l158) → só isAuthenticated
    • /analytics/attendant/:id/... (l178) → só isAuthenticated
  • O CASL no front (<Can>, useAbilities) apenas esconde elementos de UI — não bloqueia navegação. Não serve de gate de rota.

Conclusão: uma tela "permissionada" sem entrada em frontEndRoutes (e sem handler dedicado) fica acessível por URL direta a qualquer usuário autenticado — mesmo que a API por trás (backEndRoutes) bloqueie os dados.

Os regex de frontEndRoutes foram testados programaticamente contra cada path de tela (Node RegExp.test). HIT = casa / MISS = não casa.


Tabela de auditoria (rotas top-level)

Rota de telaEm frontEndRoutes?PermissãoGate efetivoStatus
/analytics/customer-service✅ HITvisualizaDashboardGeralhavePermission✅ Coberta
/analytics/attendant✅ HITvisualizaDashboardGeralhavePermission✅ Coberta
/analytics/telephony✅ HITvisualizaDashboardGeralhavePermission✅ Coberta
/apps✅ HITviewAppStorehavePermission✅ Coberta
/audit✅ HITviewAudithavePermission (handler l184 + catch-all)✅ Coberta
/automatic-routines✅ HITvisualizaRotinasAutomaticashavePermission✅ Coberta
/company✅ HITvisualiza_empresahavePermission✅ Coberta
/contacts✅ HITvisualiza_leadshavePermission✅ Coberta
/customers✅ HITvisualiza_clienteshavePermission✅ Coberta
/customer-service-periods✅ HITvisualiza_periodoshavePermission✅ Coberta
/customer-service-reasons✅ HITmotivo_atendimentohavePermission✅ Coberta
/departments✅ HITvisualiza_departamentoshavePermission✅ Coberta
/holidays✅ HITvisualiza_feriadoshavePermission✅ Coberta
/mass-messaging✅ HITvisualizaMassMessaginghavePermission✅ Coberta
/pause-periods✅ HITvisualizaPausePeriodhavePermission✅ Coberta
/quick-messages✅ HITvisualizaQuickMessageshavePermission✅ Coberta
/roles✅ HITvisualiza_permissoeshavePermission✅ Coberta
/tags✅ HITvisualizaEtiquetasEmpresa OU visualizaEtiquetasUsuariohavePermission✅ Coberta
/users✅ HITvisualiza_usuarioshavePermission✅ Coberta
/virtual-agents✅ HITvisualiza_botshavePermission✅ Coberta
/webhooks✅ HITvisualizaWebhookshavePermission✅ Coberta
/ai-environment❌ MISSviewAIEnvironmentaiEnvironmentProtect🔵 OK (gate dedicado)
/calendar❌ MISScalendarProtect🔵 OK (gate dedicado)
/about❌ MISSisAuthenticated🔵 OK (sem permissão por design)
/user-profile❌ MISSisAuthenticated🔵 OK (perfil próprio)
/opening-customer-service-by-attendant❌ MISSvisualiza_abrir_atendimento_atendente (existe; gateia a API)isAuthenticated⚠️ GAP
/update-system❌ MISSupdateSystem (existe)isAuthenticated⚠️ GAP
/dashboards/customer⚠️ MISS no path-raizvisualizaDashboardClienteisAuthenticated na raiz⚠️ Regex (ver obs.)
/dashboards/manager❌ MISSisAuthenticated (handler l158)❓ Decisão
/dashboards/attendant❌ MISSisAuthenticated (handler l158)❓ Decisão
/dialer❌ MISSisAuthenticated❓ Decisão
/call-manager❌ MISSisAuthenticated❓ Decisão
/whatsapp-calls❌ MISSisAuthenticated❓ Decisão
/featured-news❌ MISSisAuthenticated❓ Decisão
/customer-service/[id]/call-copilot❌ MISSisAuthenticated❓ Decisão

Legenda: ✅ Coberta · ⚠️ Gap/regex a corrigir · 🔵 OK por design · ❓ Decisão de produto necessária.


Gaps confirmados (com evidência)

1. /opening-customer-service-by-attendant — tela não gateada

  • Existe a permissão de sistema visualiza_abrir_atendimento_atendente (suite/src/model/permissoes.js:33).
  • A API está protegida: backEndRoutes mapeia ^/services/opening-customer-service-by-attendant(/.*)?$visualiza_abrir_atendimento_atendente (middleware/index.js:42) e o mount aplica havePermission (routes/services/routing.js:30).
  • A tela /opening-customer-service-by-attendant (app/(core)/(routes)/opening-customer-service-by-attendant/page.tsx) cai no catch-all do Next, mas nenhum regex de frontEndRoutes casa → só isAuthenticated. Acessível por URL direta sem a permissão.
  • Correção sugerida (a validar): adicionar em frontEndRoutes '^/opening-customer-service-by-attendant[/?]?': 'visualiza_abrir_atendimento_atendente'

2. /update-system — tela não gateada

  • Existe a permissão de sistema updateSystem (suite/src/model/permissoes.js:62).
  • O router update-system (routes/update-system/routing.js:8 montado em setup/App.js:148) usa isAuthenticated e trata apenas /timeline/:type e /running-update-data (update-system/index.js). A tela /update-system (e /update-system/in-progress) cai no catch-all do Next e nenhum regex de frontEndRoutes casa → só isAuthenticated.
  • Correção sugerida (a validar): adicionar em frontEndRoutes '^/update-system': 'updateSystem'

Observações de regex

  • ^/dashboards/customer[/?] (middleware/index.js:17): o [/?] exige um caractere após customer, então /dashboards/customer (path-raiz) não casa — só /dashboards/customer/ ou /dashboards/customer/:id. A tela-raiz app/(core)/(routes)/(dashboards)/dashboards/customer/page.tsx fica sem gate. Trocar por ^/dashboards/customer[/?]? cobre também a raiz.
  • ^/mass-messaging (l33) e ^/customer-service-periods (l20): sem âncora final → casam qualquer path que comece com o prefixo (ex.: /mass-messaging-x). Hoje sem rota colidente, mas convém ancorar ([/?]?$ ou (/.*)?$) por robustez.
  • ^/company$ (l21): âncora exata $ — cobre só /company. A tela de empresa não tem sub-rotas no front, então OK; atenção se surgirem sub-telas.
  • Sub-rota /analytics/attendant/:id (setup/Next.js:178): handler dedicado com isAuthenticated (sem havePermission). A lista /analytics/attendant é gateada (catch-all), mas o detalhe por :id não. Fora do escopo top-level, registrado como observação.
  • Sem entradas órfãs: as 20 entradas de frontEndRoutes correspondem a telas existentes no front.

Checklist de pendências para @Luan

Marcar decisão por item. "Correção técnica" = gap claro de segurança. "Decisão de produto" = depende da intenção da tela.

Correções técnicas (gaps de gate)

  • /opening-customer-service-by-attendant: adicionar '^/opening-customer-service-by-attendant[/?]?': 'visualiza_abrir_atendimento_atendente' em frontEndRoutes? (API já exige essa permissão — a tela deveria também.) [S/N]
  • /update-system: adicionar '^/update-system': 'updateSystem' em frontEndRoutes? (Permissão updateSystem existe mas não gateia a tela.) [S/N]
  • /dashboards/customer: ajustar regex ^/dashboards/customer[/?]^/dashboards/customer[/?]? para gatear também o path-raiz com visualizaDashboardCliente? [S/N]

Decisões de produto (tela pública vs permissionada)

  • /dashboards/manager e /dashboards/attendant: devem exigir alguma permissão ou seguir só autenticadas (operacionais p/ todo atendente/gestor)? Hoje: só isAuthenticated.
  • /dialer, /call-manager, /whatsapp-calls (telefonia/discador): gate por permissão de sistema, por contrato/módulo, ou só autenticadas? Hoje: só isAuthenticated. (Não há permissão de sistema específica de discador.)
  • /featured-news: tela de novidades é para todos os autenticados ou deve ter permissão? Hoje: só isAuthenticated.
  • /customer-service/[id]/call-copilot: o copilot do atendente precisa de permissão própria? Hoje: só isAuthenticated.

Robustez (opcional)

  • Ancorar ^/mass-messaging e ^/customer-service-periods para evitar match por prefixo.
  • /analytics/attendant/:id (detalhe): incluir havePermission no handler dedicado (setup/Next.js:178) ou deixar só autenticado?

Itens OK por design (sem ação)

  • /ai-environment/* → gateado por aiEnvironmentProtect (checa viewAIEnvironment).
  • /calendar* → gateado por calendarProtect.
  • /about, /user-profile → sem permissão por design (informativo / perfil próprio).
  • /services/company, /services/departments, /services/users → o mount não tem havePermission, mas cada rota interna aplica havePermission (company/index.js, departments/index.js, users/index.js, etc.). APIs protegidas — não são gaps.
Built with LogoFlowershow