
BLOG
Mini Shai-Hulud Supply-Chain Compromise of @redhat-cloud-services npm Packages via GitHub Actions OIDC Abuse
Alessandra
Rizzo
Summary
On 2026-06-01, in a new wave of Mini Shai-Hulud, an attacker abused the @redhat-cloud-services GitHub Actions OIDC trusted publisher to ship malicious versions of packages across that npm scope. Each malicious version adds a preinstall hook that runs a multi-megabyte obfuscated index.js on every npm install, harvesting cloud, CI, and registry credentials and then self-propagating through the tokens it steals. The activity matches the Mini Shai-Hulud worm toolkit and is identified by the strings "Miasma: The Spreading Blight" and "niagA oG eW ereH :duluH-iahS", which the attacker uses as the description of the public GitHub repositories it exfiltrates to.
The important point for response is that no npm token was stolen and no maintainer changed. The packages were published by Red Hat's own automated release pipeline using its legitimate trusted identity, so two-factor authentication and routine token rotation alone do not contain this.
Attack Chain
Initial access: Minting for OIDC tokens
The root cause sits in the source repositories, not the packages. In RedHatInsights/frontend-components, commit 8bf0512 ("chore: update dependencies") is an orphan commit with zero parents, pushed as a history-less branch. It adds a GitHub Actions workflow and a roughly 4 MB _index.js. The committer name shown belongs to a real contributor, and the branch from which this commit ran points to a commit directly in the root repository rather than a fork.


The developer’s push access may have been harvested through a GitHub token, an SSH key or GitHub credentials. This was enough to publish the malicious GitHub Actions ci.yaml.

The workflow is therefore the engine: it triggers on a push to any branch, requests id-token: write to mint a GitHub OIDC token, then runs bun run _index.js with an OIDC_PACKAGES environment variable listing the scope's packages. Because the npm trusted-publisher binding accepted any workflow in the repository carrying id-token: write rather than a single pinned workflow on main, the malicious job was able to publish under Red Hat's trusted identity.
The same pattern reached javascript-clients and platform-frontend-ai-toolkit. The pinned actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd in that workflow is a confirmed worm fingerprint rather than a coincidence: SafeDep observed the same pinned SHA in the worm's own propagation commits.
Delivery and payload
Each malicious version sets "preinstall": "node index.js", so the code runs before any application code, including on CI runners. The index.js grows from a few kilobytes to roughly 4.3 MB and is a single statement that rebuilds a character-code array through String.fromCharCode, reverses a Caesar shift, and evals the result. The shift is randomized per build (we measured 12 and 5 on tsc-transform-imports, StepSecurity reported 21 on host-inventory-client, and SafeDep reported 9 on patch-client), so any signature keyed to one shift value will miss the others. The static harness we built auto-detects the shift for this reason.
The decoded layer is a small async loader that AES-128-GCM-decrypts two embedded blobs using hardcoded keys: a Bun-runtime downloader and the roughly 634 KB main implant. The loader fetches Bun v1.3.13 from the official GitHub releases mirror into a temporary directory, writes the implant to /tmp/p<random>.js, and runs it under Bun so it never touches the victim's Node installation. The implant itself is wrapped in an obfuscator.io string-array scheme layered over a bespoke PBKDF2-derived substitution cipher, which is why the campaign string and C2 details only assemble at runtime and resist static extraction.
What the payload does
Once running, the implant sweeps for credentials across AWS (including instance and ECS metadata, Secrets Manager, and SSM), Azure managed identity, GCP service accounts, HashiCorp Vault, Kubernetes service-account tokens, npm, GitHub, roughly forty CI providers, and password managers including Bitwarden and gopass. It reads ~/.npmrc, ~/.netrc, and shell and database history, and runs secret-scanning regexes over what it collects.
On GitHub Actions runners it reads /proc/<pid>/mem from the Runner.Worker process and uses ACTIONS_RUNTIME_TOKEN to target variables flagged isSecret, recovering masked secrets that never appear in workflow logs. Among the variables it reads is ANTHROPIC_API_KEY.
Exfiltration goes to attacker-created public GitHub repositories whose description is "Miasma: The Spreading Blight", routed through api.github.com with a spoofed python-requests/2.31.0 user agent so it blends with ordinary automation. The committed data is hybrid-encrypted to the attacker, not readable plaintext, as detailed under GitHub Exfiltration below.
The worm propagates two ways. It republishes tampered npm tarballs after exchanging OIDC tokens for publish rights and signs them through Sigstore so they carry valid provenance, and it commits a .github/workflows/codeql.yml file on a chore/add-codeql-static-analysis branch via the GraphQL createCommitOnBranch mutation so the change appears as a signed, verified CodeQL improvement that maintainers are likely to wave through.
Beyond theft, it attempts a Docker-socket container escape to give the runner passwordless sudo, probes for CrowdStrike, SentinelOne, Carbon Black, and StepSecurity Harden-Runner before acting, and stays quiet if anti-analysis variables such as __FAKE_PLATFORM__, __IS_DAEMON, or SKIP_DOMAIN are present.
For persistence that survives npm uninstall, it injects a SessionStart hook into Claude Code's ~/.claude/settings.json and a folderOpen task into VS Code's .vscode/tasks.json.
GitHub Exfiltration
At time of writing we have identified the GitHub compromised account letsgo0 as the potential dead-drop hub account for the exfiltrated data. Just today, it committed to over 400 repositories linked to the Mini Shai-Hulud campaign.

