CI/CD
Cache do GitHub Actions: como funciona e o que esconde
A cache do GitHub Actions guarda ficheiros entre execuções de workflows, para que os jobs evitem transferências que já fizeram. Torna os builds mais rápidos. Significa também que há pacotes que chegam ao seu build sem serem obtidos, e a maioria dos registos de um build nunca os vê.
Atualizado a
Como funciona a cache
A action actions/cache guarda um conjunto de caminhos sob uma chave no fim de um job e restaura-os no
início de um job posterior. Uma configuração típica de npm coloca em cache a cache de transferências do gestor de pacotes e usa como chave o lockfile:
- uses: actions/cache@v4
with:
path: ~/.npm
key: npm-${{ runner.os }}-${{ hashFiles('package-lock.json') }}
restore-keys: |
npm-${{ runner.os }}-
- run: npm ci
Ao restaurar, a action procura primeiro uma correspondência exata com key, depois uma correspondência parcial e depois cada
prefixo de restore-keys, por ordem. Quando várias caches correspondem a um prefixo, restaura a criada mais
recentemente. Essa alternativa é o que mantém os builds rápidos depois de o lockfile mudar, e significa também que um job pode começar a partir de uma
cache criada para um lockfile mais antigo.
Muitas vezes, nem precisa da action. setup-node, setup-python, setup-java,
setup-go, setup-ruby e setup-dotnet têm cache integrada. Para Node.js,
setup-node coloca em cache os dados globais de pacotes de npm, Yarn e pnpm, não node_modules, e ativa
automaticamente a cache de npm quando package.json declara npm como o seu packageManager.
Âmbito e limites
| Regra | O que o GitHub faz |
|---|---|
| Que caches uma execução pode restaurar | As da sua própria branch e da branch predefinida; os pull requests também as da sua branch base |
| O que um pull request pode escrever | Apenas caches limitadas à sua merge ref, não à branch predefinida |
| Armazenamento | 10 GB por repositório por defeito; os proprietários de organizações e empresas podem aumentá-lo |
| Remoção | Entradas sem acesso há mais de 7 dias e, quando o armazenamento está cheio, as usadas há mais tempo |
| Segredos | Não os coloque em cache: qualquer pessoa com acesso a pull requests pode ler o conteúdo das caches |
O que a cache esconde da sua SBOM
Uma cache é um atalho que evita o registo de pacotes. É essa a sua finalidade, e é também por isso que confunde todos os registos do que um build usou:
- Sem transferência, sem rasto. Os pacotes restaurados de uma cache são instalados sem um pedido ao registo. Um registo construído a partir do tráfego de rede do build não os vê, a menos que siga a cache.
-
Conteúdo antigo sob uma chave nova. Uma correspondência parcial de
restore-keysrestaura uma cache criada para um lockfile anterior. A instalação acrescenta ou substitui depois o que mudou, mas aquilo de que o job partiu não é o que o lockfile atual descreve. - Mais do que as suas dependências. As caches também guardam ferramentas de build, compiladores e ficheiros gerados, que nenhum manifesto de aplicação lista.
- Uma análise não o consegue saber. Uma SBOM baseada no lockfile descreve o que o lockfile diz, quer o job tenha instalado exatamente isso a partir da sua cache, quer não.
Para ver como isto se enquadra no problema mais amplo, consulte porque é que um lockfile não é um registo do que foi distribuído e dependências transitivas.
Envenenamento de cache
As caches são partilhadas entre os workflows de um repositório, o que as torna um caminho entre eles. Em maio de 2024, o
investigador de segurança Adnan Khan mostrou que qualquer pessoa capaz de executar código no contexto da branch predefinida, através de um
workflow pull_request_target vulnerável ou de uma dependência comprometida, pode substituir ficheiros em workflows
que restauram caches. Com o token de cache de um job em execução, um atacante pode encher a cache do repositório para que o
GitHub remova as entradas legítimas e depois escrever entradas envenenadas sob as mesmas chaves. Um workflow de lançamento que as restaure
executa os ficheiros do atacante com os seus próprios segredos.
Para reduzir o risco:
- Compile os lançamentos a partir de um estado limpo ou de caches que só o workflow de lançamento escreve.
- Nunca execute código de pull requests não fidedignos num contexto da branch predefinida.
- Mantenha segredos e tokens fora dos caminhos em cache.
- Trate o que uma cache restaurou como uma entrada do build que exige o mesmo escrutínio que uma transferência.
Como o CRACI trata as caches
O CRACI é um runner de GitHub Actions, pelo que actions/cache e as caches setup-* funcionam como
hoje, a par da cache de camadas de contentores. A cache é também parte da razão pela qual o CRACI executa builds cerca de duas vezes mais depressa do que os
runners alojados no GitHub, com hardware mais rápido e tempos de fila mais curtos.
O que muda é o registo. O proxy do CRACI que reconhece pacotes regista os pacotes que um job obtém, e as evidências acompanham as caches: quando um job restaura uma cache, o CRACI transporta as dependências que conhece para a SBOM desse job.
- Completude por cache. Cada cache tem um estado de completude, tal como cada job. Um job que restaura uma cache incompleta recebe uma SBOM incompleta, e uma cache que esse job produza transporta esse estado, pelo que uma lacuna nunca se transforma silenciosamente num registo completo.
- Sem adivinhar a partir do arquivo. O CRACI não lê manifestos de pacotes dentro do arquivo da cache. Sabe o que está numa cache porque registou o job que a produziu.
-
Uma cache por job produtor mantém essas evidências fiáveis, juntamente com uma política de egress definida como
default: denyno job produtor. O guia de cache e completude da SBOM tem os pormenores.
Consulte SBOM durante o build para saber como funciona o registo, ou marque uma demo para executar um dos seus workflows com cache no CRACI.
Cache do GitHub Actions: perguntas frequentes
Durante quanto tempo o GitHub guarda uma cache do Actions?
O GitHub remove as entradas de cache sem acesso há mais de 7 dias. Quando um repositório ultrapassa o seu limite de armazenamento, 10 GB por defeito, remove também caches por ordem do último acesso, começando pelas mais antigas.
O setup-node coloca node_modules em cache?
Não. A entrada cache de actions/setup-node coloca em cache os dados globais de pacotes do gestor de pacotes, para npm, Yarn ou pnpm, não node_modules. O passo de instalação continua a ser executado, mas lê os pacotes da cache restaurada em vez de os transferir.
Um pull request pode ler caches da branch main?
Sim. Uma execução de workflow pode restaurar caches da sua própria branch e da branch predefinida, e um pull request pode também restaurar caches da sua branch base. As caches criadas num pull request ficam limitadas à sua merge ref, pelo que não podem ser escritas no âmbito da branch predefinida.
É seguro usar a cache do Actions em workflows de lançamento?
É um compromisso. Uma investigação publicada em 2024 mostrou que qualquer pessoa capaz de executar código num workflow da branch predefinida pode envenenar caches que outros workflows restauram, incluindo workflows de lançamento. Os workflows que publicam versões ou têm acesso a segredos de implementação ficam mais seguros se compilarem a partir de um estado limpo ou de caches que só eles escrevem.
Uma cache restaurada altera a minha SBOM?
Pode alterar. Os pacotes restaurados de uma cache são instalados sem serem transferidos, pelo que um registo construído a partir do tráfego de rede não os vê, a menos que acompanhe a cache. E uma análise do lockfile não lhe diz se a cache continha exatamente essas versões.
Mantenha as suas caches e uma SBOM completa
Execute o seu workflow no CRACI: as caches continuam a acelerar o build e as dependências que transportam ficam na SBOM registada de cada job.
Marcar demo