Vendored code: o ponto cego mais burro do SCA
Toda ferramenta de SCA (Software Composition Analysis) parte de duas premissas silenciosas:
- existe uma dependência declarada (um
package.json,requirements.txt,pom.xml,go.mod); - o risco é um CVE conhecido amarrado a uma versão dessa dependência.
Tire qualquer uma das duas do caminho e o scanner fica cego. Esta série toda — “Atacando o pipeline” — mora exatamente nesses pontos cegos. O de hoje é o mais bobo e, por isso, o mais comum.
O ataque: parar de declarar a dependência
O SCA lê o manifesto. Ele não lê o que de fato roda. Se em vez de
// package.json
"dependencies": { "left-pad": "^1.3.0" }
você pega o fonte da biblioteca e cola dentro do seu próprio repositório —
src/vendor/left-pad.js — o manifesto não menciona left-pad em lugar nenhum.
Pro scanner, aquilo é código seu. Vendoring.
Vendoring é uma prática legítima: travar uma versão, aplicar um patch que o upstream não aceitou, cortar uma dependência de build. O problema não é a técnica — é que ela transfere a biblioteca do mundo “auditado pelo SCA” pro mundo “código próprio, ninguém olha o CVE”.
E aí o abuso aparece em duas camadas:
- Acidental: alguém vendoriza uma lib há dois anos, ela acumula CVEs, e o
dashboard do SCA continua verdinho porque
left-padnunca esteve no manifesto. - Proposital: um atacante com acesso de commit cola uma versão backdoorada (ou uma lib vulnerável de propósito) e a chama de “utilitário interno”. Zero alerta.
O ponto é o mesmo: o manifesto deixou de ser a fonte da verdade sobre o que existe no build.
A regra que pega: SBOM do filesystem, não do manifesto
A correção não é “proibir vendoring” — é parar de confiar só no manifesto pra saber o que está no artefato. O controle é gerar o SBOM (Software Bill of Materials) a partir do que realmente está lá: o filesystem e o binário final, não a lista de dependências declaradas.
Ferramentas como o Syft varrem o diretório /
imagem e identificam componentes por conteúdo — assinatura de arquivo,
hashes conhecidos, padrões de código — não por entrada no package.json. Um
left-pad colado em src/vendor/ aparece no SBOM mesmo sem estar declarado.
Com o SBOM na mão, o SCA passa a escanear o que o build é, não o que ele
diz que é.
No pipeline, em ordem de maturidade:
- Gerar SBOM do artefato (
syft dir:. -o spdx-json) e versioná-lo junto do build. - Escanear o SBOM (ex.: Grype/Trivy) em vez de só o manifesto.
- Complementar com SAST (Semgrep) pra pegar o padrão de código colado — útil quando o vendoring foi minimamente ofuscado e o Syft não bate a assinatura.
# exemplo de gate no CI
- run: syft dir:. -o spdx-json=sbom.json
- run: grype sbom:sbom.json --fail-on high
Resumindo
| Ataque | colar o fonte da lib no repo → some do manifesto → some do SCA |
| Por que funciona | SCA confia no manifesto como fonte da verdade |
| Regra | SBOM gerado do filesystem/binário (Syft) + SAST no padrão colado |
É o primeiro post da série porque é o bypass que todo mundo já executou sem querer — e entender ele é entender a premissa que todos os próximos quebram: o SCA não sabe o que ele não consegue enxergar.
No próximo: código que está limpo na hora do scan e baixa o payload depois,
no install — time-of-check contra time-of-use.