Mini Shai-Hulud: Cross-Ecosystem Supply Chain Attack Hits npm, PyPI, and Packagist

Date Observed: April 29–30, 2026
Ecosystem: npm, PyPI, Packagist (PHP)
Targets: SAP enterprise developers, AI/ML engineers, DevOps and DevSecOps teams using Intercom SDKs
Attack Type: Supply chain compromise via stolen and abused CI/CD publishing credentials; preinstall hook injection; cross-ecosystem worm self-propagation
Impact: Eight confirmed compromised package versions across three ecosystems; multi-cloud credential theft (GitHub, AWS, GCP, Azure); IDE persistence via AI agent and VSCode hooks; autonomous worm propagation via stolen npm publish tokens

Key Takeaways

  • The Mini Shai-Hulud campaign (Shai-Hulud / TeamPCP Wave 3) compromised mbt, @cap-js/sqlite, @cap-js/postgres, @cap-js/db-service, intercom-client, lightning, and intercom/intercom-php across npm, PyPI, and Packagist; all within 48 hours.
  • Each compromised package injected a preinstall (or import-time) hook that downloads Bun v1.3.13 and executes an 11+ MB obfuscated JavaScript credential stealer targeting GitHub, npm, AWS, GCP, and Azure.
  • intercom-client@7.0.4 was hijacked 29 hours after the SAP packages via credentials stolen from an Intercom developer whose local machine had installed the compromised lightning package through pyannote-audio.
  • A primary exfiltration channel routes through api.github.com, allowing the attackers to blend malicious traffic with trusted GitHub API activity and evade simple domain-based blocking.
  • The payload persists by injecting itself into VSCode tasks and Claude Code SessionStart hooks across every accessible repository.

On April 29, 2026, StepSecurity identified malicious versions of mbt@1.2.48 and @cap-js/sqlite@2.2.2, two SAP enterprise npm packages, injected with preinstall hooks that download the Bun JavaScript runtime and execute a heavily obfuscated 11.6 MB credential stealer. Two additional SAP packages: @cap-js/postgres@2.2.2 and @cap-js/db-service@2.10.1 were confirmed compromised within hours of the first disclosure.

This was not a contained single-package incident. On April 30, the lightning PyPI package versions 2.6.2 and 2.6.3 were compromised, and it was through the lightning payload that an Intercom developer’s local machine was infected. Those stolen credentials were then used to publish intercom-client@7.0.4 via a hijacked GitHub Actions OIDC pipeline, 29 hours after the initial SAP package compromise. By end of April 30, the chain extended further to intercom/intercom-php@5.0.2 on Packagist, completing a confirmed cross-ecosystem compromise across npm, PyPI, and Packagist.

Scope and Impact of the Attack

Eight package versions were confirmed compromised across three ecosystems:Eight package versions were confirmed compromised across three ecosystems:

  • npm: mbt@1.2.48, @cap-js/sqlite@2.2.2, @cap-js/postgres@2.2.2, @cap-js/db-service@2.10.1, intercom-client@7.0.4npm: mbt@1.2.48, @cap-js/sqlite@2.2.2, @cap-js/postgres@2.2.2, @cap-js/db-service@2.10.1, intercom-client@7.0.4
  • PyPI: lightning@2.6.2, lightning@2.6.3
  • Packagist: intercom/intercom-php@5.0.2

intercom-client@7.0.4 had 361,510 weekly downloads at time of compromise. The lightning PyPI package serves hundreds of thousands of AI and ML engineers daily; deep learning training pipelines routinely carry GPU cluster credentials, Hugging Face API keys, and Weights & Biases tokens. intercom/intercom-php records approximately 12,700 installs per day with 20.7 million lifetime installs. The credential stealer targets the full cloud footprint of any affected environment:

  • GitHub tokens (PATs, OAuth tokens, Actions/OIDC credentials)
  • npm publish tokens
  • AWS IAM credentials via EC2 IMDS
  • GCP service account tokens
  • Azure client secrets and connection strings
  • SSH private keys
  • Kubernetes service account tokens
  • Secrets from:
    • AWS Secrets Manager
    • GCP Secret Manager
    • Azure Key Vault
  • Sensitive file paths and local data, including:
    • .env files
    • Shell history
    • Docker credentials
    • AI tool configs (.claude.json, ~/.kiro/settings/mcp.json)

Socket confirmed the cross-ecosystem propagation chain: pyannote-audio pulled in the compromised lightning package as a transitive dependency. An Intercom developer installed it locally, infecting their machine. That machine held the credentials used to compromise intercom-client@7.0.4 on npm and then intercom/intercom-php@5.0.2 on Packagist.

Two access vectors were used against the SAP packages. mbt@1.2.48 was published using a stolen static npm automation token from the cloudmtabot service account. The @cap-js packages were compromised via OIDC trusted publishing abuse: an attacker with access to a developer’s GitHub account pushed commits to a non-main branch, modified the release workflow to print the npm OIDC token double-base64-encoded to the workflow log, published the malicious versions, then force-reverted the branch. The npm trusted publisher was scoped to the full repository rather than a specific protected branch; a misconfiguration that enabled this path without any persistent credential.

