CI Secret Theft: Probe Pull Requests and Deleted Workflow Runs

How secret-stealer campaigns test stolen tokens with pull requests, erase workflow logs, and what to check and rotate when you find one.
CI secret theft campaigns open probe pull requests, let workflows expose secrets, then delete the runs. The audit signals that catch them, and how to respond.
November 9, 2026

CI secret theft rarely starts with an exploit. It starts with a credential that already works. An attacker gets a developer's token, opens a pull request to see what it can do, lets a workflow run with the repository's secrets in scope, and then deletes the run so its logs are gone.

Nothing in the code looks different afterwards. The pull request may be closed within minutes. The workflow file may never change. The evidence lives in one place: the audit trail of who did what, and when.

This post explains how CI secret-stealer campaigns work, which audit signals catch them, and what to do in the first hours after you find one. The examples use GitHub Actions, because that is where these campaigns have been most visible.

How CI secret theft works

A CI pipeline is a credential broker. It holds cloud deploy roles, registry tokens, package publishing keys and signing material, and it hands them to whatever code the workflow runs. Anyone who can make the workflow run code of their choosing can usually read what it holds.

That makes the pipeline a better target than the secrets store behind it. The attacker does not need to break the vault. They need the vault to hand its contents to a job they control.

Secret-stealer campaigns tend to follow the same three steps:

  1. Get a token. A personal access token, an OAuth grant or an SSH key belonging to someone with write access. These come from infected developer machines, malicious packages that read local credential files, or leaked secrets in other repositories.
  2. Probe. Use the token to open a pull request or push a branch, and see whether the account can trigger workflows and whether those workflows run with secrets.
  3. Collect and clean up. Let a workflow run code that prints, encodes or sends secrets out, then delete the workflow run so the logs that show it are gone.

Each step is small. None of them needs a vulnerability in the CI platform. That is why these campaigns are an operations problem as much as a code problem.

Probe pull requests: testing a stolen token

The first thing an attacker with a fresh token wants to know is what it can reach. A probe pull request answers that cheaply. If the pull request opens, the token has write access to the repository. If workflows start, the attacker knows CI is wired to that repository and can see which jobs run on pull requests.

Campaigns often reuse a fixed title or branch name across many repositories, because the probe is automated. That reuse is useful to defenders. A published campaign indicator, such as a known pull request title, lets you search for the probe directly instead of reasoning about intent.

What a probe can reach depends on how the workflow is triggered:

  • A branch inside the repository. When an account with write access pushes a branch and opens a pull request from it, pull_request workflows run with the repository's secrets. A stolen write token gets the same treatment as the real developer.
  • A fork. Workflows triggered by pull_request from a fork do not receive repository secrets by default. That is the protection most teams rely on.
  • pull_request_target. This trigger runs in the context of the base repository, with secrets, even for forks. A workflow that checks out and runs the pull request's code under this trigger gives secrets to anyone who can open a pull request.

The point about stolen tokens is the one teams miss. Fork protections assume the attacker is outside the organization. A stolen token puts them inside it.

Treat a probe pull request as an account compromise, not as spam. An account that opened one is likely compromised even when nothing else followed, because the probe is the test that comes before the theft.

Erasing the tracks: token-driven workflow run deletion

Workflow logs are the obvious place to see a secret being read or sent. GitHub masks secret values it knows about in logs, but an attacker can encode a value before printing it, or send it to an external host. Either way, the run's logs and step output are evidence.

So the cleanup step deletes the run. A user with write access can delete a workflow run, and with it the logs, through the web interface or through the REST API. A campaign script does this through the API, with the same token it used to open the probe.

The timing is the tell. Teams delete old runs for housekeeping, usually in bulk, usually long after the fact, and usually through a bot or a retention job. A person deleting a run through a token within an hour of the run starting is a different pattern. It is someone removing what just happened.

The deletion itself is recorded in the organization audit log, even though the run is gone. That record is often the only trace left of the theft.

Audit signals that catch CI secret theft

Code scanning will not see this. The workflow files can be unchanged and the repository clean. Detection has to come from activity: the audit log, push events and token usage.

These are the signals worth watching, roughly in the order a campaign produces them.

Signals from the probe

  • A pull request title or branch name matching a published campaign indicator. High confidence, low volume. Match against indicators as they are published.
  • Workflows running from a branch that has never been reviewed. Repeated runs from a non-default branch are a route to running CI code that has not been through review.
  • A workflow that has not run in the repository before. An attacker may add a new workflow file on their branch to get a job that does what they want.
  • A workflow file changed on the default branch without a pull request. A direct push to .github/workflows/ skips the review that is supposed to protect CI.

Signals from the token

  • One token used from more than one country. A token that is shared, proxied or stolen.
  • Classic personal access tokens in use. Classic tokens carry broad, long-lived scopes. Their use is worth surfacing even when it is routine.
  • A personal access token and a GitHub App acting from the same IP address. One actor driving both credentials.

Signals from the cleanup

  • A workflow run deleted through a token shortly after it ran. Filter out bots and retention jobs. What remains is short.
  • Pull requests opened and closed quickly by the same account with no review. The probe is done once the attacker has the answer.

If you want to look by hand, the organization audit log is searchable through the API. A query along these lines lists recent workflow run deletions:

gh api "/orgs/YOUR-ORG/audit-log?phrase=action:workflows.delete_workflow_run&per_page=100"

Compare each deletion's actor and time against the run it removed. A deletion by a person, within the hour, through a token, is the one to chase.

Close the gaps before the next campaign

