Cloud Build supply chain security comes down to two things most GCP teams get wrong on day one: an over-privileged build service account, and zero cryptographic proof of what actually produced a given artifact. On March 15, 2023, the Open Source Security Foundation formalized SLSA v1.0 to address exactly this gap — a compromised build step can produce a "clean" artifact that passes every test yet ships a backdoor, because traditional pipelines only verify what code looked like, not what actually produced the binary. Google Cloud Build sits at the center of this problem for thousands of GCP-native teams: it checks out source, runs arbitrary build steps with a powerful service account, and pushes images to Artifact Registry, often with no record of which commit, which builder, or which dependencies went into the artifact. Fixing that means locking down what the build service account can touch and attaching verifiable SLSA provenance to every artifact so a tampered build gets caught before it reaches production. This post walks through the concrete mechanics of doing both.
What does Cloud Build supply chain security actually protect against?
The core risk is that Cloud Build runs every step in a build under a single, often over-privileged identity — the default PROJECT_NUMBER@cloudbuild.gserviceaccount.com service account — regardless of whether that step is trusted first-party code or a third-party dependency pulled from npm or PyPI. By default, this account is granted the Cloud Build Service Account role, which bundles broad permissions across Storage, Logging, and frequently Artifact Registry write access. If a single npm install in a build step executes a malicious post-install script (the pattern behind real incidents such as the 2021 ua-parser-js and 2022 node-ipc compromises), that script inherits the full permission set of the build's service account — not just read access to source, but potentially the ability to push new container images, write to Cloud Storage buckets, or read secrets from Secret Manager. Because Cloud Build triggers are commonly wired directly to GitHub or Bitbucket webhooks, a malicious pull request from an outside contributor can, in misconfigured setups, kick off a build before any human review happens. Combine that with the absence of build provenance — most teams have no artifact-to-source traceability beyond a Cloud Build log that eventually ages out — and a single compromised dependency can silently poison every downstream deployment.
How do you actually lock down Cloud Build service account permissions?
You lock it down by replacing the default broad-scope service account with a dedicated, least-privilege account scoped per trigger or per environment. Google's own hardening guidance recommends assigning roles like roles/artifactregistry.writer instead of blanket Storage Admin, and roles/logging.logWriter instead of project-level Editor — with no Secret Manager access unless a build step explicitly needs a credential, in which case it should be fetched via Workload Identity Federation rather than a long-lived JSON key. Concretely, this means editing your cloudbuild.yaml to set a serviceAccount field pointing to a purpose-built identity (for example, projects/PROJECT_ID/serviceAccounts/ci-build-prod@PROJECT_ID.iam.gserviceaccount.com) instead of relying on the implicit default. Teams should also split triggers by trust boundary — public-PR builds get a read-only, no-push account; main-branch builds get a separate account with registry push rights — and enforce the split with an Organization Policy constraint rather than tribal knowledge, since one engineer manually reassigning a trigger back to the default account undoes the entire control in a single click.
What is SLSA provenance and why does Cloud Build need it?
SLSA provenance is a signed, machine-readable statement — an in-toto attestation — that records exactly which source commit, which builder, and which build configuration produced a given artifact, and Cloud Build needs it because without that record, nothing downstream can distinguish an artifact built by your trusted pipeline from one built by an attacker with stolen credentials. Under SLSA v1.0, provenance documents are structured JSON containing fields like predicateType, builder.id, invocation.configSource, and materials — effectively a cryptographic receipt tying a container digest back to the exact git SHA, trigger, and worker pool that built it. The practical payoff is verification at deploy time: a Binary Authorization policy or admission controller can reject any image lacking provenance signed by a trusted builder identity, which directly blocks the attack where someone pushes a malicious image straight to Artifact Registry, bypassing the build pipeline entirely. This is the essence of SLSA provenance Cloud Build teams are increasingly required to produce — Cloud Build can generate this automatically, attaching it to the image as an attestation that tools like cosign verify-attestation or Binary Authorization check before allow-listing a deployment.
How does Cloud Build generate SLSA Level 3 provenance automatically?
Cloud Build generates SLSA Level 3 provenance automatically when builds run on Google-managed or private worker pools with the provenance feature enabled, because at that build level Google's infrastructure — not a user-controlled script — isolates the build and signs the resulting metadata. Concretely, when you build and push an image through Cloud Build (via docker build steps or Kaniko), Cloud Build attaches a provenance attestation to the image manifest for builds that meet SLSA Level 3 requirements: an isolated, ephemeral build environment, a non-forgeable provenance record, and a build service that itself can't be tampered with by the build's own steps. That's meaningfully stronger than the common pattern of running docker build && docker push from a self-hosted Jenkins job with no attestation at all — a setup that provides SLSA Level 0 assurance, meaning zero verifiable claims about how the artifact came to exist. Teams should confirm provenance generation is active by checking the build's Build.Results.provenance field via the Cloud Build API or gcloud builds describe, since private pools and some legacy trigger configurations require explicit opt-in rather than enabling it by default.
Why do secure CI/CD GCP practices need more than just provenance?
Provenance alone tells you what was built, but secure CI/CD GCP practices also need control over what the pipeline can reach while it's running, because a build that generates a perfectly valid provenance record can still exfiltrate secrets or poison a downstream artifact if its network egress and IAM scope are left unrestricted. Google's guidance pairs provenance with VPC Service Controls around the build's worker pool, Binary Authorization policies that require both provenance and vulnerability-scan attestations before deploy, and branch protection rules that prevent a cloudbuild.yaml change from silently expanding a build's permissions on the next run. In practice, that means a GCP shop in 2026 should treat the build configuration file itself as a security-sensitive artifact requiring the same code review as production code, since a one-line change adding a serviceAccount override or a new trusted trigger source can undo months of least-privilege work in a single merged pull request.
How Safeguard Helps
Safeguard is built to make Cloud Build supply chain security something teams can verify continuously, not something they configure once and hope holds. Safeguard continuously inventories every Cloud Build trigger, worker pool, and associated service account across your GCP projects, flagging any trigger still bound to the default broad-scope @cloudbuild.gserviceaccount.com identity and any build configuration with roles/editor or roles/owner attached — the exact drift that manual reviews miss between audits. On the provenance side, Safeguard ingests and independently verifies SLSA attestations generated by Cloud Build, cross-checking builder.id, source commit, and material hashes against your actual repository state, so a spoofed or replayed attestation gets caught instead of trusted at face value. Safeguard also maps dependency-level risk — the npm packages, base images, and third-party actions a given build pulls in — directly onto the Cloud Build service account permissions that dependency could reach if compromised, turning an abstract least-privilege goal into a concrete, prioritized list: which triggers to re-scope first, which secrets to move behind Workload Identity Federation, and which provenance gaps to close before the next release. For teams already running Binary Authorization, Safeguard validates that deployed images actually carry the provenance and attestations your policies claim to require, closing the gap between a policy that looks correct on paper and one that's actually enforced in production.