Pular para o conteúdo
Development

Um gate que nunca roda não é um gate

Por Victor Da Luz•
rustcisecuritydev-loggreenhouse

Uma revisão do pipeline de CI do Greenhouse encontrou o tipo de achado que é pequeno individualmente mas preocupante no conjunto: uma lista de 24 comandos Tauri duplicados manualmente entre o limite de IPC em TypeScript e o registro de handlers em Rust, sem nada pegando divergência entre os dois. Um teste de contraste de cor com Chromium real que existia no repositório mas rodava em lugar nenhum no CI. Nenhuma auditoria de dependências. Um workflow de release que compilava e enviava um .dmg sem nunca abri-lo. O issue de acompanhamento era simples: trabalhar a lista até o fim.

A maior parte foi como você esperaria. O teste de contrato de IPC é algumas linhas de regex comparando dois arquivos entre si. O smoke test de release monta o .dmg, checa a assinatura de código, abre o app com um caminho de vault descartável para pular o diálogo de onboarding, e confirma que ele ainda está vivo cinco segundos depois. Bem direto.

A auditoria de dependências foi onde ficou interessante. O issue citava um aviso específico para ignorar, GHSA-wrw7-89jp-8q8g no crate glib. Fui atrás, mapeei para RUSTSEC-2024-0429, escrevi a entrada de ignore, rodei cargo deny check advisories para confirmar que funcionava, e obtive um resultado limpo com um aviso: “advisory was not encountered”. O crate que o issue estava preocupado nem está mais no grafo de dependências atual. Mas a checagem de verdade encontrou dezoito outros avisos que eu não estava procurando. Dois deles eram vulnerabilidades reais, não apenas avisos de desatualização: bugs de tempo quadrático e alocação sem limite no quick-xml, trazidos transitivamente pelo crate plist que o Tauri usa para empacotamento no macOS. Os dois foram resolvidos de graça com um bump de versão só no Cargo.lock, sem mudança de manifesto necessária. Os outros dezesseis eram avisos unmaintained contra os bindings GTK3 do próprio Tauri para Linux e uma dependência antiga de Unicode, nada que eu consiga corrigir a partir deste repositório, então esses ganharam ignores individuais documentados, cada um com sua justificativa, em vez de uma exceção genérica.

A parte que mais ficou comigo, porém, foi o próprio gate de contraste, exatamente o que o issue tratava: uma checagem que existe mas não roda. Conectei o npm run a11y:contrast ao CI, rodei localmente primeiro para garantir que ia passar, e não passou. Dois testes falharam. Não instável, não ambiental, simplesmente errado. Um commit anterior tinha adicionado uma chamada getConfigStages() ao dashboard e ao assistente de onboarding para que os nomes de estágio pudessem ser resolvidos a partir da config ao vivo em vez de adivinhados de um ID bruto. Os testes baseados em jsdom que rodam no CI já tinham mocks para essa chamada. O teste de contraste com Chromium real, que não roda em lugar nenhum, não tinha. Então um componente quebrava tentando fazer .map() sobre uma lista de config nula, e o outro desabilitava silenciosamente seu próprio botão “Próximo” para sempre porque a promise que ele aguardava resolvia para nulo em vez de um array vazio. Ninguém percebeu, porque não havia etapa de CI para perceber. É todo o argumento deste issue resumido num bug: um gate que existe no repositório mas não roda em lugar nenhum é funcionalmente idêntico a nenhum gate, exceto que também dá uma falsa confiança de que existe um.

Achado bônus pequeno pelo caminho: o mesmo arquivo de teste não tinha configuração headless no Playwright, então toda execução local desse teste abria uma janela real e visível do Chromium, roubando o foco. Ninguém tinha rodado o suficiente em sequência para achar isso irritante até essa semana.

Seis das sete peças foram resolvidas de forma direta, só com pesquisa e verificação local. A sétima, colocar o Chromium no runner de CI, precisava de um fato que quem estava trabalhando nisso não conseguia obter de dentro do repositório: se o runner Linux self-hosted já tinha as bibliotecas compartilhadas de sistema operacional que um navegador headless precisa. Essa eu consegui responder diretamente, já que sou eu quem administra o host: a mesma máquina já roda Chromium headless para as checagens de acessibilidade de outro site, então a correção foi só baixar o binário do navegador, sem precisar de nenhum pacote de sistema novo. Vale lembrar que algumas perguntas realmente precisam de alguém com acesso SSH, e é mais rápido perguntar do que adivinhar e descobrir com um CI vermelho.

Leitura relacionada

Você também pode achar útil

Proton

Proton VPN

VPN comercial com filtragem NetShield e interruptor de desligamento automático.

Como parceiro da Proton, ganho com compras qualificadas dos serviços de privacidade e segurança da Proton (Pass, Mail, VPN, Drive).

Saiba mais
NordPass

NordPass

Gerenciador de senhas da equipe por trás da NordVPN, com um plano gratuito.

Como afiliado da NordPass, ganho com compras qualificadas.

Saiba mais
RackNerd

RackNerd VPS

Hospedagem VPS econômica para serviços leves que funcionam continuamente.

Como afiliado da RackNerd, ganho com compras qualificadas.

Saiba mais