Skip to content

Mini Shai-Hulud is Back: Developer Secrets Are Being Stolen Right Now

Japneet SinghJapneet Singh· Head of Research7 min read

Key takeaways

  • Since 21 October, infected computers have created 990 dead-drop repositories, with the newest appearing at 07:45 UTC on 3 November.
  • The worm almost always uploads using a token stolen from another developer, passed between infected computers, so nobody manages it by hand.
  • This wave has had almost no poisoned packages behind it, unlike the May and August waves.
  • A secret-stealing GitHub Actions workflow gets added and deleted within about 10 seconds, too fast for an owner to notice.
  • A May version of the worm is still spreading: found in 139 forks, 137 of which still carry it, and one fired again on 1 November.

Most reporting on the Shai-Hulud worms covers the moment of attack: a poisoned package, a hijacked build tool, a rush to revoke stolen tokens. This post is about what is happening now.

As we write, computers infected with the worm are still sending their owners’ secrets to GitHub. Since 21 October they have created 990 dead-drop repositories: public repositories holding the encrypted stolen data. The pace was about 86 a day until 30 October and has been about 47 a day since. The newest appeared at 07:45 UTC on 3 November. Based on the worm’s code, each upload holds what it collected from one developer’s computer or build server: GitHub and npm tokens, cloud keys and other saved secrets. We estimate that 100 to 200 infected computers are behind these uploads, sending again and again.

During the upload, instead of using the infected computer’s own GitHub token, the worm almost always uses a token stolen from another developer, passed between infected computers. When that token stops working, another infected computer notices this and shares its own, and the uploads carry on. Nobody manages this by hand.

Bar chart showing new stolen-secret repositories created each day from October 21 to November 3, dropping from about 86 a day to about 47 a day, with uploads still ongoing.

What’s new in this wave#

How the worm works is already well documented, as are the return of the hijacked actions-cool GitHub Actions from 16 to 25 September and the poisoned tensorlake package on npm on 8 October. We don’t repeat any of that here.

Two things set this wave apart: almost no poisoned packages are behind it, and the uploads have run through shared tokens for weeks.

1. Almost no poisoned packages. The big earlier waves, in May and August, put many poisoned package versions on npm within hours (more than 160 on 11 May alone). This wave has had almost none of its own: the only one reported is tensorlake, on 8 October, and we found nothing linking it to the computers here. That could change quickly, because this version still carries code to republish a victim’s packages when it finds an npm token that can publish without a two-factor check. How and when these computers were infected is still open. Some may be old infections that were never cleaned up; others may come from the hijacked actions-cool actions in September, which returned the day before this chain of shared tokens started.

2. Uploads have run through shared tokens for weeks. The August and early-October waves were described as uploading under each victim’s own account. The public GitHub event archive suggests August already passed uploads from one account to the next within minutes, as shared upload accounts did in May and June. What is new is how long it has lasted: since 21 October, almost every upload has gone through one shared token at a time, and only two tokens have carried them.

How big it is, and how steady#

  • Uploads. Beyond the 990 still online, at least 1,500 more were created between 17 September and 20 October and have since been deleted: a total of more than 2,500 in all.
  • Computers. Grouping the uploads by file size suggests 100 to 200 infected computers. Since 21 October, about 60 of these groups have come back on several different days, which suggests the same computers keep sending.
  • New victims. After the first three days, about 2 new repeat senders appear each day. So far that is a slow trickle of new infections, not a burst. File sizes are only a rough guide, so treat these numbers as estimates.
Bar chart showing upload sources by the day they first appeared, most are repeat senders that keep coming back rather than new infections.

How the theft keeps going: tokens passed from computer to computer#

  • One token at a time carries everyone’s uploads. Switch-over to the next shared account happens automatically. On 28 October, a new token took over within 20 minutes of the old one going quiet. When the shared token stops working, the next infected computer that finds no working token, and whose owner’s account shows no organizations, shares its own. The worm’s code works exactly this way.
Flowchart showing an infected computer’s decision logic for uploading stolen data, paired with a timeline showing one shared token handing off to the next with a 19-minute gap and no human involved.

A secret-stealing workflow that vanishes in seconds#

When it runs, the worm also tries to steal the secrets stored in the victim’s repositories. It adds a GitHub Actions workflow that would copy them out, on a fake Dependabot branch that is deleted within about 10 seconds, too fast for an owner to notice. Unlike the uploads, which go through a shared token, this step uses only the infected computer’s own token, and only if that token has the workflow scope. So a push means the worm ran on that developer’s own computer, and the owners of the shared tokens are victims too.

See it in action.

In September and October, at least 12 repositories had this workflow pushed into them. GitHub’s repository activity view shows these pushes even after the branch is gone. The same workflow hit at least 30 repositories in the August wave too.

The stakes go beyond one developer. The September and October targets include projects whose workflows use signing keys and publishing passwords. With those, an attacker could ship a malicious update that users would install as genuine.

Timeline of secret-stealing pushes from 15 September to 1 November, each marking a moment the worm ran on an infected machine.

Separately: forks infected in May still spread the worm#