How the Attack Works

Stage 1: Preinstall Hook → Bun Loader

Every compromised npm package injects “preinstall”: “node setup.mjs” into package.json. The setup.mjs loader is byte-for-byte identical across all Shai-Hulud family packages. It detects the host platform (including Alpine/musl for CI containers), downloads Bun v1.3.13 from GitHub Releases, executes the obfuscated payload (execution.js or router_runtime.js), then deletes the Bun binary. Bun is used specifically to evade EDR rules monitoring for suspicious node child processes during npm install. The Bun download URL (github.com/oven-sh/bun/releases) fires during install; before any application code runs.

For lightning on PyPI, the payload fires on package import via a daemon thread that silently downloads Bun and executes router_runtime.js from a hidden _runtime/ directory bundled in the wheel. For intercom/intercom-php, a Composer plugin class subscribes to post-install-cmd and post-update-cmd events, then executes setup-intercom.sh, which downloads Bun v1.3.13 and runs the same payload.

Stage 2: Multi-Cloud Credential Harvest

Five parallel collectors run simultaneously:

  • npm tokens from ~/.npmrc and environment
  • AWS credentials via EC2 IMDS and AWS_ACCESS_KEY_ID / AWS_SECRET_ACCESS_KEY environment variables
  • GCP credentials and Application Default Credentials JSON
  • Azure credentials from connection strings and client_secret patterns
  • GitHub Tokens Harvest

On GitHub Actions Linux runners, the payload spawns a Python process that reads the Runner.Worker process memory directly via /proc/{pid}/mem, extracting every secret from the runner’s address space, bypassing GitHub’s built-in secret masking. Stolen tokens with npm publish scope are immediately validated and queued for worm propagation.

Stage 3: Encrypted Exfiltration

Stolen data is encrypted with AES-256-GCM. The AES key is wrapped with an attacker-controlled RSA-4096 public key embedded in the payload. Encrypted payloads are committed to public repositories created on the victim’s own GitHub account, using Dune-themed names (e.g., fremen-melange-831) and the hardcoded description “A Mini Shai-Hulud has Appeared.” All traffic routes through api.github.com; universally allowlisted in corporate firewalls and CI/CD egress policies. Domain blocklists and IP-based network monitoring are ineffective against this channel.

Stage 4: Worm Propagation and IDE Persistence

Every stolen npm publish token is used to inject the malicious preinstall hook and payload files into new versions of accessible packages, published via direct npm registry API calls, bypassing the npm CLI entirely. Concurrently, the payload commits .vscode/tasks.json (with “runOn”: “folderOpen”) and .claude/settings.json (a Claude Code SessionStart hook) alongside a full copy of itself into all accessible GitHub repositories. Any developer who clones or opens an infected repository in VSCode or Claude Code silently re-executes the full credential stealer.

Why This Matters to DevOps and DevSecOps Teams

This campaign introduces three escalations that change the threat model for build pipelines.

First, multi-cloud scope: prior Shai-Hulud waves targeted GitHub and npm tokens. This wave explicitly queries AWS IMDS, GCP metadata server, and Azure credential patterns; any cloud-authenticated CI runner is now a target for full cloud credential theft, not just repository access.

Second, cross-ecosystem worm propagation: npm, PyPI, and Packagist were all compromised within a single 48-hour window using a single credential chain. –ignore-scripts in npm blocks the npm vector, but the PyPI vector activates on import and the PHP vector fires on Composer install/update; the same payload reaches all three ecosystems through different execution mechanisms.

Third, AI coding agent persistence: the abuse of Claude Code’s SessionStart hook is a new TTP documented in this wave. Infected repositories re-execute the payload for every developer who opens them, independent of package installation. Standard supply chain security controls focused on registry scanning do not detect this vector.

How InvisiRisk Protects Against This Attack

Unauthorized API Action Enforcement

BAF enforces policy on outbound API calls during CI/CD builds based on action, not just destination; critical when attackers abuse trusted platforms like GitHub.

In this case, the malicious workflow issues POST /user/repos to create dead-drop repositories and PUT /repos/{owner}/{repo}/contents to write encrypted data. These actions fall outside normal build behavior, so BAF blocks them before execution, preventing both data persistence and covert communication.

The same control also stops propagation attempts, such as unauthorized package publishing via API calls to npm, as seen in incidents like the Bitwarden CLI compromise.

Stability Buffer

Newly published packages are flagged and blocked before they can be consumed in a build (default: 48-hour window). mbt@1.2.48, intercom-client@7.0.4, and lightning@2.6.2 were all identified as malicious within hours of publication. A 48-hour stability window would have blocked these newly published versions from entering protected build pipelines during the highest-risk window before the community identified them as malicious.

Why Build-Time Defenses Matter

Mini Shai-Hulud shows how quickly a supply chain attack can move across ecosystems when attackers steal publishing credentials, abuse trusted APIs, and execute inside build and developer environments. The combination of multi-cloud credential theft and cross-ecosystem propagation makes this more than a package-security story; it is a Build and CI/CD security problem. Teams that rely only on registry checks or post-install scanning will struggle to catch attacks at the point where they actually execute.

Please fill out the form and we will get back to you.