ci-builder-image-hero (1)

Your CI Runner Image Can Deploy to Prod. Who’s Scanning It?

Gilad Golani November 03, 2026

Your CI Runner Image Can Deploy to Prod. Who’s Scanning It?

Book A Demo
See how Upwind uses runtime context to turn cloud security fingdings into real-time protection.
Book a Demo

How to find, rank, and trace the most privileged containers in your cloud before an attacker does

Key Takeaways

  • Build images hold deploy roles, registry push rights, and signing keys. Yet risk models that rank by internet exposure score a CI builder below a public API.
  • Build jobs finish before a scheduled scan arrives. And the runner image often has no owner: security assumes platform owns it and platform assumes security scans it.
  • Cloud and AI security platforms already scan registries. Upwind also ties each CI image to what it can reach and what it built, so “are we affected?” takes minutes instead of days.

Every recent supply-chain attacks hit the build path

Someone reports a compromise in a popular CI dependency, and security asks one question in the incident channel: “Are we affected?” Production dashboards are green, runtime alerts are quiet, and vulnerability reports show nothing new on any internet-facing workload. Three days later, the team is still exporting registry lists into spreadsheets. Nobody can say what the build agents can reach.

That’s how some of the best-known supply-chain attacks played out. In 2020, attackers compromised SolarWinds’ build system rather than the product itself. A backdoored update reached about 18,000 customers. In 2021, attackers used a credential exposed in Codecov’s Docker image build process to tamper with a CI script. For about 2 months, that script stole secrets from customer pipelines. In 2024, a poisoned build pipeline published crypto-mining releases of Ultralytics, a popular computer vision library. And in 2025, a compromised GitHub Action dumped CI secrets into build logs. That action, tj-actions/changed-files, ran in more than 23,000 repositories.

Every one of these attacks landed on the build path. Yet production monitoring had nothing to say about any of them. The stakes are high because build images hold deploy roles, registry push rights, and signing keys. If an attacker compromises one, every image it ever produced becomes suspect.

CI/CD images slip past standard security controls, and ranking them by exposure gets the risk backwards. Here is how to scan, classify, rank, and trace them, including when your CI runs on someone else’s infrastructure.

Images build images, and nobody ranks the builder

Take a pipeline running in a tool like Buildkite. An agent pulls a container image, which might be a builder, a deploy tool or an ML training image. That container checks out source code, takes on a cloud role, builds new images, signs them, pushes them to the registry and deploys them to production. Then it disappears.

image-3-1024x546

Security controls focus on what the pipeline produces, while the privilege lives in the image that runs it.
Because these images carry the label “not production,” three things go wrong:

  • Prioritization ignores them. Most risk models rank findings by production and internet exposure, so a critical vulnerability in a builder image scores below a medium one in a public API.
  • Runtime scanning misses them. A build job lasts seconds or minutes and is gone before a scheduled scan arrives.
  • Nobody owns them. The platform team built the runner image once, over a year ago. Since then, security has assumed platform owns it, and platform has assumed security is scanning it.

Put the control in the registry and at runtime

We considered four places to put the control.

In the pipeline

Scanning inside the pipeline is the most common answer. But it only checks the images a pipeline builds, not the image the pipeline runs on. If an attacker compromises the builder itself, the scan runs inside a compromised job.

In the registry

The registry is where every image waits between build and run. It’s also the one place a short-lived CI image stays put. Every agent pulls from it, however long the job runs. It also keeps working when you don’t own the runner, because you don’t install anything where the job runs.

At admission

Admission control can stop unapproved images from entering a cluster. But build agents often run outside those clusters, and an approved image says nothing about what that image can access.

At runtime

Runtime is the ground truth. When the Upwind Sensor sits on a self-hosted build agent, it sees what the job did: which role it assumed, what it pulled, what it pushed, and whether it ever loaded a vulnerable package. The problem is the jobs it can’t see. A hosted runner is gone in 90 seconds, and you can’t install a sensor on infrastructure you don’t own.

So the answer is both: the Upwind Sensor wherever it can run, and the registry for everything it can’t see.

Find the builders that can hurt you

Most vulnerabilities in cloud workloads come from the packages and base images inside container images and, increasingly, from the tools that build them. The registry is where all of those sit. Registry scanning answers “what’s in the image?” But to answer “does it matter?”, you need three more pieces of context: the image’s role, its reach, and its lineage.

Scan where the images sit still