Detection buys time. Reducing what a stolen token can reach buys more. A few changes cover most of the exposure:

  • Scope the default token. Set permissions: at the top of each workflow and grant only what the job needs.
  • Keep secrets in environments with reviewers. Deploy credentials in a protected environment are not available to a job on an unreviewed branch.
  • Avoid running pull request code under pull_request_target. If you need the trigger, do not check out or execute the head branch in that job.
  • Tie cloud federation to specific branches and environments. An OIDC trust that accepts any branch of a repository accepts the attacker's branch too.
  • Prefer fine-grained, short-lived tokens over classic personal access tokens, and review which accounts hold write access to repositories with sensitive secrets.

A minimal starting point for a pull request workflow:

  • on: pull_request rather than pull_request_target for anything that runs the pull request's code.
  • permissions: { contents: read } at the top of the file, widened per job only where needed.
  • uses: actions/checkout@<full-commit-sha> for every action, pinned to a commit rather than a tag.
  • No secrets.* references outside a job bound to a protected environment:.

No deploy secrets, no write scopes, actions pinned to a commit. Jobs that need more go behind an environment. For the pinning side of this, see GitHub Actions Security: Your CI Pipeline Is a Dependency Graph, and GitHub's own security hardening guide for GitHub Actions.

CI secret theft response checklist

When a probe pull request or a fast run deletion turns up, assume the secrets in scope of that repository's workflows were read. Then work through this list. Order matters: cut the attacker's access first, then rotate what they may have taken.

  1. Revoke the compromised account's access. For the account that opened the probe, revoke its personal access tokens, OAuth grants and SSH keys, and end its sessions. Do this before rotating anything, or the attacker can read the new values too.
  2. Rotate workflow secrets and variables. Every repository, environment and organization secret the affected workflows could read. Include variables that hold anything sensitive.
  3. Check cloud federation. Remove any federated credential the attacker added to an Azure identity, and review AWS role trust policies for subjects and conditions you did not set.
  4. Look for persistence. New deploy keys, new GitHub Apps or OAuth apps, new webhooks, new self-hosted runners, changed branch protections.
  5. Scope the reach. List every repository the compromised account could write to, not only the one where the probe appeared. Campaigns run the same probe widely.
  6. Check the package side. If a publishing token was in scope, compare recent releases of your packages against what you intended to ship.
  7. Find the original leak. Work out how the token was stolen: a developer machine, a malicious dependency or a credential committed somewhere. Otherwise the next token goes the same way.

For leaked credentials, the question before revoking is what breaks. See Leaked Credential Blast Radius: Know the Reach Before You Revoke, and for sorting live keys from noise, Secret Scanning Triage: Not Every Leaked Key Is an Incident.

How Heeler detects and responds to CI secret theft

Heeler's Behavioral Checks read the GitHub audit log for connected organizations and raise findings from patterns in your activity rather than your code. For the wfx-loot CI secret-stealer campaign, two checks raise high-severity detections on GitHub and GitHub Enterprise repositories with audit activity collected:

  • Pull request matches a known campaign indicator. A pull request was opened whose title matches a published indicator of the campaign. Attackers open these to test a stolen token's write access, so the account that opened it is likely compromised. The detection names the campaign.
  • Workflow run deleted through a token soon after it ran. Someone other than a bot deleted a workflow run through an access token within 60 minutes of the run starting. Deletions made in the GitHub web interface are not reported.

The same check family covers the other signals above: workflow bursts from a non-default branch, workflows outside a 30-day baseline, workflow files changed on the default branch outside a pull request, personal access tokens used across countries, and classic personal access token use. Each detection keeps links to the activity it was raised from, and audit activity is kept for 90 days.

The campaign also has its own entry under Threats on the Threat Intelligence dashboard, with the affected package versions and the response steps: rotate workflow secrets and variables, revoke the tokens, OAuth grants and SSH keys of the accounts that opened the probe pull request, and remove the attacker's Azure federated credentials and review AWS role trust.

When a stolen credential has also leaked into code, Revoke credential asks the provider to stop it working and then re-tests it to confirm. Heeler revokes with the leaked credential itself, so it needs no credential of yours at the provider. Revoke now covers Vercel, DigitalOcean, Doppler, Buildkite, crates.io, Twitch, GitHub App user tokens and Heroku, alongside providers such as AWS, GitHub, GitLab, Google Cloud and Slack. Heeler enables revoke for your organization, and an administrator turns it on. See CI/CD security and secrets detection and validation for more.

FAQ

What is CI secret theft?

CI secret theft is stealing the credentials a CI pipeline holds by getting a workflow to run attacker-chosen code. The attacker usually starts with a stolen developer token rather than a vulnerability, and the secrets leave through workflow output or an outbound request.

What is a probe pull request?

A probe pull request is one an attacker opens to test what a stolen token can do. If it opens and workflows start, the attacker knows the token has write access and which jobs run. Treat the account that opened it as compromised.

Why would an attacker delete a workflow run?

Deleting a workflow run removes its logs, which may show secrets being printed or sent out. The deletion is still recorded in the organization audit log, which is why a fast, token-driven run deletion is a strong signal.

Do fork protections stop CI secret theft?

Fork protections help only when the attacker is outside the organization. A stolen token with write access can push a branch inside the repository, and workflows on that branch run with the repository's secrets.

What should I rotate first after a CI secret theft?

Revoke the compromised account's tokens, OAuth grants and SSH keys first, then rotate workflow secrets and variables. Rotating before cutting the attacker's access lets them read the new values.

Heeler watches your GitHub audit activity for campaign probes and fast run deletions, links each detection to the threat entry and its response steps, and revokes leaked credentials at the provider with a verified result. To see it on your own repositories, Get a demo.

What’s new on Heeler
Thank you! Your submission has been received!
Oops! Something went wrong while submitting the form.

Related resources

See All Resources