groundy
developer tools

Malicious npm Packages Hit Red Hat's Published JavaScript Clients

Malicious versions of 32 Red Hat npm packages carried a credential-stealing worm, published through the vendor's OIDC pipeline. Vendor namespaces are not a trust boundary.

6 min · · · 4 sources ↓

Thirty-two packages inside Red Hat’s official @redhat-cloud-services npm scope shipped malicious versions on June 1, 2026, each embedding a credential-stealing worm in a preinstall hook. The packages were published through Red Hat’s own GitHub Actions OIDC trusted-publisher pipeline. A single compromised GitHub account was enough to inherit that workflow’s publishing authority and push weaponized builds to an enterprise vendor namespace that downstream teams had every reason to treat as trusted.

What happened

On June 1, 2026, malicious versions of 32 packages under the @redhat-cloud-services npm scope appeared on the npm registry. The affected packages include patch-client, frontend-components, rbac-client, compliance-client, host-inventory-client, insights-client, and vulnerabilities-client, plus 25 others. Red Hat issued advisory RHSB-2026-006 the same day, confirming the scope of the compromise.

These are frontend JavaScript libraries consumed by Red Hat’s Hybrid Cloud Console at console.redhat.com. They are not the managed OpenShift or Ansible Automation Platform cloud services; the blast radius is the npm build toolchain of any team pulling these clients directly into its own JavaScript build, not the hosted console itself.

The version-numbering pattern

Each compromised package received two or three malicious versions following a numbering gap pattern: X.0.N, X.0.N+1, X.0.N+3, with the X.0.N+2 version never published. For patch-client, the compromised versions were 4.0.4, 4.0.5, and 4.0.7, according to the GitHub issue tracking the scope-wide compromise. The gap at X.0.N+2 makes the sequence harder to spot in changelogs.

The last known clean version of patch-client was 4.0.3. Version 4.0.4, published at approximately 10:55 UTC on June 1, contained the payload.

The payload

The malicious version of patch-client added a preinstall hook that ran node index.js. The file ballooned from 7,926 bytes to 4,294,136 bytes, roughly a 540x increase. The technical analysis in issue #493 describes a multi-stage execution chain: ROT-9 encoding, then AES-128-GCM decryption, then a download of a Bun runtime, then execution of a credential-stealing worm matching the pattern researchers call Shai-Hulud.

The worm harvested SSH keys from ~/.ssh, registry credentials from ~/.npmrc and ~/.docker/config.json, AWS keys matching the AKIA and AWS_ prefixes, GitHub tokens (gh_token), npm publish credentials, gists, and roughly 129 environment variables, according to the LinuxSecurity feature on the Miasma campaign. It then self-propagated by publishing to any npm packages the infected victim’s credentials could reach.

OIDC trusted publishing was the attack vector

The malicious packages were not published with a stolen npm token. They were published through the same GitHub Actions OIDC trusted-publisher configuration that legitimate releases use. Red Hat’s advisory states that a compromised GitHub account was used to push unauthorized commits to RedHatInsights repositories. The workflow was already authorized to publish to the official namespace, so the attacker inherited that authority by controlling the account that could merge into the publishing branch.

This is a different threat model from token theft. OIDC trusted publishing was designed to eliminate long-lived secrets from CI pipelines. But the trust relationship itself became the credential. An attacker who controls a contributor account on a repo with OIDC publishing does not need to find, steal, or rotate a token. The pipeline is already authorized. The attack surface shifted from “protect the token” to “protect every account with push access,” which is a substantially larger perimeter.

Vendor namespaces are not a trust boundary

The practical damage here is not just the 32 packages. It is the assumption that pulling a dependency from a vendor’s official npm scope is safe by default. Many organizations maintain SBOM policies and CI pinning rules that exempt vendor namespaces from full review. The logic is sound in theory: Red Hat has a security team, a release process, and a reputation. Auditing every version of every @redhat-cloud-services package seems like overhead that a vendor-published scope should let you skip.

That assumption does not survive this incident. The packages came from the vendor’s own namespace, published through the vendor’s own CI pipeline, signed with the vendor’s own OIDC trust. Every structural indicator of legitimacy was present. And the payload was a credential-stealing worm.

