On May 18, 2026, 53 tags of the GitHub Action actions-cool/issues-helper
were rewritten in less than 3 minutes and 16 seconds to point to an
imposter commit that exfiltrates CI/CD secrets from the memory of the
Runner.Worker process. A second action from the same organization,
actions-cool/maintain-one-comment, received the same treatment in the same
window (15 tags in 39 seconds). Only workflows pinned to a full legitimate
commit SHA are spared. This article describes the mechanism, the IOCs
published by StepSecurity, and the immediate action to take if you use either
of these actions.
What will you learn here?
This post gives you everything needed to assess and contain the incident:
- Understand what an imposter commit is in a tag poisoning attack
- Identify whether your workflows use one of the compromised actions
- Detect the indicators of compromise on the runner side
- Protect yourself through SHA pinning and block the payload's network egress
- Harden your posture after the incident
Who is exposed to this attack?
Any repository that consumes actions-cool/issues-helper or
actions-cool/maintain-one-comment through a tag is exposed, typically
@v3, @v3.7.6, @v2. That is the default pattern when you copy an example
from the GitHub Actions Marketplace: easy to write, fragile in reality. The
defense rule, pin every action to the full commit SHA, stopped being an
expert-only recommendation after the tj-actions/changed-files precedent of
March 2025. Yet it is still massively ignored, and every new compromise reminds
us why.
What exactly happened?
The attacker gained the ability to move the tags in the
actions-cool/issues-helper GitHub repository. He generated one imposter
commit per tag, each with a fake commit message in the style of
"Build action for vX.Y.Z" that mimics the maintainer's convention, and
re-pointed all 53 tags to these new commits, which are attached to no
branch of the repository (orphan commits).
The tell is in the timing: the 53 imposter commits were all created between
19:10:24 and 19:13:40 UTC on 2026-05-18. The same operation was carried out
17 minutes later on maintain-one-comment, with 15 commits created in 39
seconds. A human does not type 53 handcrafted commits in 3 minutes: this is
automation.
On the payload side, when a workflow executes the compromised action on a runner:
- Download of the
bunJavaScript runtime into/home/runner/.bun/bin/bun. - Read of
/proc/<Runner.Worker PID>/memfrom a child Python process. ThisRunner.Workerprocess is the one holding the decrypted secrets of the workflow. - Filtering of the memory looking for
"isSecret":true, then extraction of the authentication tokens (gh auth token, pipeline secrets). - HTTPS exfiltration to
t.m-kosche.comon port 443.
Sources: the full analysis and the IOCs are published by StepSecurity. The maintainers were notified through issue #11 of the repository.
Are you affected?
One grep at the root of each repository answers the question:
grep -rE "actions-cool/(issues-helper|maintain-one-comment)" \ .github/workflows/ 2>/dev/nullThree possible outcomes:
- No result: you do not use these actions. Keep reading for the right preventive reflexes.
- Reference by tag (
@v3,@v3.7.6,@v2, etc.): your workflow is compromised on the next run. Immediate action below. - Reference by SHA: check that the SHA is not one of the imposter commits. Every SHA in the list published by StepSecurity must be banned. If your SHA is not in it, it is probably legitimate; confirm by checking that it belongs to the default branch of the repository.
What should you do immediately?
Rotate first, clean up second: that is the order that matters.
Once the rotation is done, two options for what comes next:
-
Remove the action entirely if it is not critical. That is the safest option as long as the maintainers have not regained control of the repository and published an official statement.
-
Pin to the last legitimate SHA: the one from the
masterbranch on the eve of the incident. Concretely:# BEFORE - vulnerable- uses: actions-cool/issues-helper@v3# AFTER - SHA of a legitimate commit from the master branch- uses: actions-cool/issues-helper@<SHA-of-master-pre-May-18-2026> # v3To retrieve a legitimate SHA, open the history of the repository's
masterbranch on GitHub at a date prior to May 18. Do not take a SHA from a tag: all of them were hijacked.
If you use the StepSecurity Harden-Runner tool with egress block mode, the
exfiltration to t.m-kosche.com is already blocked. That does not prevent the
memory read but it blocks the leak of the secrets: defense in depth.
How do you audit your whole fleet in one command?
Checking by hand that every uses: of every workflow is pinned by SHA does not
scale beyond a handful of repositories. That is exactly what
Plumber does, a CI/CD
compliance scanner that covers GitHub Actions and GitLab CI. Its ISSUE-104
control flags any action referenced by tag or by unpinned SHA, which is
precisely the attack surface of tag poisoning:
# At the root of a GitHub repositoryplumber analyze --scoreTypical output on a vulnerable workflow:
CRIT [ISSUE-104] Third-party actions must be pinned by commit SHA .github/workflows/issues.yml:12 actions-cool/issues-helper@v3, mutable reference (tag)Bonus: Plumber also maintains a database of action SHAs carrying CVEs
(control ISSUE-703). As soon as the imposter SHAs published by StepSecurity
land in that database, a workflow that had mistakenly pinned to one of them
would be alerted as well. Run plumber analyze in a CI step to block any
pull request that regresses on these controls; that is exactly what would
have prevented the incident for disciplined consumers.
Which indicators of compromise should you look for?
On a compromised runner, you will find:
- Presence of the
bunbinary in/home/runner/.bun/bin/bunwhen your workflow does not expect it. - Reads of
/proc/<PID>/memfrom a childpython3process, withtr/greppipelines filtering on"isSecret":true. - Outbound HTTPS connection to the domain
t.m-kosche.comon port 443.
This domain is now on the global Harden-Runner blocklist. If you manage your own network allow-list for runners, you can add it to your egress firewall rules.
Why does this attack repeat the same pattern?
Because the playbook works: this is the third time in two months that we
observe the same modus operandi. An attacker takes control of a GitHub Actions
repository, rewrites all the tags so they point to malicious code that steals
secrets through the memory of Runner.Worker. Before actions-cool/, there
was Trivy (twice) and KICS from Checkmarx. This is
no longer an isolated incident, it is an industrialized playbook.
The target is always the same: popular actions that run with production secrets and that teams consume by tag, not by SHA. The defense is also always the same, and it is not negotiable:
GitHub itself acknowledges the problem: the
GitHub Actions 2026 security roadmap
announces a system of dependency lock files (the equivalent of
go.mod/go.sum for workflows) that will freeze all direct and transitive
SHAs. Public preview announced in 3 to 6 months; until then, manual discipline
remains the only defense.
What should you remember?
- Tag poisoning: an attacker who controls a repository can rewrite all the tags to point to malicious code. Users pinned by tag pick up the payload on their next run.
- Runner.Worker memory: it is the process holding the decrypted secrets.
Reading
/proc/<PID>/memis enough to extract them, no need to bypass GitHub Secrets. - Pinning to a legitimate SHA is the only reliable and free defense. Everything else (rotation, monitoring, allow-list) is a complement.
- The pattern repeats: Trivy in March, KICS in April, actions-cool in May.
Every month, a new popular action. Consider that the third-party actions in
your
.github/workflows/must be pinned before the next wave, not after. - The workflow lock file is coming natively from GitHub during 2026: prepare your workflows in pinned-SHA mode now, you will save time at migration.