A compromised CI/CD runner can expose much more than the secret that first triggered the incident. The real blast radius depends on which jobs executed while the runner was untrusted, which credentials those jobs or the host could access, which repositories and networks were reachable, and whether artifacts, caches, packages or deployments left the compromised environment.
Direct answer
What should you scope after a CI/CD runner compromise?
Start with the exposure window and enumerate every job that crossed the runner during that period. For each job, map repository access, job tokens, long-lived secrets, environment credentials, cloud identity, package registries, artifacts, caches, signing credentials, network reachability and deployment targets.
Critical distinction
Rebuilding the runner closes only one part of the incident
A clean runner rebuild does not revoke credentials already copied by an attacker, remove malicious artifacts already published, reverse repository changes or undo cloud actions already performed.
Exposure
Credential accessible
Attacker-controlled code could reach a token, secret, host credential or workload identity.
Evidence
Unauthorized use
Audit or service telemetry shows the credential or access path was actually used.
Impact
Downstream compromise
Repository, cloud, registry, artifact, deployment or another connected system was modified or abused.
In this guide
- Runner compromise vs workflow compromise
- Establish the exposure window
- Map every affected job
- Repository exposure
- Job-scoped tokens
- Long-lived CI/CD secrets
- Environment secrets
- Cloud and OIDC access
- Runner-host credentials
- Network reachability
- Shared and persistent runners
- Cache poisoning
- Artifact integrity
- Registries and signing keys
- Exposure vs unauthorized use
- Contain and recover
- FAQs
Runner compromise and malicious workflow execution are not exactly the same incident
A CI/CD workflow is designed to execute repository-controlled automation. If attacker-controlled code reaches a job, that job may access whatever the workflow and runner make available.
A runner compromise is broader. It means the execution environment itself can no longer be trusted.
| State | What it means | Primary investigation concern |
|---|---|---|
| Malicious job or action | Attacker-controlled code executed during a workflow | What that specific job could reach |
| Runner compromised | The execution environment is untrusted | Credentials, source, files and network resources reachable from the runner |
| Persistent runner compromise | Unauthorized changes survive after the original job | Later jobs and later credentials may also become exposed |
| CI control-plane compromise | Repository, organization or runner-management controls are compromised | Potential scope can extend beyond the runner host itself |
TPS rule: A compromised runner defines an exposure environment. It does not automatically prove every credential, repository or cloud account connected to the CI platform was compromised.