This is not part of the October wave, but it is still infecting developers. On 18 May, a hijacked GitHub Action in the sync workflow that forks of a popular open-source AI chat app inherit committed the May version of the worm into those forks, together with Claude Code and VS Code settings that run it when the repository is opened. We found it in 139 forks; 137 still carry it, 108 of them on their main branch. One fork owner’s computer ran it again on 1 November. If you own a fork of a repository whose workflows use actions-cool/issues-helper, run the fork check under “What to do now” before opening it.

Three-panel diagram showing why infected forks go unnoticed: GitHub search misses them, the commit looks like a normal verified release, and the fork’s sync silently breaks instead of raising an alert.

What to do now#

Check whether your accounts hold dead-drop repositories. Run both lines for each developer account you are responsible for:

gh api -X GET search/repositories -f q='"Shai-Hulud: Here We Go Again" in:description user:LOGIN' --jq .total_count
gh api -X GET search/repositories -f q='"niagA oG eW ereH :duluH-iahS" in:description user:LOGIN' --jq .total_count

Replace LOGIN with the account’s GitHub username. The second line checks the reversed description that earlier versions use.

Any result above zero means the worm ran with that account’s token, either for its owner’s own uploads or for other people’s. Zero does not mean clean: most infected computers upload through someone else’s token, so their owners’ accounts hold nothing.

Check whether the secret-stealing branch touched your repositories, even though it is gone:

gh api "repos/OWNER/REPO/activity?ref=refs/heads/dependabot/github_actions/format/setup-formatter" \
  --jq '.[] | [.timestamp, .activity_type, .actor.login, .after] | @tsv'

Replace OWNER/REPO with the repository’s owner and name, here and in the commands below. Empty output means GitHub has no record of that branch being pushed.

Do not approve Run Copilot workflows. List workflow runs waiting for approval and delete any you didn’t set up:

gh api "repos/OWNER/REPO/actions/runs?status=action_required" \
  --jq '.workflow_runs[] | [.id, .name, .path, .head_branch, .created_at] | @tsv'

Check forks you own for the planted files, on every branch:

for b in $(gh api "repos/OWNER/REPO/branches" --paginate --jq '.[].name'); do
  gh api "repos/OWNER/REPO/commits?sha=$b&path=.claude/setup.mjs&per_page=1" \
    --jq ".[] | \"$b \(.sha) \(.commit.author.name) \(.commit.author.date)\""
done

If a token or computer is involved, disconnect the computer before revoking any token, because the worm wipes the user’s files when its stolen token is revoked. Then revoke the tokens and rotate every password and key that computer could reach. Deleting the dead-drop repositories alone does not stop the theft. If a branch carries the planted files, delete it before anyone opens it.

If you find dead-drops on someone else’s account, don’t report the account for suspension or revoke its token. Tell GitHub Security instead; suspending or revoking before the owner disconnects wipes their computer.

Restrict classic tokens. The worm uses only classic and OAuth GitHub tokens, never fine-grained ones, so restricting classic tokens in your organisation narrows what a stolen token can reach.

Treat AI-agent and editor settings as code. A command set to run when a Claude Code session starts, or when a folder opens in VS Code, runs with the developer’s full access. Check these files in any branch you didn’t write before opening the folder.

WhatWhat it looks likeNotes
Dead-drop repositoryPublic repository described as Shai-Hulud: Here We Go Again, holding results/results-<number>.json files; earlier versions use the reversed text niagA oG eW ereH :duluH-iahS990 created since 21 October and still online
Secret-stealing branchdependabot/github_actions/format/setup-formatter created, pushed to and deleted within about 10 seconds by a user’s tokenThe vanishing pattern is the sign; the branch name is public
Workflow run waiting for approvalBy github-actions[bot], signed by GitHub, reusing the previous commit’s message, adding .claude/index.js, .claude/setup.mjs, .vscode/setup.mjs, .claude/settings.json, .vscode/tasks.json18 May 19:00 to 19 May 00:30 UTC in the forks we checked
The worm file (May version).claude/index.js, 500,143 bytes, git blob 2931c1be43b0d04174636ddbff54963aec92bdebAlso found in two repositories infected by automatic workflows in September
Sign in the workflow run logA step using actions-cool/issues-helper taking over 10 seconds, plus an extra step that earlier runs did not haveRuns between 18 and 19 May, or 16 and 25 September

How we did this, and what it does not cover#

What we saw directly: public GitHub information (repository search, file lists, commit details, workflow step timings, public events and repository activity), the public GH Archive event dataset, and public package-registry records. The upload counts, the secret-stealing commits, and the fork check all come from these.

What we read in the worm source code: how infected computers choose a token, and when they add the secret-stealing workflow, comes from reading the worm’s code. We never ran the worm and never opened or decrypted the stolen data.

Limits:

  • We can’t see the infected computers, so what started the worm on them is inferred from timing.
  • 100 to 200 computers is an estimate from file sizes, and counts from the GitHub event archive are minimums.
  • We can’t see private repositories, deleted workflow runs or owners’ logs.
  • For most developers whose tokens were shared, we couldn’t find how their computers were first infected.

Disclosure#

We reported these findings to GitHub on 2 November 2026 and privately notified the owners of the most exposed accounts before publication.

Japneet Singh

Japneet Singh

Head of Research

Japneet leads the Research team at Metano. Two decades in cybersecurity, one decade in AI x cybersecurity. Hands on practitioner, leading by example. Interested in applying AI to defensive and offensive security.