Start by connecting every registry your build agents pull from, not just the repositories that hold application images. That means the repositories for CI tools, base images, deploy tooling, and ML, plus any mirrors or caches in between.

One lesson we learned early is that an image’s name doesn’t tell you which version ran. “builder:latest” today may be a different image from “builder:latest” last month. Track findings against each exact version (its digest), and map every tag that points to it back to that digest. Otherwise, there’s no reliable way to answer “was this image vulnerable when it ran?”

Classify every image by role

You can’t prioritize what you can’t name. Upwind labels each image as a production workload, CI/CD tooling, test or dev, a base image, or an ML training image. It bases the label on where the image runs, what it is, and how it gets pulled.

In practice, one image can play more than one role. For example, teams often reuse a “tools” image for both CI jobs and a long-running operations service. It’s safer to treat role as a set of labels and to judge the image by its most privileged use.

Rank CI images by what they can reach

A build image is almost never internet-facing, so exposure-based scoring consistently underrates it. Instead, Upwind ranks it by what it can reach: deploy roles, registry push rights, signing keys, and secrets. For ML images, that also means sensitive data. That reverses the usual ranking:

ImageWorst vulnerabilityInternet-facingWhat it can reachPriority
CI builderHighNoDeploy rights in every production account1
ML training imageMediumNoRaw customer data2
Public APICriticalYesOne service and one storage bucket3
Test runnerCriticalNoNothing outside the test account4

The riskiest settings often hide in cloud trust policies. A single overly broad rule can let any pipeline, on any branch, take on a production deploy role. Tightening those rules to specific pipelines and branches turned out to be one of the fastest wins.

Trace each builder to everything it built

When an attacker compromises a builder, the question changes from “is this image vulnerable?” to “what did it produce, and where is that running now?” For each builder, Upwind lists every image it produced. For each of those images, it shows where the image runs today, whether it’s internet-facing, and who owns it.

That single view is both the blast radius and the remediation list: what to rebuild, who owns it, and which credentials to rotate. Older images often have no build record at all. It’s worth flagging them as findings in their own right rather than leaving them out.

When you don’t own the runner

Many teams run their pipelines on third-party hosted CI, such as GitHub-hosted runners, Buildkite hosted agents, CircleCI, or Bitbucket Pipelines. The job runs on the vendor’s infrastructure. There’s no way to install a sensor on the runner or deploy a scanner next to it. Jobs also tend to pull public images and third-party plugins that never pass through your own registry. tj-actions hit exactly there: a third-party action running on hosted runners, outside anything its victims controlled.

You may not own the runner, but the job still has to touch three things you do own.

The images it pulls

Route public images through a registry you control and pin them to a specific version. That brings them into view of registry scanning without installing anything on the runner. Third-party actions and plugins are code rather than images, so pin and review them in the pipeline itself.

The cloud role it uses

The runner belongs to the vendor, but you define the role it takes on in your own cloud account, so Upwind can still measure what that role can reach. Your cloud audit logs also record every time a pipeline uses the role. With no sensor in the job, your warning sign is an unexpected pipeline taking on a deploy role, or a CI role that starts reading secrets.

The images it produces

Outputs land back in your registry and run in your clusters, so tracing each builder to the workloads it created works as it does with self-hosted agents.

Hosted CI takes the runner out of reach, and with it visibility inside the job and into third-party plugins. But it doesn’t take away your registry, your cloud permissions, or your audit logs. You keep visibility of everything the job can touch. For blast radius, that’s the part that matters.

Give build images the same scrutiny as production

A builder that can push, sign, or deploy is as dangerous as anything in production. Rank it by the deploy roles, signing keys, and secrets it can reach. Start with these five steps:

  1. Connect every registry your build agents pull from, including mirrors and base-image repos.
  2. Route public images through a registry you control, and pin third-party actions to a full commit SHA.
  3. Scope every CI trust policy to specific pipelines and branches.
  4. Flag runner images older than 6 months, or with credentials baked into layers.
  5. For each builder, list the images it produced and where they run today.

If you want to see which of your runner images can deploy to production today, book a demo, and we’ll map your build path with you.

Demo the Upwind Platform

See how Upwind is reducing risk and strengthening cloud security for global enterprises.
Get a Demo
Contents
Add the Upwind RSS Feed to Slack
Connect the Upwind RSS Feed to your Slack.
Follow the how-to here.
Threat RSS
Add the Upwind RSS Feed to Slack
Connect the Upwind RSS Feed to your Slack.
Follow the how-to here.
Main RSS