Pular para conteúdo

0019 — Bumps comum-web 0.5.0 + seguranca 0.4.0: dropa handlers, dedup AuthSupport, adota CSRF de sessão

Status: Aprovado · Responsável: Gustavo Madruga · Atualizado em: 2026-08-19 · Decidido em: 2026-08-19

Contexto. As libs da casa evoluíram cobrindo o que o app ainda espelhava localmente. A xadm-comum-web 0.5.0 fez o UnifiedErrorResponseProcessor logar no ponto único (5xx em ERROR com a causa-raiz → pega o SentryAppender; 4xx em DEBUG) — feedback §6 do ciclo do 0017 que landou na lib. A xadm-seguranca 0.4.0 extraiu o contrato byte-idêntico do AuthSupport (constantes de atributo/cookie + isHttps/ deveRecusarAnonimo) e trouxe o CsrfTokens (token CSRF derivado da sessão, exposto pelo SessionAuthenticationFetcher no atributo auth.csrf). É a continuação da iniciativa libs+contrato (0017/0018).

Decisão.

  • Dropar ServerExceptionHandler/UnauthorizedExceptionHandler. Com o log no processor da lib, os @ExceptionHandler dedicados viraram recópia. ServerException/UnauthorizedException passaram a extends HttpStatusException implements PortadoraDeProblema (carregam status + code de máquina); o UnifiedErrorResponseProcessor renderiza o problem+json e loga o 5xx→Sentry. Corpo byte-idêntico (o default PT de mensagem nula migrou para dentro da exceção); provado por ExceptionRenderingIT. Nuance aceita: 401 loga em DEBUG (era WARN no handler) — erro de cliente, esperado. GlobalExceptionHandler (página HTML de erro p/ browser) e ViewRejectionHandler (policy de auth) ficam — a lib só emite problem+json.
  • Dedup do AuthSupport local. Constantes (AUTH_EMAIL_ATTR/AUTH_NAME_ATTR/AUTH_CSRF_ATTR/ SESSION_COOKIE) + isHttps vêm da lib (br.com.xadm.comum.seguranca.AuthSupport). A whitelist de views (policy de rota do app) saiu para uma classe própria ViewWhitelist — mesmo nome de conceito ≠ mesma decisão. O deveRecusarAnonimo local (código morto) foi removido.
  • Adotar CSRF de sessão nas escritas das views. CsrfTokens.verify foi dobrado no UiProductionGuard (assertCrudWriteAllowed(csrf)/assertDebugAllowed(csrf)) — o mesmo call-site que todo @Post de escrita já invocava. GlobalViewModel expõe csrfToken (do atributo auth.csrf); todo form de escrita (form.html save + list.html deletar/restaurar + os POST JS do /debug) renderiza o campo oculto _csrf. O verify é no-op em dev/test (auth em bypass, o atributo auth.csrf não é setado) e exige o token em produção (403 em divergência). Provado por CsrfProtectionIT (sem token → 403, token errado → 403, token correto → passa). O login CSRF (double-submit xadm_login_csrf do LoginController, fase pré-sessão) é outro mecanismo e fica como está.
  • Corrige gap pré-existente de guard. 5 handlers de escrita (ItensLote.delete, ItensPed.save, NotaCompl.delete, TbpcoEst.save, Veiculos.save) não chamavam o UiProductionGuard — a ViewSecurityRule já barrava anônimo em prod, mas a defesa-em-profundidade faltava e sem ela não haveria verificação CSRF nesses endpoints. Agora todos os 42 POST de escrita das entidades (14 × 3)
  • os 3 do /debug passam pelo guard com CSRF.

Consequências. ./gradlew check sobe com comum-web:0.5.0 + seguranca:0.4.0. Correção de segurança/erro na casa chega ao app por bump. A superfície de escrita das views deixou de ser CSRF-vulnerável. A PortadoraDeProblema é o seam de erro do app daqui pra frente (novas exceções de domínio só a implementam).

Fora de escopo: as libs em si (repo xadm-commons); CSRF do endpoint GET /propriedades/sync-nome-curto (escrita disparada por GET — anomalia pré-existente, sem form/token; fica para um saneamento próprio do verbo).