SCA, SBOM e secrets em CI/CD com Bitbucket Pipelines
Execute o WASViking Sentinel dentro do Bitbucket Pipelines para montar um SBOM CycloneDX, verificar dependências contra OSV e CISA KEV, analisar a working tree e o histórico do git em busca de credenciais embutidas no código e barrar uma release vulnerável antes que ela chegue à sua branch principal.
Este guia executa Análise de Composição de Software (SCA) e um scan de secrets dentro de um build do Bitbucket Pipelines. O WASViking® Sentinel lê os seus manifestos de dependências, monta um SBOM CycloneDX, enriquece-o com OSV e CISA KEV, percorre a working tree e o histórico do git em busca de credenciais embutidas no código e faz o build falhar antes que uma dependência vulnerável ou uma chave vazada chegue à sua branch principal.
Os dois gates usam o mesmo binário e a mesma API do guia de GitHub Actions. Nenhum deles está preso a um fornecedor de CI: o agente roda como um comando de execução única, autentica com uma chave de API da organização e envia os dados por HTTPS REST. O que muda entre fornecedores é o YAML em volta, mais dois detalhes do Bitbucket que passam despercebidos com facilidade e que esta página destaca explicitamente (a profundidade do clone e as variáveis protegidas).
Não há túnel mTLS neste fluxo. Nenhum código-fonte sai do runner, apenas o grafo de dependências (nomes e versões de pacotes) e as ocorrências de secrets mascaradas.
A configuração tem duas metades, e a ordem importa: configure
primeiro o portal da WASViking (uma chave de API com os escopos ci:scan,
sca:submit e secrets:submit) e depois conecte-a ao Bitbucket.
O que esta integração faz
- Gera um SBOM CycloneDX 1.5 a partir dos seus manifestos (npm, pip, go,
composer, Maven, gem, pub) em pull requests e em pushes para
main. - Enriquece os componentes com OSV e CISA KEV (vulnerabilidades com exploração conhecida).
- Analisa a working tree e, com
--git, o histórico do repositório em busca de secrets embutidos no código, enviando apenas ocorrências mascaradas. - Opcionalmente verifica uma ocorrência no endpoint de identidade do
provedor com
--verify, para que uma chave já rotacionada não pareça um incidente em andamento. - Faz o build falhar por severidade com
--fail-on, o que bloqueia o merge. - Grava o SARIF 2.1.0 e o JSON CycloneDX bruto como artefatos do build.
- Monta um inventário de software consolidado por organização e detecta drift entre envios consecutivos.
- Não deixa nada para trás no runner. O agente é instalado a cada build e morre junto com o container.
Como funciona
- O container do build baixa e instala o agente Sentinel usando a chave de API.
- O agente executa uma verificação prévia de licença (preflight) na API
da WASViking. A aprovação fica em cache por 30 minutos em
~/.wasviking/, o que, em um build do Bitbucket, significa que ela é buscada de novo a cada execução. - Ele percorre os manifestos do projeto e monta um SBOM CycloneDX.
- Ele enriquece os componentes com dados do OSV e aplica a validação CISA KEV.
- Ele analisa a árvore, e o histórico do git quando
--gitestá definido, em busca de credenciais embutidas no código. - Ele envia o SBOM e as ocorrências mascaradas por HTTPS REST. A API valida a cota, registra o snapshot, detecta drift de componentes e promove achados.
- O agente grava as saídas JSON e SARIF em
--out, e o Bitbucket coleta esse diretório como artefato do build.
Postura de acesso
- Apenas os manifestos e o grafo de dependências são lidos. Nenhum
código-fonte, nenhuma variável do repositório além da chave que você
informa e nada fora de
.wasviking/é coletado. - Os valores brutos dos secrets nunca saem do runner. Os envios carregam um hash e uma prévia mascarada.
- O envio é feito por HTTPS REST com a chave de API em um header. Não há túnel mTLS nem conexão de entrada para o runner.
--verifyadiciona chamadas somente leitura aos endpoints de identidade dos provedores das credenciais encontradas (por exemplo, a API de identidade do GitHub ou da AWS). Remova a flag se chamadas de saída para terceiros não forem aceitáveis na sua rede de build.--air-gappedno gatesbomgarante zero tráfego externo de saída: sem consulta ao OSV e sem envio.- Revogue o acesso a qualquer momento revogando a chave de API no portal.
Pré-requisitos
| Requisito | Detalhe |
|---|---|
| Plano da WASViking | Envios de SBOM e de secrets ativados (Pro ou superior). |
| Papel no portal | Admin ou Manager, para emitir chaves de API. |
| Bitbucket | Pipelines ativado no repositório, em Repository settings → Pipelines → Settings. |
| Permissão no Bitbucket | Admin no repositório, para criar variáveis de repositório protegidas e fazer o commit do bitbucket-pipelines.yml. |
| Imagem de build | Qualquer imagem Linux x86_64 com curl, sha256sum e dpkg-deb (ou ar, do binutils). A atlassian/default-image:5 tem todos eles. |
| Profundidade do clone | clone: depth: full quando você analisa o histórico do git com --git. O Bitbucket faz clone raso (shallow) por padrão. |
| Manifestos do projeto | Pelo menos um lockfile suportado: package-lock.json, yarn.lock, pnpm-lock.yaml, requirements.txt, Pipfile.lock, go.sum, composer.lock, pom.xml, Gemfile.lock, pubspec.lock. O lockfile precisa estar commitado no repositório. |
| Saída de rede | HTTPS para api.wasviking.com e api.osv.dev na porta 443. Nenhuma conexão de entrada é necessária. |
Usa GitLab CI, Jenkins, CircleCI ou qualquer outra ferramenta capaz de executar um binário Linux? Os comandos abaixo são idênticos. Só o dialeto do YAML muda. Comece por wasviking-sentinel em CI/CD.
Passo 1: Crie uma chave de API para o pipeline (portal)
Uma única chave de API cobre o build inteiro: ela autoriza o download do instalador do agente e os dois envios. Nenhum token de agente Sentinel entra aqui, isso pertence ao fluxo do túnel.
Vá em Settings → System Settings → API Keys e clique em + New Key.
| Campo | Valor |
|---|---|
| Label | Algo que você ainda reconheça daqui a seis meses, por exemplo Bitbucket SCA, checkout-api. Use uma chave por repositório, para que revogá-la não derrube outros pipelines. |
| Scopes | ci:scan (Trigger scans from a CI/CD pipeline, que também é o que autoriza o download do instalador), sca:submit (Send SBOMs from the Sentinel agent. OWASP A06) e secrets:submit (Send hard-coded credential matches from the Sentinel agent. OWASP A07). |
| Expiration | 90 dias é um padrão sensato. Faça a rotação nessa cadência. |
Salve e copie a chave, que aparece uma única vez. Você vai colá-la no
Bitbucket no Passo 2 como WASV_SCA_API_KEY.
A WASViking autentica com o header
Authorization: ApiKey <key>, e nãoBearer. Os escopos podem ser ajustados depois com Edit, sem alterar o valor da chave, de modo que o pipeline continua funcionando.
Settings → System Settings → API Keys.
Selecione os três escopos do pipeline ao criar a chave.
Passo 2: Guarde a chave como variável de repositório protegida
Nunca faça commit de uma chave no repositório. No Bitbucket, vá em Repository settings → Pipelines → Repository variables e adicione:
| Nome | Valor | Secured |
|---|---|---|
WASV_SCA_API_KEY |
A chave de API do Passo 1. | Sim |
Marque Secured (protegida) para que o valor seja mascarado no log do build e não possa ser lido de volta pela interface. Se vários repositórios compartilham uma mesma chave, uma variável de workspace funciona do mesmo jeito, embora uma chave por repositório deixe a revogação bem mais limpa.
Um comportamento do Bitbucket que vale prever: builds de pull request disparados a partir de um repositório que é um fork não recebem variáveis protegidas. A primeira linha do step abaixo é uma proteção que faz o build falhar imediatamente nesse caso, em vez de deixar o scan rodar sem autenticação e reportar um falso resultado limpo.
Passo 3: Adicione o pipeline
Crie o bitbucket-pipelines.yml na raiz do repositório:
image: atlassian/default-image:5
# Full Git history is required when using the secrets --git option.
clone:
depth: full
definitions:
steps:
- step: &wasviking-security-scan
name: WASViking SCA + SBOM + Secrets
max-time: 20
script:
- test -n "$WASV_SCA_API_KEY"
# Run from the repository root.
- cd "$BITBUCKET_CLONE_DIR"
# WASViking API endpoint.
- export WASV_API_BASE="https://api.wasviking.com"
# Install WASViking Sentinel.
- |
curl -fsSL \
-H "Authorization: ApiKey $WASV_SCA_API_KEY" \
"$WASV_API_BASE/api/v1/sentinel/install.sh" | sh
- mkdir -p wasviking-reports
# Generate CycloneDX SBOM, run SCA, and submit results.
- |
./.wasviking/wasviking-sentinel sbom \
--api "$WASV_API_BASE" \
--api-key "$WASV_SCA_API_KEY" \
--path . \
--app-name "$BITBUCKET_REPO_FULL_NAME" \
--app-version "$BITBUCKET_COMMIT" \
--fail-on high \
--submit \
--out ./wasviking-reports
# Scan the working tree and Git history for hard-coded secrets.
- |
./.wasviking/wasviking-sentinel secrets \
--api "$WASV_API_BASE" \
--api-key "$WASV_SCA_API_KEY" \
--path . \
--git \
--verify \
--fail-on high \
--submit \
--out ./wasviking-reports
artifacts:
- wasviking-reports/**
pipelines:
pull-requests:
'**':
- step: *wasviking-security-scan
branches:
main:
- step: *wasviking-security-scan
custom:
wasviking-security-scan:
- step: *wasviking-security-scan
Vale entender quatro escolhas desse arquivo antes de adaptá-lo:
clone: depth: full. A flag--gitpercorre o histórico de commits. Com o clone raso padrão do Bitbucket, o agente só enxerga os últimos commits, então uma credencial commitada meses atrás e depois removida da working tree continua invisível. Removadepth: fulle--gitjuntos se você quer analisar apenas a árvore atual, o que deixa os builds mais rápidos.- A âncora YAML (
&wasviking-security-scan). O step é declarado uma vez emdefinitionse referenciado três vezes. Pull requests e amainrecebem o gate automaticamente, e a entradacustompermite que qualquer pessoa o execute sob demanda em Pipelines → Run pipeline, o que ajuda no primeiro rollout. max-time: 20. Um teto em minutos para o step. Repositórios com histórico profundo ou muitos manifestos devem aumentá-lo. O agente tem os seus próprios timeouts, 5 minutos para o pipeline de SBOM e 10 para secrets, ambos ajustáveis com--timeout.artifacts. O Bitbucket mantémwasviking-reports/**anexado ao build, então as saídas SARIF e JSON podem ser baixadas na aba Artifacts depois da execução.
Faça o commit do arquivo e o pipeline começa no próximo push ou pull request.
Passo 4: Flags dos comandos
sbom (SCA e SBOM)
wasviking-sentinel sbom [flags]
| Flag | Obrigatória | Descrição |
|---|---|---|
--api-key |
Sim | Chave de API com o escopo sca:submit. Também lê WASV_API_KEY. |
--api |
Não | URL base da API da WASViking. Padrão: https://api.wasviking.com. |
--path |
Não | Diretório analisado recursivamente em busca de manifestos. Padrão: diretório atual. |
--app-name |
Não | Nome do projeto nos metadados CycloneDX. $BITBUCKET_REPO_FULL_NAME entrega workspace/repo. Informe-o: sem ele o agente recorre ao nome do repositório no CI, já que o diretório de checkout no Bitbucket sempre se chama build e daria um rótulo ruim para o inventário. |
--app-version |
Não | Versão do projeto nos metadados. $BITBUCKET_COMMIT amarra o SBOM ao commit exato. |
--fail-on |
Não | Menor severidade que faz o build falhar: critical, high, medium, low, none. Padrão: high. |
--submit |
Não | Envia o SBOM para a WASViking. Omita para uma execução apenas local. |
--out |
Não | Diretório de saída para o JSON CycloneDX e o SARIF. Padrão: diretório atual. |
--no-osv |
Não | Pula o enriquecimento com OSV e entrega um SBOM sem enriquecimento. |
--from-cyclonedx |
Não | Ingere um SBOM produzido por outra ferramenta em vez de gerar um. |
--air-gapped |
Não | Totalmente offline. Sem consulta ao OSV e sem envio. |
--timeout |
Não | Tempo máximo total (wall-clock) para o pipeline de SBOM. Padrão: 5 minutos. |
Saídas: wasviking-sbom.cdx.json (CycloneDX 1.5) e
wasviking-sbom.sarif (SARIF 2.1.0).
secrets
wasviking-sentinel secrets [flags]
| Flag | Obrigatória | Descrição |
|---|---|---|
--api-key |
Sim | Chave de API com o escopo secrets:submit. |
--api |
Não | URL base da API da WASViking. |
--path |
Não | Diretório analisado recursivamente. Padrão: diretório atual. |
--git |
Não | Percorre também o histórico do git. Exige clone: depth: full. |
--verify |
Não | Confirma as ocorrências nos endpoints de identidade dos provedores, somente leitura. |
--fail-on |
Não | Mesma escala e mesmo padrão (high) do gate de SBOM. |
--submit |
Não | Envia as ocorrências mascaradas para a WASViking. |
--out |
Não | Diretório de saída. |
--timeout |
Não | Tempo máximo total (wall-clock). Padrão: 10 minutos. |
Saídas: wasviking-secrets.json e wasviking-secrets.sarif.
Exit codes
O Bitbucket faz o step falhar em qualquer saída diferente de zero, então são estes códigos que transformam um achado em um merge bloqueado.
| Código | Significado |
|---|---|
0 |
Nada no limite de --fail-on ou acima dele. |
70 |
Gate de SCA: uma dependência sinalizada no KEV, no limite ou acima dele. Trate como urgente, é uma vulnerabilidade com exploração conhecida. |
71 |
Gate de SCA: achados no limite ou acima dele, nenhum deles no KEV. |
73 |
Gate de secrets: uma credencial confirmada como ativa pelo --verify. |
74 |
Gate de secrets: ocorrências no limite ou acima dele que não foram verificadas. |
79 |
Falha de cobertura: a raiz do scan não pôde ser percorrida, então nada foi analisado. Veja abaixo. |
1 / 2 |
Erro operacional: manifesto ilegível, chave de API rejeitada, falha de rede no --submit. |
Falha de cobertura (exit 79)
Um gate que não analisa nada não encontra nada e, sem uma verificação, isso se lê exatamente como uma execução limpa. O exit 79 separa as duas situações: o agente mede o que cobriu em relação ao que a raiz do scan realmente continha, e interrompe a execução em vez de reportar aprovação sobre uma varredura vazia.
Não é um achado, e não dispara em um repositório vazio nem em um sem manifesto suportado. Esses casos são percorridos corretamente e passam, e o log informa quantas entradas foram percorridas para que você veja a diferença.
O Bitbucket merece uma observação aqui. $BITBUCKET_CLONE_DIR é sempre
/opt/atlassian/pipelines/agent/build, então nesta plataforma a raiz do
scan se chama build, que também é um nome que o agente exclui dentro
dos projetos. As exclusões valem apenas para subdiretórios, então o
checkout é analisado por inteiro e o log registra que o nome da raiz foi
reconhecido. Um diretório build/ dentro do seu repositório continua
excluído, como deve ser. A referência completa está em
Falhas de cobertura.
Política de fail-on
--fail-on define a menor severidade que faz o build falhar, e tudo
acima dela também falha.
| Configuração | Comportamento |
|---|---|
--fail-on none |
Nunca falha. Apenas relata, útil na primeira semana. |
--fail-on critical |
Falha apenas em critical. |
--fail-on high |
Falha em high e critical. É o padrão, e um bom padrão para pull requests. |
--fail-on medium |
Falha em medium e acima. Mais rigoroso, mais atrito. |
--fail-on low |
Postura máxima. Realista em projetos novos, sem dívida acumulada. |
Um rollout que costuma sobreviver ao contato com uma equipe de verdade:
comece com none em pull requests para ver o volume, passe para high
quando o backlog estiver triado e mantenha a main mais rigorosa do que
as feature branches.
Como ler os resultados
O portal é o sistema de registro. Os dados de dependências ficam em Inventory → SBOM e as ocorrências de credenciais em Inventory → Secrets, ambos com os achados promovidos vinculados à postura da sua organização.
Os arquivos SARIF são gravados para ferramentas que consomem o formato. O Bitbucket não ingere SARIF nativamente, então nesta plataforma eles são artefatos do build que você pode baixar ou entregar a outra ferramenta, e não uma camada de anotações no pull request. O log do build traz a linha de resumo, e o portal guarda o histórico.
O drift compara o conjunto de componentes deste envio com o do envio
anterior que carrega o mesmo --app-name, e ignora --app-version
por completo. Um commit que não altera nenhuma dependência não reporta
drift, mesmo que a string de versão tenha mudado.
Isso faz de --app-name o valor a manter estável. Se ele varia entre
execuções, cada envio inicia a sua própria linhagem, sem nada com que
comparar, e o drift silenciosamente nunca dispara. Derivá-lo de
$BITBUCKET_REPO_FULL_NAME, como faz o pipeline acima, mantém o valor
constante durante toda a vida do repositório.
Modo air-gapped
Redes de build sem saída externa ainda conseguem produzir um SBOM:
./.wasviking/wasviking-sentinel sbom \
--path . \
--air-gapped \
--out ./wasviking-reports
--air-gapped pula a consulta ao OSV e o envio, trabalhando com os dados
de KEV embutidos. Use em build farms isoladas ou para validar localmente
antes de entregar um artefato manualmente.
O que é e o que não é coletado
A WASViking não coleta o código-fonte do seu repositório, variáveis do
repositório além da chave que você informa, arquivos fora de
.wasviking/ nem qualquer dado de produção. O SBOM contém o grafo de
dependências (nomes de pacotes, versões, licenças, hashes de manifestos).
O gate de secrets envia um hash e uma prévia mascarada, nunca a credencial
em si. Os dados são processados na região dos Estados Unidos,
criptografados em trânsito (TLS 1.2+) e em repouso (AES-256), com retenção
definida pelo seu plano. Um DPA e residência de dados na UE estão
disponíveis sob solicitação. Consulte o
Trust Center para o mapeamento
completo de conformidade (ISO 27001, SOC 2, LGPD, GDPR, NIST SSDF,
OWASP Top 10 A06/A07, OWASP DSOMM, elementos mínimos de SBOM da CISA).
Problemas comuns
| Problema | Causa provável |
|---|---|
| O step falha na primeira linha, antes de qualquer saída | WASV_SCA_API_KEY não está definida. Em um pull request vindo de um fork, as variáveis protegidas não são entregues ao build. |
HTTP 401 Unauthorized |
Chave revogada, expirada ou sem ci:scan, sca:submit ou secrets:submit. O header é Authorization: ApiKey <key>, e não Bearer. |
HTTP 402 quota exceeded |
Cota mensal de envios atingida. Aguarde o próximo ciclo, faça upgrade ou adicione um pacote. |
| O scan de secrets não encontra nada no histórico | Falta clone: depth: full, então o clone raso não tem histórico para percorrer. |
neither dpkg-deb nor ar is available |
Uma imagem de build mínima (Alpine e similares). Instale o binutils ou troque o step para atlassian/default-image:5. |
Erro no download do install.sh |
Política de rede bloqueando api.wasviking.com ou o bucket de releases. |
| Step encerrado aos 20 minutos | Histórico profundo somado a --verify em um repositório grande. Aumente o max-time e o --timeout do agente, ou remova --verify nos pull requests e mantenha na main. |
| Exit 79 | A raiz do scan não pôde ser percorrida. Compare a linha Root: com o layout do seu repositório e depois confira se o step consegue ler o checkout. |
manifest not detected |
Nenhum lockfile suportado em --path. Faça o commit dos seus lockfiles. |
| Timeout do OSV | api.osv.dev lento ou inacessível. Use --no-osv para um SBOM sem enriquecimento, ou --air-gapped. |
| Os achados diferem entre execuções | Um lockfile regenerado durante o build. Instale com npm ci ou equivalente e faça o commit do lockfile. |
| Exit 70 inesperado em uma mudança pequena | Uma nova dependência transitiva chegou pelo lockfile e corresponde ao CISA KEV. Inspecione com npm ls <package> ou equivalente. |
Onde isto se encaixa na plataforma
- O inventário de software fica em Inventory → SBOM, e as ocorrências de credenciais em Application Security → Hard-coded Secrets.
- A versão deste mesmo pipeline para GitHub Actions é SCA, SBOM e secrets no CI/CD com GitHub Actions.
- O gate de DAST a partir da nuvem é um terceiro subcomando do mesmo binário, com o mesmo modelo de chave de API. Veja DAST em CI/CD com Bitbucket Pipelines para o exemplo completo no Bitbucket, wasviking-sentinel em CI/CD para a referência e DAST em CI/CD com GitHub Actions para a versão do GitHub.
- Prefere não mudar nada no pipeline? O Code Security executa os mesmos mecanismos de SBOM e de secrets no lado do servidor, nos repositórios que você conecta pelo GitHub App ou pelo Bitbucket OAuth, na cadência do seu plano e a cada push; o gate no pipeline continua sendo o caminho para bloquear um build.
- O roteamento de alertas está documentado em Slack e Teams e Webhooks.
