fix: gera id local sem crypto.randomUUID em contexto inseguro - #549
Merged
PabloTzeliks merged 1 commit intoJul 26, 2026
Merged
Conversation
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.
14 tasks
PabloTzeliks
approved these changes
Jul 26, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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:
crypto.randomUUID()só existe em contexto seguro (HTTPS oulocalhost) — é 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 éundefinede a chamada explode dentro do.map()deaddFiles, antes doonChangepopular 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:publishsempre 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 umgenerateId()baseado emcrypto.getRandomValues(), que não tem a restrição de contexto seguro, com fallback paraMath.random()caso não existacryptoalgum.Corrige três fluxos que estavam quebrados na demo:
FileUploadviaAnnouncementContentStep, não havia sido reportado;packages/checklistjá tinha a correção equivalente emgenerateItemKey(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/rootimporta de@portal/shared(packages/shared/src/utils/generateId.ts), que é o lugar que oAGENTS.mdindica para utilitário genérico e já era dependência declarada — não toca no lockfile.packages/uirecebeu uma cópia local, irmã defileValidation.ts(mesmo padrão da pasta), porque não depende de@portal/sharedhoje. Declarar essa dependência regravaria opnpm-lock.yaml, que oDockerfileinstala 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:Automatizado (é o que cobre a regressão):
Os testes novos removem
crypto.randomUUIDdo global mantendogetRandomValuesreal, reproduzindo o contexto inseguro. Verificado que pegam a regressão: reintroduzindocrypto.randomUUID()o teste falha com exatamenteTypeError: crypto.randomUUID is not a function.Real, na EC2, depois do deploy — mesma origem HTTP que expôs o bug:
Tipo de mudança
Checklist do autor
pnpm lintverde nos 7 pacotes)pnpm typecheckverde)localhost)pnpm check:storiesverde; nenhum componente novo)pnpm install --frozen-lockfilepassa)Notas pro revisor
Ressalva honesta sobre o build local.
pnpm buildcompila (✓ Compiled successfully, 57 páginas geradas) mas falha no passo final deoutput: 'standalone'comEPERM: operation not permitted, symlink. É restrição do Windows (criar symlink exige Modo de Desenvolvedor ou admin), não da mudança: rodei limpo namaine 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 jobBuilddeste PR é a confirmação real.Sobre a mudança em
packages/ui. PeloAGENTS.md, exige aprovação de ao menos um integrante do squad de Front-End. A alteração é de uma linha noaddFilesmais um módulo auxiliar novo — sem mudança visual, sem mudança de API do componente.Sobre o id. É só
keyde React e chave de dedup local (uploadedLocalIdsRefemuseCreateAnnouncement.ts, lista de rascunho de curso). Nunca é persistido nem enviado ao servidor — o upload identifica a imagem pelo retorno do presign (announcementImagesClient.tsusaitem.idapenas no callbackonUploaded). Não há argumento de segurança contra trocarrandomUUIDpor esse fallback.Pós-merge (GitFlow, CONTRIBUTING.md §Emergência). Merge na
mainexige tag SemVer — sugestãov3.1.1— e merge de volta nadevelop.