What Red Hat says vs. what downstream consumers should check

As of June 4, 2026, Red Hat’s advisory states that no customer action is required based on current findings. Red Hat has removed the compromised versions from the npm registry. The investigation is ongoing and the advisory may be updated.

The IOC hashes for patch-client@4.0.4 are tarball SHA256 031BA872D5A84BFB18115F432811E4B45180346A1BAE653F7FD85F918E7BB3A3 and index.js SHA256 DF1732F5BFEC12E066BE44DEE02EC8A243E4868D38672C1B1D065359DD735A14, per the technical breakdown in issue #493.

“Customer action is not required” is scoped to Red Hat’s own hosted services. Teams that vendored or pinned these npm packages in their own builds are outside that scope and need to verify independently.

What to check today

Lockfiles. Search your lockfiles for any @redhat-cloud-services package version published on or after June 1, 2026. The version-numbering gap pattern means you need to check every version, not just the latest.

Scope whitelists. If your CI configuration or SBOM policy contains a blanket exemption for @redhat-cloud-services or any other vendor namespace, remove it. Vendor scopes can be compromised through the same account-takeover and CI-pipeline attacks as any other namespace.

Runner credentials. If any CI runner installed one of the compromised versions, rotate every credential that runner had access to: npm tokens, GitHub tokens, SSH keys, and AWS keys. The worm’s self-propagation step means the blast radius extends to every package your credentials could publish.

OIDC trust boundaries. If you maintain GitHub Actions workflows with OIDC trusted publishing, audit which accounts have push access to the publishing branch. That access list is now your entire publishing attack surface. Protect it accordingly.

Frequently Asked Questions

Does this affect Red Hat managed services like ARO, ROSA, or OpenShift Dedicated?

No. The compromised packages are frontend JavaScript clients consumed by the Hybrid Cloud Console (console.redhat.com) only. Red Hat’s managed cloud services, including ARO, OSD, ROSA, ACS Cloud Service, and AAP Managed, do not pull these npm packages into their build pipelines.

How does this differ from dependency-confusion or typosquatting attacks?

Dependency-confusion exploits registry priority to inject a package that never existed at the internal name. Typosquatting relies on mistyped names. Here, the attacker published malicious versions of the real packages, in the real namespace, through the real CI pipeline. Namespace allowlists and registry pinning, the standard defenses against those other vectors, would not have caught it.

Would npm —ignore-scripts have prevented the worm from executing?

Yes. The entire payload relied on a preinstall hook running node index.js. Passing —ignore-scripts or setting ignore-scripts=true in .npmrc would have blocked execution. The tradeoff is that any legitimate dependency requiring install-time scripts for native module compilation (node-gyp, esbuild, sharp) would also break, so this works as a targeted override rather than a blanket policy.

Were Kubernetes credentials in the worm’s harvest scope?

Yes. The worm read Kubernetes cluster configs from ~/.kube/config alongside SSH keys, Docker credentials, and cloud keys. Teams running CI inside Kubernetes clusters should check whether an infected runner held a kubeconfig with cluster-admin or write-scoped permissions. Those credentials could have been exfiltrated and reused against the cluster independently of the npm publishing chain.

What OIDC publishing controls would have prevented this?

The attack exploited the fact that any account with push access to the publishing branch could trigger an OIDC-authorized release. Requiring signed commits, mandatory review approvals on the publishing branch, and restricting OIDC token claims to specific workflow files and actor identities would narrow the surface from every contributor with push access to approved reviewers on release PRs. GitHub environment protection rules with required reviewers can enforce this today without custom tooling.

sources · 4 cited

  1. RHSB-2026-006 Supply chain compromise of @redhat-cloud-services npm packages vendor accessed 2026-06-04
  2. [SECURITY]: Malicious npm releases detected across @redhat-cloud-services/ scope community accessed 2026-06-04
  3. SECURITY: @redhat-cloud-services packages compromised — malicious preinstall payload (credential stealer / worm) community accessed 2026-06-04
  4. Red Hat npm Package Compromise Highlights a Growing Supply Chain Problem analysis accessed 2026-06-04