Skip to content

fix: gera id local sem crypto.randomUUID em contexto inseguro - #549

Merged
PabloTzeliks merged 1 commit into
mainfrom
hotfix/corrige-crypto-randomuuid-contexto-inseguro
Jul 26, 2026
Merged

fix: gera id local sem crypto.randomUUID em contexto inseguro#549
PabloTzeliks merged 1 commit into
mainfrom
hotfix/corrige-crypto-randomuuid-contexto-inseguro

Conversation

@BlMedeiros

Copy link
Copy Markdown
Contributor

Contexto

Na demo servida por HTTP puro (sem TLS), criar comunicado com imagem publicava o comunicado sem a imagem, e a preview não aparecia — sem erro visível na tela. O console do navegador, durante o teste real, mostrava:

Uncaught TypeError: crypto.randomUUID is not a function
    at Array.map (<anonymous>)
    at onChange

crypto.randomUUID() só existe em contexto seguro (HTTPS ou localhost) — é assim por especificação da Web Platform, não é bug de lógica. Servida por HTTP puro num host que não é localhost, a API é undefined e a chamada explode dentro do .map() de addFiles, antes do onChange popular o estado. Daí os dois sintomas: preview vazia e, no submit, array de imagens vazio — o upload nem chega a ser tentado (bate com o log do backend: publish sempre 201, zero chamadas ao endpoint de imagens).

Não reproduz em pnpm dev: localhost é isento da exigência por especificação, de propósito, para não travar o desenvolvimento local.

O que muda

As duas chamadas restantes de crypto.randomUUID() passam a usar um generateId() baseado em crypto.getRandomValues(), que não tem a restrição de contexto seguro, com fallback para Math.random() caso não exista crypto algum.

Corrige três fluxos que estavam quebrados na demo:

  • criar comunicado com imagem (o bug reportado);
  • editar comunicado adicionando imagem nova — mesmo FileUpload via AnnouncementContentStep, não havia sido reportado;
  • criar curso (lista de rascunho) — ninguém havia testado esse fluxo na demo.

packages/checklist já tinha a correção equivalente em generateItemKey (com o mesmo comentário explicando o bug); este PR fecha os dois call sites que faltavam.

Varredura no repo inteiro por outras APIs restritas a contexto seguro (navigator.clipboard, navigator.geolocation, navigator.mediaDevices, crypto.subtle, serviceWorker, isSecureContext): nenhuma ocorrência. O escopo está fechado nesses dois arquivos.

Por que duas cópias e não um util único

apps/root importa de @portal/shared (packages/shared/src/utils/generateId.ts), que é o lugar que o AGENTS.md indica para utilitário genérico e já era dependência declarada — não toca no lockfile.

packages/ui recebeu uma cópia local, irmã de fileValidation.ts (mesmo padrão da pasta), porque não depende de @portal/shared hoje. Declarar essa dependência regravaria o pnpm-lock.yaml, que o Dockerfile instala com --frozen-lockfile — risco desnecessário num hotfix de demo. Convergir as cópias fica como fast-follow. O comentário no arquivo registra isso.

Issue relacionada

Sem issue aberta — bug levantado direto no teste da demo. Abrir e referenciar aqui, se o board exigir.

Como testar

O cenário não reproduz em localhost (isento por especificação). Duas formas de validar:

  1. Automatizado (é o que cobre a regressão):

    pnpm test
    

    Os testes novos removem crypto.randomUUID do global mantendo getRandomValues real, reproduzindo o contexto inseguro. Verificado que pegam a regressão: reintroduzindo crypto.randomUUID() o teste falha com exatamente TypeError: crypto.randomUUID is not a function.

  2. Real, na EC2, depois do deploy — mesma origem HTTP que expôs o bug:

    • criar comunicado com imagem → preview aparece e a imagem sobe;
    • editar um comunicado existente adicionando imagem nova → idem;
    • criar curso → o curso entra na lista de rascunho.

Tipo de mudança

  • Nova feature
  • Correção de bug
  • Refatoração (sem mudança de comportamento)
  • Documentação
  • Infraestrutura / config / build
  • Outro: ___

Checklist do autor

  • Código segue convenções definidas em CONTRIBUTING.md
  • Validei localmente que a aplicação compila/gera build sem erros (ver ressalva em Notas pro revisor)
  • Verifiquei que não há erros de análise estática ou alertas relevantes no código (pnpm lint verde nos 7 pacotes)
  • Confirmei que não há erros de tipagem/TypeScript no escopo da mudança (pnpm typecheck verde)
  • Testei manualmente os cenários principais (ver ressalva — o cenário só existe fora de localhost)
  • Componentes novos/alterados documentados no Storybook (pnpm check:stories verde; nenhum componente novo)
  • Documentação atualizada (não aplicável)
  • Não introduzi dependências novas sem alinhamento prévio (nenhuma dependência nova; pnpm install --frozen-lockfile passa)

Notas pro revisor

Ressalva honesta sobre o build local. pnpm build compila (✓ Compiled successfully, 57 páginas geradas) mas falha no passo final de output: 'standalone' com EPERM: operation not permitted, symlink. É restrição do Windows (criar symlink exige Modo de Desenvolvedor ou admin), não da mudança: rodei limpo na main e na branch, e as duas falham identicamente no mesmo ponto. O build de produção roda em Linux nos dois caminhos (Dockerfile: node:22-slim; CI: ubuntu-latest), onde isso não ocorre. O job Build deste PR é a confirmação real.

Sobre a mudança em packages/ui. Pelo AGENTS.md, exige aprovação de ao menos um integrante do squad de Front-End. A alteração é de uma linha no addFiles mais um módulo auxiliar novo — sem mudança visual, sem mudança de API do componente.

Sobre o id. É só key de React e chave de dedup local (uploadedLocalIdsRef em useCreateAnnouncement.ts, lista de rascunho de curso). Nunca é persistido nem enviado ao servidor — o upload identifica a imagem pelo retorno do presign (announcementImagesClient.ts usa item.id apenas no callback onUploaded). Não há argumento de segurança contra trocar randomUUID por esse fallback.

Pós-merge (GitFlow, CONTRIBUTING.md §Emergência). Merge na main exige tag SemVer — sugestão v3.1.1e merge de volta na develop.

crypto.randomUUID() só existe em contexto seguro (HTTPS ou localhost), por
especificação da Web Platform. Servida por HTTP puro num host que não é
localhost, a chamada lançava "TypeError: crypto.randomUUID is not a function"
e derrubava o handler antes de ele atualizar o estado: a imagem não aparecia na
preview e o comunicado era publicado sem ela, sem erro visível na tela.

Troca as duas chamadas restantes por um generateId() baseado em
crypto.getRandomValues(), que não tem essa restrição, com fallback para
Math.random() quando não há crypto algum. O id é apenas chave local de lista e
key de React — nunca é persistido nem enviado ao servidor.

Corrige três fluxos: criar comunicado com imagem, editar comunicado
adicionando imagem (mesmo FileUpload) e criar curso (lista de rascunho).
packages/checklist já tinha a correção equivalente em generateItemKey.

A cópia local em packages/ui evita declarar dependência de @portal/shared, que
regravaria o pnpm-lock.yaml instalado com --frozen-lockfile no Dockerfile.
Convergir as cópias em @portal/shared fica como fast-follow.

Como o cenário não reproduz em localhost (isento por especificação), os testes
removem crypto.randomUUID do global para cobrir a regressão.
@PabloTzeliks
PabloTzeliks merged commit 593d8ec into main Jul 26, 2026
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Algo não está funcionando priority: high Prioridade alta squad: frontend

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants