Thirty-one package versions across roughly thirty @redhat-cloud-services npm packages were published carrying a credential-stealing worm on June 1 — the latest wave of the Shai-Hulud lineage. StepSecurity, JFrog, Aikido, Wiz, OX Security, SafeDep, Socket, and Microsoft converged on the same payload within hours and named the campaign after the marker it leaves on victim GitHub repositories: “Miasma: The Spreading Blight.” The packages are legitimate Red Hat frontend components, API clients, and MCP tooling — together about 117,000 downloads a week — and the malicious code runs at install time, before anything imports them.

What happened

The bad versions were published through GitHub Actions OIDC from the RedHatInsights/javascript-clients repository, meaning the CI/CD pipeline itself was abused rather than a single leaked token. Evidence points to a compromised Red Hat employee GitHub account as patient zero: the account pushed malicious orphan commits to two RedHatInsights repositories, sidestepping code review, and the release workflow’s publish rights did the rest. The first commit carrying the “Miasma” string appeared on May 29, so the operator was likely testing for a couple of days before the June 1 push.

This is a Mini Shai-Hulud variant. The tooling traces to TeamPCP — the group behind the March Trivy and Checkmarx Action compromises — but TeamPCP open-sourced the worm, so anyone could be driving this one and attribution is genuinely unsettled.

How the worm works

Each package declares "preinstall": "node index.js". For a types-only package, that alone is a tell. The index.js is ~4.2 MB: a ROT-21 outer layer wraps two AES-128-GCM blobs — a small Bun bootstrapper and the main implant. The loader downloads Bun v1.3.13 from GitHub releases if it is missing, writes the implant to /tmp/p<random>.js, and runs it under Bun. The install-time telemetry to hunt for is an npm install that spawns node, then curl/unzip, then bun run against a temp file.

The implant sweeps everything: GitHub PATs and Actions tokens, npm tokens, AWS/GCP/Azure credentials, Vault and Kubernetes tokens, SSH and GPG keys, Docker config, CircleCI tokens, and password-manager material. On GitHub Actions runners it reads /proc/<pid>/mem for the Runner.Worker process and uses the runtime API’s isSecret flag to lift masked secrets straight out of memory — the same anti-log-masking trick from earlier TeamPCP waves. Wiz notes this variant adds GCP and Azure identity collectors, a shift from grabbing static secrets toward enumerating cloud access itself.

Exfiltration is disguised. The direct channel posts to api.anthropic[.]com/v1/api — a real Anthropic host that returns a 404, used purely as camouflage in network logs; it is not an Anthropic compromise. A GitHub dead-drop fallback commits encrypted results to attacker-created public repos described “Miasma: The Spreading Blight.” Then it self-propagates: given a usable npm token it injects its own preinstall hook, adds a Bun dependency, bumps the patch version, and republishes — using npm OIDC trusted publishing and a bypass_2fa parameter to beat 2FA-protected accounts.

Who is exposed

If any project pulled an affected @redhat-cloud-services version on June 1 — directly or transitively — treat every credential reachable from that machine or CI job as leaked. CI runners are the worst case: the /proc/.../mem scrape captures secrets that never touch a log line, so build output will not tell you anything happened.

Persistence survives removing the package. It installs a kitty-monitor service (systemd on Linux, a LaunchAgent on macOS), injects a SessionStart hook into Claude Code’s settings.json, and writes a folderOpen task into .vscode/tasks.json so the payload re-runs on normal developer activity.

What to do now

One order-of-operations rule matters most: isolate affected hosts and runners before you revoke any tokens. The worm plants a gh-token-monitor dead-man switch that polls GitHub for token validity and, if a stolen token is invalidated while the host is still live, runs destructive shell commands that delete the user’s home and Documents directories. Revoke credentials from a clean system only after the host is contained.

Then find affected installs (check lockfiles, package caches, CI logs, and container images), remove the bad versions, and regenerate lockfiles from trusted metadata. Run CI with npm ci --ignore-scripts where lifecycle scripts are not required. Remove persistence — the kitty-monitor and gh-token-monitor services plus the Claude Code and VS Code hooks — before rotating GitHub, npm, AWS/GCP/Azure, Vault, Kubernetes, SSH, and Docker credentials. Rebuild runners and workstations from clean images wherever compromise cannot be ruled out. Full IOCs, including file hashes and the complete affected-version list, are in the JFrog and StepSecurity writeups.

Sources and advisories