The repositories are named by a generator that combines an adjective, a mythological creature, and a number, for example relentless-thunderbolt-87930 and stygian-onslaught-86745, interspersed with Dune terms such as kralizec, ghola, mentat, fedaykin, and heighliner. Repository descriptions carry campaign banners, including "Miasma: The Spreading Blight" and "Shai-Hulud: Here We Go Again” stored reversed, as in niagA oG eW ereH :duluH-iahS, an evasion that defeats a plaintext GitHub description search, so any count based on searching for the forward string undercounts the hub.
Each repository holds exfil records at paths like /results/results-<numbers>-0.json, structured as an { envelope, key } pair. This is hybrid encryption: the stolen blob is encrypted with a random symmetric key (envelope), and that key is encrypted to a hardcoded attacker public key (key). So although the repositories are public, the data inside is not readable plaintext, and only the attacker's private key can open it. Cracking the envelope is not feasible without that key and is not a useful avenue. Scoping which organizations were affected therefore has to come from install telemetry and from CI and GitHub audit logs rather than from reading the dumps. Because the attacker can decrypt, the exposure is real, so takedown of the hub and rotation by affected parties remain the priorities.
Indicators of compromise
Note that exfiltrated data is committed via GitHub API to a new repository with the description "Miasma: The Spreading Blight". AES keys are regenerated per build, so each package version's keys are distinct indicators.
Type | Indicator |
|---|---|
Commit |
|
Publish workflow |
|
Worm propagation | branch |
Exfil hub account |
|
Exfil repo banners | descriptions |
Exfil repo naming | generated |
Exfil record format |
|
Exfil transport | commits via |
Package marker |
|
|
|
|
|
Decrypted payload SHA256 |
|
AES key, | key |
AES keys, |
|
AES keys, |
|
Runtime artifacts | Bun download from |
Host changes |
|
Anti-analysis env vars |
|
Affected packages
Confirmed 32 packages published through the same trusted publisher, with patch-client@4.0.4 as its detailed delivery sample; StepSecurity reported an overlapping set. We also confirm the malicious payload in tsc-transform-imports@1.2.4
Treat the published lists as a floor, not a ceiling, and verify any @redhat-cloud-services/* dependency by content and by publish timestamp around 2026-06-01, since malicious tarballs can be yanked before they are catalogued.
Package | Version |
|---|---|
chrome | 2.3.1 |
compliance-client | 4.0.3 |
config-manager-client | 5.0.4 |
entitlements-client | 4.0.11 |
eslint-config-redhat-cloud-services | 3.2.1 |
frontend-components | 7.7.2 |
frontend-components-advisor-components | 3.8.2 |
frontend-components-config | 6.11.3 |
frontend-components-config-utilities | 4.11.2 |
frontend-components-notifications | 6.9.2 |
frontend-components-remediations | 4.9.2 |
frontend-components-testing | 1.2.1 |
frontend-components-translations | 4.4.1 |
frontend-components-utilities | 7.4.1 |
hcc-feo-mcp | 0.3.1 |
hcc-kessel-mcp | 0.3.1 |
hcc-pf-mcp | 0.6.1 |
host-inventory-client | 5.0.3 |
insights-client | 4.0.4 |
integrations-client | 6.0.4 |
javascript-clients-shared | 2.0.8 |
notifications-client | 6.1.4 |
patch-client | 4.0.4 |
quickstarts-client | 4.0.11 |
rbac-client | 9.0.3 |
remediations-client | 4.0.4 |
rule-components | 4.7.2 |
sources-client | 3.0.10 |
topological-inventory-client | 3.0.10 |
tsc-transform-imports | 1.2.2 and 1.2.4 |
types | 3.6.1 |
vulnerabilities-client | 2.1.8 |
Recommendations
If you installed any affected version, treat every credential reachable from that host or CI runner as compromised, including masked CI secrets, and assume ANTHROPIC_API_KEY and any reachable password-manager material were taken. Sequence the response carefully, because revoking credentials too early can backfire. The payload installs a destructive token monitor, a dead-man switch that watches a stolen GitHub token and, if it detects that the token has been invalidated, can run destructive commands such as wiping the user's home directory. Do not rotate the GitHub token first. Isolate the affected machine or CI runner, remove persistence, and preserve forensic evidence (disk image, npm cache, shell history, process and CI telemetry, GitHub audit logs) before rotating anything. Only once persistence is confirmed removed, and working from a clean system, rotate cloud keys, npm tokens, GitHub PATs, GitHub Actions secrets, and Vault and Kubernetes tokens. Pin affected dependencies back to the last known-good versions and regenerate lockfiles from trusted metadata. Removing persistence means more than npm uninstall. Inspect ~/.claude/settings.json and .vscode/tasks.json for hooks or tasks you did not add, and look for a token monitor installed as a user service or launch agent (for example gh-token-monitor and kitty-monitor units, with token, handler, and state files under ~/.config/), since all of these survive package removal. Audit your GitHub org for unexpected public repositories, for commits adding .github/workflows/codeql.yml on a chore/add-codeql-static-analysis branch, and for the pinned checkout SHA above. To prevent recurrence, pin each npm OIDC trusted publisher to one workflow file on main and require a manual-approval environment, scope workflow permissions tightly so jobs cannot freely mint OIDC tokens, and disable install scripts for CI installs where feasible.
Attribution
The TTPs and the "Miasma: The Spreading Blight" exfil-repo description match the Mini Shai-Hulud worm toolkit that SafeDep and others have tracked across recent npm incidents, including the TanStack and SAP @cap-js compromises.
References
SafeDep, "Mini Shai-Hulud 'Miasma: The Spreading Blight' Hits @redhat-cloud-services," 2026-06-01: https://safedep.io/redhat-cloud-services-hit-by-mini-shai-hulud-npm-worm/
StepSecurity, "Multiple redhat-cloud-services npm Packages compromised," 2026-06-01: https://www.stepsecurity.io/blog/multiple-redhat-cloud-services-npm-packages-compromised
JFrog Security Research, "Shai-Hulud - Miasma: The Spreading Blight Hits Red Hat npm Packages," 2026-06-01: https://research.jfrog.com/post/shai-hulud-miasma-redhat-cloud-services/
Malicious commit: https://github.com/RedHatInsights/frontend-components/commit/8bf051251ec3b973e39a313547e53421a2f8d2f6
Version diff (1.2.4 vs 1.2.2): https://npmdiff.dev/%40redhat-cloud-services%2Ftsc-transform-imports/1.2.4/1.2.2/package/index.js
Disclosure issues: RedHatInsights/javascript-clients#492 and #493, frontend-components#2329, platform-frontend-ai-toolkit#57
Background on the Shai-Hulud lineage: Unit 42, Datadog Security Labs, and Microsoft Security have all published analyses of prior waves.
Read More
Read Panther's latest threat research reports here.
Share:
RESOURCES






