Auditoria de Permissão das Rotas de Front-end (OPAII-2489)
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 middlewarehavePermission(frontEndRoutes/backEndRoutesemsuite/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 Next —
suite/src/setup/Next.js:196:app.all('*', isAuthenticated, havePermission, pageViewCapture, ...). Toda tela servida pelo Next passa por aqui, entãofrontEndRoutesé o gate efetivo da navegação. - Handlers dedicados ANTES do catch-all (
setup/Next.js) escapam dohavePermission:/audit(l184) → temhavePermission✅/calendar*(l188) →calendarProtect(gate próprio, semhavePermission)/ai-environment/*(l192) →aiEnvironmentProtect(gate próprio; checaviewAIEnvironment, 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
frontEndRoutesforam testados programaticamente contra cada path de tela (NodeRegExp.test). HIT = casa / MISS = não casa.
Tabela de auditoria (rotas top-level)
| Rota de tela | Em frontEndRoutes? | Permissão | Gate efetivo | Status |
|---|---|---|---|---|
/analytics/customer-service | ✅ HIT | visualizaDashboardGeral | havePermission | ✅ Coberta |
/analytics/attendant | ✅ HIT | visualizaDashboardGeral | havePermission | ✅ Coberta |
/analytics/telephony | ✅ HIT | visualizaDashboardGeral | havePermission | ✅ Coberta |
/apps | ✅ HIT | viewAppStore | havePermission | ✅ Coberta |
/audit | ✅ HIT | viewAudit | havePermission (handler l184 + catch-all) | ✅ Coberta |
/automatic-routines | ✅ HIT | visualizaRotinasAutomaticas | havePermission | ✅ Coberta |
/company | ✅ HIT | visualiza_empresa | havePermission | ✅ Coberta |
/contacts | ✅ HIT | visualiza_leads | havePermission | ✅ Coberta |
/customers | ✅ HIT | visualiza_clientes | havePermission | ✅ Coberta |
/customer-service-periods | ✅ HIT | visualiza_periodos | havePermission | ✅ Coberta |
/customer-service-reasons | ✅ HIT | motivo_atendimento | havePermission | ✅ Coberta |
/departments | ✅ HIT | visualiza_departamentos | havePermission | ✅ Coberta |
/holidays | ✅ HIT | visualiza_feriados | havePermission | ✅ Coberta |
/mass-messaging | ✅ HIT | visualizaMassMessaging | havePermission | ✅ Coberta |
/pause-periods | ✅ HIT | visualizaPausePeriod | havePermission | ✅ Coberta |
/quick-messages | ✅ HIT | visualizaQuickMessages | havePermission | ✅ Coberta |
/roles | ✅ HIT | visualiza_permissoes | havePermission | ✅ Coberta |
/tags | ✅ HIT | visualizaEtiquetasEmpresa OU visualizaEtiquetasUsuario | havePermission | ✅ Coberta |
/users | ✅ HIT | visualiza_usuarios | havePermission | ✅ Coberta |
/virtual-agents | ✅ HIT | visualiza_bots | havePermission | ✅ Coberta |
/webhooks | ✅ HIT | visualizaWebhooks | havePermission | ✅ Coberta |
/ai-environment | ❌ MISS | viewAIEnvironment | aiEnvironmentProtect | 🔵 OK (gate dedicado) |
/calendar | ❌ MISS | — | calendarProtect | 🔵 OK (gate dedicado) |
/about | ❌ MISS | — | isAuthenticated | 🔵 OK (sem permissão por design) |
/user-profile | ❌ MISS | — | isAuthenticated | 🔵 OK (perfil próprio) |
/opening-customer-service-by-attendant | ❌ MISS | visualiza_abrir_atendimento_atendente (existe; gateia a API) | só isAuthenticated | ⚠️ GAP |
/update-system | ❌ MISS | updateSystem (existe) | só isAuthenticated | ⚠️ GAP |
/dashboards/customer | ⚠️ MISS no path-raiz | visualizaDashboardCliente | só isAuthenticated na raiz | ⚠️ Regex (ver obs.) |
/dashboards/manager | ❌ MISS | — | isAuthenticated (handler l158) | ❓ Decisão |
/dashboards/attendant | ❌ MISS | — | isAuthenticated (handler l158) | ❓ Decisão |
/dialer | ❌ MISS | — | só isAuthenticated | ❓ Decisão |
/call-manager | ❌ MISS | — | só isAuthenticated | ❓ Decisão |
/whatsapp-calls | ❌ MISS | — | só isAuthenticated | ❓ Decisão |
/featured-news | ❌ MISS | — | só isAuthenticated | ❓ Decisão |
/customer-service/[id]/call-copilot | ❌ MISS | — | só isAuthenticated | ❓ 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:
backEndRoutesmapeia^/services/opening-customer-service-by-attendant(/.*)?$→visualiza_abrir_atendimento_atendente(middleware/index.js:42) e o mount aplicahavePermission(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 defrontEndRoutescasa → 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:8montado emsetup/App.js:148) usa sóisAuthenticatede trata apenas/timeline/:typee/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 defrontEndRoutescasa → 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óscustomer, então/dashboards/customer(path-raiz) não casa — só/dashboards/customer/ou/dashboards/customer/:id. A tela-raizapp/(core)/(routes)/(dashboards)/dashboards/customer/page.tsxfica 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 sóisAuthenticated(semhavePermission). A lista/analytics/attendanté gateada (catch-all), mas o detalhe por:idnão. Fora do escopo top-level, registrado como observação. - Sem entradas órfãs: as 20 entradas de
frontEndRoutescorrespondem 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'emfrontEndRoutes? (API já exige essa permissão — a tela deveria também.) [S/N] -
/update-system: adicionar'^/update-system': 'updateSystem'emfrontEndRoutes? (PermissãoupdateSystemexiste mas não gateia a tela.) [S/N] -
/dashboards/customer: ajustar regex^/dashboards/customer[/?]→^/dashboards/customer[/?]?para gatear também o path-raiz comvisualizaDashboardCliente? [S/N]
Decisões de produto (tela pública vs permissionada)
-
/dashboards/managere/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-messaginge^/customer-service-periodspara evitar match por prefixo. -
/analytics/attendant/:id(detalhe): incluirhavePermissionno handler dedicado (setup/Next.js:178) ou deixar só autenticado?
Itens OK por design (sem ação)
/ai-environment/*→ gateado poraiEnvironmentProtect(checaviewAIEnvironment)./calendar*→ gateado porcalendarProtect./about,/user-profile→ sem permissão por design (informativo / perfil próprio)./services/company,/services/departments,/services/users→ o mount não temhavePermission, mas cada rota interna aplicahavePermission(company/index.js,departments/index.js,users/index.js, etc.). APIs protegidas — não são gaps.