Step 1: Establish the runner exposure window
The investigation becomes manageable only when you establish when the runner stopped being trustworthy.
Build a timeline containing:
- last known-good runner state
- first suspicious or malicious execution
- first confirmed unauthorized execution
- evidence of persistence, if any
- every job that executed afterward
- time the runner was isolated
- time the runner was removed or rebuilt
- credential revocation times
- artifact withdrawal or rebuild times
If the exact compromise time is unknown, use the earliest defensible suspicious point and widen the investigation rather than inventing a precise start time.
THE MALICIOUS JOB ENDING ≠ THE EXPOSURE WINDOW ENDING. On a persistent runner, attacker-controlled changes can survive and capture credentials from later jobs.
Step 2: Identify every job that ran during the exposure window
This is the core blast-radius exercise.
For each job, record:
- repository
- workflow name
- workflow run and job identifier
- branch, tag or triggering event
- runner or runner group
- job permissions
- referenced secrets
- environment used
- cloud role or workload identity
- package or container registries accessed
- artifacts created or downloaded
- caches restored or written
- deployment targets
- internal network resources reachable
GitHub’s current security-incident guidance specifically recommends using workflow runs and logs to determine what executed, which credentials a job could access and how suspicious activity correlates with audit events.
Repository exposure is not one single state
A runner normally receives source code because it must build or test it. Repository impact should therefore be classified separately.
Code read
Private source checked out onto the runner may be visible to attacker-controlled processes.
Repository write
A token with write permission may allow commits, tags, branches or other repository changes.
Release / workflow influence
Broader permissions may allow changes that affect future CI executions or software releases.
NO SECRET STOLEN ≠ NO SOURCE CODE EXPOSED. Source code and credentials are different exposure classes.
If one specific GitHub token becomes part of the investigation, use the TPS guide GitHub Token Exposed: How to Check What It Accessed Before and After Revocation.
Job-scoped CI tokens need lifecycle-aware investigation
GitHub creates a GITHUB_TOKEN for workflow jobs. GitHub’s incident-response documentation states that the token is scoped to the job and cannot be reused after the job finishes, subject to its effective maximum lifetime.
That makes the right questions:
- What repository did the token belong to?
- What were its effective permissions?
- Was the job still active when attacker-controlled code executed?
- Did audit evidence show API or repository actions during the token’s valid lifetime?
JOB TOKEN EXPIRED ≠ OTHER CREDENTIALS SAFE. Personal access tokens, app tokens, API keys and other long-lived credentials can remain valid after the workflow ends.
Long-lived CI/CD secrets are a different exposure class
Review every long-lived credential actually available to jobs or the runner host during the compromise window.
Examples include:
- personal access tokens
- GitHub App or integration credentials
- third-party API keys
- SSH keys
- package-registry credentials
- container-registry credentials
- database credentials
- deployment tokens
- service-account credentials
- code-signing keys
If attacker-controlled code could read a long-lived secret, treat it as exposed even if audit logs do not yet prove misuse.
ACCESSIBLE ≠ USED. A credential can require revocation because it was exposed even when there is no evidence it was subsequently abused.
Environment secrets may reduce blast radius — if the job never reached the environment
CI/CD environments can create an important boundary.
For example, GitHub environment secrets are scoped to jobs referencing that environment and can also be protected by approval rules.
Therefore a compromised test job does not automatically prove production environment secrets were materialized.
But persistent runner compromise changes that conclusion. If the same compromised runner later receives an approved production job, malware already present on the host may capture those later credentials.
Environment never reached
The production secret may never have been delivered to the suspect job.
Persistent runner later used for production
The later production credentials may become part of the exposure window.
Cloud access: static credentials and OIDC require different responses
CI/CD jobs may access AWS, Azure, Google Cloud or another platform through either stored credentials or federated identity.
Static cloud key
If a long-lived cloud credential was accessible to the compromised environment, revoke or rotate it and investigate its cloud audit history.
OIDC / workload federation
OIDC reduces the need to store long-lived cloud secrets. A workflow exchanges its identity for a short-lived cloud token.
That does not mean a compromised runner has zero cloud exposure. If attacker-controlled code executes while the temporary cloud token is valid, it may be able to use the role assigned to the job.
Check:
- which cloud role could be assumed
- whether a token was issued during the exposure window
- the role’s effective permissions
- cloud audit events under that role
- repository, branch, environment or workflow conditions on the trust policy
- whether federation should be temporarily restricted
OIDC REDUCES LONG-LIVED SECRET RISK ≠ ZERO CLOUD RISK DURING THE JOB.
If a static AWS access key becomes one of the exposed credentials, continue with AWS Access Key Exposed: How to Check Whether It Was Used.
Do not limit the inventory to secrets stored in the CI platform
A self-hosted runner can contain credentials that were never configured as repository or organization secrets.
Check the host for:
- SSH private keys
- cloud CLI credential files
.npmrcor package-manager credentials- Maven or Gradle settings
- Docker credential stores
- kubeconfig files
- service-account files
- machine certificates
- signing material
- mounted secret volumes
- local deployment configuration
- credential-helper stores
GitLab’s runner security guidance explicitly warns that sensitive files placed on the runner host can be stolen by malicious jobs.
CI SECRET INVENTORY ≠ COMPLETE RUNNER CREDENTIAL INVENTORY.
What could the runner reach without stealing another secret?
Network placement can make a self-hosted runner materially more powerful than its CI secret configuration suggests.
Investigate access to:
- corporate LAN services
- cloud VPC resources
- Kubernetes APIs
- internal databases
- private package repositories
- artifact stores
- deployment endpoints
- cloud metadata services
- Docker daemon or local privileged sockets
- internal APIs relying on network trust
GitLab recommends runner network segmentation and restrictions around cloud metadata access because malicious CI code can otherwise reach infrastructure beyond the immediate job.
Ask a separate question: What could this machine access simply because of where it was running?
Persistent and shared runners widen the investigation
Ephemeral and persistent runners should not be treated as equivalent.
| Runner model | Primary exposure boundary | Main concern |
|---|---|---|
| Ephemeral runner | Usually one job or short-lived execution environment | Credentials and outputs from that execution |
| Persistent self-hosted runner | Multiple jobs across time | Persistence can expose later jobs and host data |
| Shared runner | Multiple repositories or projects | One compromised host may cross repository boundaries |
| Privileged runner | May have host-level capability | Container or job compromise can become host compromise |
GitHub runner groups can be shared with selected repositories. That tells you which repositories could have used the runner, but not which repositories were actually exposed.
RUNNER GROUP ACCESS ≠ PROVEN REPOSITORY EXPOSURE. Use the allowed-repository list to define what to investigate, then identify the jobs that actually executed during the window.
Caches can become a future-execution path
A compromised runner can create cache risk even if no long-lived secret was stolen.
Investigate:
- cache keys written during the compromise window
- dependency caches
- compiler and build caches
- tool caches
- which later trusted jobs restored those caches
GitHub’s guidance is to treat cache content as untrusted and not store secrets in caches.
A poisoned cache can potentially influence later trusted workflows.
CACHE POISONED ≠ SECRET STOLEN. It is an integrity and persistence problem that can survive beyond the original malicious job.
Artifacts produced on the runner may no longer be trustworthy
A runner often builds the software that later becomes a release or deployment.
For every artifact produced during the exposure window, determine:
- what source commit it used
- which workflow and job built it
- whether attacker-controlled code could modify build inputs or outputs
- where the artifact was uploaded
- whether it was released or published
- whether another system deployed or consumed it
Confidentiality exposure
Attacker could read private artifacts, logs or generated output.
Integrity exposure
Attacker could modify the artifact before publication or deployment.
RUNNER CLEANED ≠ ARTIFACT TRUST RESTORED. Suspect builds may need to be quarantined and rebuilt from a trusted environment.
What can artifact attestations prove?
Artifact provenance can help identify which repository, workflow, environment, commit and triggering event produced a build.
That is useful for scoping.
It does not mean an artifact produced by a compromised runner is automatically safe. Provenance can tell you that a suspect execution produced the artifact, which may be a reason to quarantine and rebuild it.
Package registries and signing keys can turn the incident into a supply-chain compromise
Review any runner access to:
- npm
- PyPI
- Maven repositories
- NuGet feeds
- container registries
- GitHub Packages
- GitLab Package Registry
- private artifact repositories
Determine whether the credential was read-only or could publish, overwrite or promote packages.
Signing credentials require especially careful treatment. If a runner could access a code-signing, package-signing or container-signing key, an attacker may be able to produce malicious output that appears legitimate.
A CI/CD runner incident can become a software supply-chain incident even when no production server was directly accessed.
Secret masking in logs does not prove the secret was protected
CI platforms can redact known secrets from displayed logs.
That protects the log output. It does not prevent code executing inside the job from reading a secret that has already been supplied to the process.
SECRET MASKED IN LOG ≠ SECRET NOT ACCESSIBLE TO THE JOB.
Logs remain useful for identifying commands, dependencies, uploads, suspicious URLs and execution timing, but they are only one evidence source. GitHub also notes that workflow logs capture standard output, not every file-system modification, network call or background process.
Separate exposure from actual misuse
The investigation should preserve four different states.
Do not downgrade an exposed long-lived credential merely because no misuse has yet been observed. But also do not report every potentially reachable resource as confirmed compromised without evidence.
Use this runner blast-radius matrix
| Exposure class | Primary questions |
|---|---|
| Repository | What code was checked out and what write permissions existed? |
| Job token | What was the token scope, permission and valid lifetime? |
| Long-lived secrets | Which PATs, API keys, SSH keys and service credentials materialized? |
| Environment secrets | Which protected environments did affected jobs actually reach? |
| Cloud | Which static key or OIDC role was available and what actions occurred? |
| Runner host | Which local credential files, machine identities or mounted secrets existed? |
| Network | Which internal services could the runner reach because of its placement? |
| Cache | Which caches were read or written and were they later restored? |
| Artifacts | Which builds were created, published, released or deployed? |
| Registry | Could the runner read private packages or publish new versions? |
| Signing | Could the runner use code, package or container signing material? |
| Other repositories | Which repositories actually used the same persistent runner during the exposure window? |
Contain the runner before trying to restore trust
For an active incident, the first objective is to stop additional jobs and access paths from crossing the untrusted environment.
Depending on your platform and incident scope:
- cancel suspicious or in-progress workflow runs
- stop new jobs from reaching the runner
- isolate the runner from the network
- remove or disable the affected self-hosted runner
- preserve runner disk or VM evidence where practical
- preserve workflow logs and CI metadata
- preserve repository and organization audit logs
- preserve cloud and registry audit events
GitHub’s current incident-response guidance specifically includes cancelling malicious workflow runs, disabling Actions where necessary and removing suspect self-hosted runners as possible containment measures.
Then rotate or revoke credentials according to actual exposure
Prioritize credentials that attacker-controlled code could access.
Possible remediation includes:
- revoke exposed personal access tokens
- replace API keys
- rotate SSH and deployment credentials
- rotate package-publishing credentials
- replace signing keys when exposure warrants it
- review cloud role sessions and static credentials
- tighten OIDC trust conditions
- remove and recreate runner authentication where runner identity itself may be compromised
GitHub’s August 18, 2026 credential-management update also added finer-grained token-type and user-specific revocation capabilities for incident response in supported environments.
Rebuild persistent runners from a trusted image
Do not assume that deleting one malicious file returns a long-lived runner to a trustworthy state.
Persistence can exist in:
- operating-system files
- services
- shell startup files
- runner configuration
- build tooling
- container configuration
- root certificates
- credential stores
- scheduled jobs
A safer recovery pattern is to replace or rebuild the runner from a trusted image, then re-register it with fresh runner credentials where applicable.
Restore artifact and repository trust separately
Runner recovery is only one track.
1
CREDENTIAL TRACK
Revoke or rotate materialized long-lived secrets and investigate their use.
2
ARTIFACT TRACK
Quarantine suspect builds and rebuild from a trusted environment.
3
CODE / CLOUD TRACK
Review repository modifications, deployments, cloud activity and downstream systems.
RUNNER REBUILT ≠ CREDENTIALS SAFE ≠ ARTIFACTS TRUSTED.
Recommended eight-state evidence ladder
Evidence boundary
A runner compromise proves that an execution environment became untrusted. It does not by itself prove every secret, repository, artifact, cloud account or downstream service was compromised. The blast radius should be reconstructed from the runner exposure window, job history, credential availability, effective permissions, host state, network reachability and audit evidence.
CI/CD runner compromise: frequently asked questions
If the runner was compromised, should I rotate every secret in the organization?
Not automatically. First identify which jobs and persistent host resources crossed the compromise window. Long-lived credentials actually reachable by attacker-controlled code should be treated as exposed. Unrelated secrets that never reached the runner should not be reported as compromised without evidence.
Do I need to rotate GITHUB_TOKEN?
GitHub generates the token for workflow jobs and it expires with the job or its effective maximum lifetime. Investigate what permissions it had and whether it was abused while valid. Long-lived credentials referenced by the workflow require separate revocation decisions.
Are production secrets safe if only a test job was compromised?
Potentially, if the production environment was never entered and the runner was ephemeral. On a persistent compromised runner, later production jobs may still expose their credentials to existing attacker persistence.
Does OIDC prevent cloud compromise?
It reduces long-lived cloud-secret exposure, but a compromised job may still use a short-lived cloud token while it is valid. Review the assumed role, trust conditions and cloud audit logs.
If I rebuild the runner, can I trust artifacts already produced?
No. Artifacts created during the exposure window require their own integrity review and may need to be quarantined and rebuilt from a trusted environment.
Can a compromised runner affect another repository?
Yes, especially when a persistent or shared self-hosted runner services multiple repositories. Identify which repositories were allowed to use it, then determine which actually executed jobs during the exposure window.
If no secrets appeared in the workflow logs, are they safe?
No. Secret masking protects displayed log output. It does not prove that code running inside the job could not read the secret before masking occurred.
Bottom line
A compromised CI/CD runner is not simply a leaked-secret incident. It is an execution-environment incident that can cross credentials, source code, repositories, cloud identities, internal networks, caches, artifacts, package registries and deployment systems.
The investigation should therefore begin with the runner’s exposure window and every job that crossed it.
Best evidence chain: runner compromise → affected jobs → credentials and host access → repository/cloud/network permissions → audit evidence → artifact and code integrity → downstream systems.
Last verified: August 22, 2026. Current GitHub and GitLab runner-security, incident-response, credential, OIDC, cache and artifact guidance was reviewed.

