A 48-hour supply chain cascade ending Thursday saw the same TeamPCP-linked credential stealer ride into npm and PyPI through three high-trust shipping channels: SAP’s Cloud Application Programming (CAP) packages, Intercom’s official npm SDK, and PyTorch Lightning. Researchers at Wiz, Socket, Aikido, Snyk, and Sophos have converged on the same operator — branding the campaign “Mini Shai-Hulud” after the message it leaves on victim GitHub accounts — and the same payload: a Bun-runtime stealer that bypasses GitHub Actions log masking by scraping secrets directly out of the runner’s process memory.
What happened
The campaign opened at 09:55 UTC on April 29 with the publication of malicious versions of four SAP packages on npm: mbt 1.2.48, @cap-js/db-service 2.10.1, @cap-js/sqlite 2.2.2, and @cap-js/postgres 2.2.2. The bad releases were live for roughly two hours before SAP’s response team yanked them. SAP later confirmed an attacker pushed malicious commits to the upstream repos that hijacked the release workflow and triggered unauthorized npm publishes — the workflow held publish permissions with no manual approval gate.
On April 30, the same payload reappeared in two more registries. Intercom’s intercom-client shipped malicious 7.0.4 and 7.0.5 to npm. Hours later, lightning 2.6.2 and 2.6.3 — the PyTorch Lightning AI training library, with 8.3M monthly downloads and 2.1M weekly — went out on PyPI carrying the same code. Socket flagged the Lightning versions 18 minutes after publication; PyPI quarantined them, but a Socket-opened GitHub issue warning maintainers was closed within one minute by a pl-ghost account posting a “SILENCE DEVELOPER” meme — strong signal that the project’s GitHub account itself is compromised, not just its publishing token.
How the stealer works
Each compromised package adds a preinstall hook in package.json (or its PyPI equivalent — Lightning hides a _runtime/ directory that auto-runs on import lightning). The hook executes setup.mjs, which downloads the Bun JavaScript runtime from GitHub and runs an obfuscated execution.js (about 11MB in the Lightning variant, distributed as router_runtime.js).
The payload sweeps the host for the standard credential trove: SSH keys, shell history, .npmrc and .gitconfig tokens, AWS, Azure, GCP, and Kubernetes config, plus cryptocurrency wallets. Stolen data is AES-256-GCM encrypted and exfiltrated by creating a new public repo on the victim’s own GitHub account with the description “A Mini Shai-Hulud has Appeared.”
The CI-runner branch is the dangerous bit. On GitHub Actions runners, the payload drops an embedded Python script that opens /proc/<pid>/maps and /proc/<pid>/mem for the Runner.Worker process and greps for the in-memory secret structure ("key":{"value":"...","isSecret":true}). That pulls every secret the workflow has access to — including ones never logged or echoed — and bypasses the log-masking GitHub Actions applies on stdout. It is the same anti-masking trick TeamPCP shipped in the March Trivy/Checkmarx/LiteLLM compromises, and the shared RSA public key plus encoding routines are why Wiz, Sophos, and Snyk all attribute this to the same operator.
Self-propagation is wired in: when the payload finds a developer with npm publish rights to other packages, it reuses the harvested token to push the same loader downstream. That is how a single SAP repo compromise turned into a multi-registry incident in 48 hours.
Who is exposed
If you ran npm install against any project pulling those four SAP packages between 09:55 and ~12:14 UTC on April 29, or against intercom-client 7.0.4/7.0.5, or pip install-ed lightning 2.6.2/2.6.3 — assume every credential reachable from that machine or CI job is leaked. CAP is enterprise-default for SAP shops; intercom-client is in many SaaS support stacks; Lightning sits inside a large slice of AI/ML training pipelines. Combined exposure is in the eight figures of monthly downloads.
CI runners are the worst case. The /proc/.../mem scrape captures secrets the workflow has explicit access to but which never touch a log line, so you cannot detect this by combing build output.
What to do now
Pin and verify: npm ls @cap-js/db-service @cap-js/sqlite @cap-js/postgres mbt intercom-client and pip show lightning. Anything matching the bad versions needs to be rolled forward to a clean release — verify against the registry before pinning.
Rotate everything the affected machine or runner could see: GitHub PATs and Actions secrets, npm tokens, AWS/Azure/GCP credentials, kubeconfigs, SSH keys. For org-level GitHub accounts, audit recently created public repos for the “Mini Shai-Hulud” description string — that is the canary.
If you publish packages, gate npm publish behind a manual approval step or a separately scoped publish workflow rather than a token sitting in your normal CI environment. The SAP root cause was an attacker hijacking a release workflow that already had publish rights — there was no second factor between repo write and registry publish.
Sources and advisories
- Socket: SAP CAP analysis, Intercom-client, Lightning
- Wiz: Mini Shai-Hulud writeup
- Aikido: SAP technical analysis
- Snyk: Bun-based stealer breakdown
- Sophos: Mini Shai-Hulud campaign overview
- The Hacker News: SAP coverage, Lightning + Intercom
- BleepingComputer: SAP npm compromise