The open-source ecosystem has once again become the stage for a major supply chain attack, this time leveraging the popular npm package @ctrl/tinycolor and spreading virally across the registry. What began on September 15, 2025, quickly escalated into one of the most significant npm compromises to date, impacting over 180 packages across the ecosystem.
This was not an isolated compromise, but a coordinated, viral campaign—a wake-up call for how fast modern supply chain threats can evolve and propagate.
The Anatomy of the Attack
- Initial Access: Maintainer credentials exposed in earlier incidents were reused to push trojanized releases of @ctrl/tinycolor and other popular packages.
- Payload Drop: Each rogue release added a post-install script that executes a 3.7 MB, Webpack-minified bundle.js during npm install. Most build pipelines treat this as benign.
- Secrets Exfiltration: The script dumps environment variables, scans local files with TruffleHog, and plants a GitHub Actions workflow that exfiltrates every new secret on every future CI run.
- Worm-Like Propagation: If an npm token is found, the malware automatically bumps and republishes every package owned by the compromised maintainer—turning one breach into an ecosystem-wide outbreak.
- Persistence & Exposure: Stolen GitHub tokens are used to create public repos named Shai-Hulud or to fork private repos with a “-migration” suffix, exposing sensitive code and amplifying business impact.
Key Indicators
- bundle.js SHA-256 hash family 46faab8a…f09
- .github/workflows/shai-hulud-workflow.yml
- Outbound traffic to webhook[.]site/*bb8ca5f6*
The sophistication of this campaign demonstrates that supply chain attackers are no longer simply inserting backdoors—they are engineering autonomous, viral threats that grow and persist without direct attacker intervention.
Why This Matters
- Trust is exploitable: Popular libraries are high-value targets. A single compromise can cascade into thousands of downstream applications.
- Secrets are the real prize: By harvesting developer and CI/CD credentials, attackers don’t just compromise code—they gain a foothold inside infrastructure, pipelines, and production systems.
- The blast radius is amplified: Self-replicating malware means a single infected account can create an ecosystem-wide outbreak in hours.
How Security Teams Should Respond
Responding to an attack of this magnitude requires both immediate incident response and long-term actions:
- Suspend unverified installs: Temporarily pause npm install in CI pipelines, or enforce trusted, mirrored registries until remediations are confirmed.
- Hunt for indicators: Search repositories, lockfiles (package-lock.json, yarn.lock), and caches for infected versions. Look specifically for IoCs such as malicious bundle.js hashes or suspicious Shai-Hulud workflows.
- Remediate environments: Assume developer machines and CI runners that executed npm install during the window are compromised. Rebuild them clean and rotate all credentials (npm tokens, GitHub PATs, SSH keys, cloud provider access keys).
Long-Term Guardrails
- Enforce least-privilege and short-lived tokens: Limit package publishing permissions and avoid static, long-lived secrets.
- Require provenance and signed builds: Adopt cryptographic attestations and signatures to validate package authenticity.
- Detect suspicious behaviors early: Flag anomalies like new maintainers, post-install hooks, or oversized binaries before they make it into production pipelines.
- Connect the supply chain end-to-end: Ensure visibility not just into open-source dependencies, but also into developer environments, CI/CD pipelines, and runtime exposure.
How Apiiro Helps
At Apiiro, we help organizations stay ahead of these evolving threats by embedding proactive supply chain security into every stage of the SDLC:
- End-to-end visibility: Continuously inventories every direct and transitive dependency across code.
- Detect Malicious Code Early: With Apiiro’s Malicious Code Ruleset to catch suspicious payloads during CI/CD, and our PRevent tool to block malicious commits via GitHub app, stopping threats from entering repos.
- Context-aware prioritization: Focuses teams on dependencies that are actually executed in prod or reachable in code, eliminating remediation noise.
- Owner Mapping: Automatically routes each risky to the right code owner, streamlining fixes and accountability.
Closing the Loop
This attack is a stark reminder that supply chain security goes far beyond CVEs. It’s not only about patching known vulnerabilities—it’s about securing the entire lifecycle of software development.
Without holistic visibility and proactive defenses, every dependency update becomes is a potential Trojan horse.
List of Compromised Packages
The following table contains a list of 189 known-infected packages:
- @ahmedhfarag/ngx-perfect-scrollbar
- @ahmedhfarag/ngx-virtual-scroller
- @art-ws/common
- @art-ws/config-eslint
- @art-ws/config-ts
- @art-ws/db-context
- @art-ws/di
- @art-ws/di-node
- @art-ws/eslint
- @art-ws/fastify-http-server
- @art-ws/http-server
- @art-ws/openapi
- @art-ws/package-base
- @art-ws/prettier
- @art-ws/slf
- @art-ws/ssl-info
- @art-ws/web-app
- @crowdstrike/commitlint
- @crowdstrike/falcon-shoelace
- @crowdstrike/foundry-js
- @crowdstrike/glide-core
- @crowdstrike/logscale-dashboard
- @crowdstrike/logscale-file-editor
- @crowdstrike/logscale-parser-edit
- @crowdstrike/logscale-search
- @crowdstrike/tailwind-toucan-base
- @ctrl/deluge: 7.2.2
- @ctrl/golang-template: 1.4.3
- @ctrl/magnet-link: 4.0.4
- @ctrl/ngx-codemirror: 7.0.2
- @ctrl/ngx-csv: 6.0.2
- @ctrl/ngx-emoji-mart: 9.2.2
- @ctrl/ngx-rightclick: 4.0.2
- @ctrl/qbittorrent: 9.7.2
- @ctrl/react-adsense: 2.0.2
- @ctrl/shared-torrent: 6.3.2
- @ctrl/tinycolor: 4.1.1, @4.1.2
- @ctrl/torrent-file: 4.1.2
- @ctrl/transmission: 7.3.1
- @ctrl/ts-base32: 4.0.2
- @hestjs/core
- @hestjs/cqrs
- @hestjs/demo
- @hestjs/eslint-config
- @hestjs/logger
- @hestjs/scalar
- @hestjs/validation
- @nativescript-community/arraybuffers
- @nativescript-community/gesturehandler: 2.0.35
- @nativescript-community/perms
- @nativescript-community/sentry 4.6.43
- @nativescript-community/sqlite
- @nativescript-community/text: 1.6.13
- @nativescript-community/typeorm
- @nativescript-community/ui-collectionview: 6.0.6
- @nativescript-community/ui-document-picker
- @nativescript-community/ui-drawer: 0.1.30
- @nativescript-community/ui-image: 4.5.6
- @nativescript-community/ui-label
- @nativescript-community/ui-material-bottom-navigation
- @nativescript-community/ui-material-bottomsheet: 7.2.72
- @nativescript-community/ui-material-core: 7.2.76
- @nativescript-community/ui-material-core-tabs: 7.2.76
- @nativescript-community/ui-material-ripple
- @nativescript-community/ui-material-tabs
- @nativescript-community/ui-pager
- @nativescript-community/ui-pulltorefresh
- @nexe/config-manager
- @nexe/eslint-config
- @nexe/logger
- @nstudio/angular
- @nstudio/focus
- @nstudio/nativescript-checkbox
- @nstudio/nativescript-loading-indicator
- @nstudio/ui-collectionview
- @nstudio/web
- @nstudio/web-angular
- @nstudio/xplat
- @nstudio/xplat-utils
- @operato/board
- @operato/data-grist
- @operato/graphql
- @operato/headroom
- @operato/help
- @operato/i18n
- @operato/input
- @operato/layout
- @operato/popup
- @operato/pull-to-refresh
- @operato/shell
- @operato/styles
- @operato/utils
- @teselagen/bounce-loader
- @teselagen/liquibase-tools
- @teselagen/range-utils
- @teselagen/react-list
- @teselagen/react-table
- @thangved/callback-window
- @things-factory/attachment-base
- @things-factory/auth-base
- @things-factory/email-base
- @things-factory/env
- @things-factory/integration-base
- @things-factory/integration-marketplace
- @things-factory/shell
- @tnf-dev/api
- @tnf-dev/core
- @tnf-dev/js
- @tnf-dev/mui
- @tnf-dev/react
- @ui-ux-gang/devextreme-angular-rpk
- @yoobic/design-system
- @yoobic/jpeg-camera-es6
- @yoobic/yobi
- airchief
- airpilot
- angulartics2: 14.1.2
- browser-webdriver-downloader
- capacitor-notificationhandler
- capacitor-plugin-healthapp
- capacitor-plugin-ihealth
- capacitor-plugin-vonage
- capacitorandroidpermissions
- config-cordova
- cordova-plugin-voxeet2
- cordova-voxeet
- create-hest-app
- db-evo
- devextreme-angular-rpk
- ember-browser-services
- ember-headless-form
- ember-headless-form-yup
- ember-headless-table
- ember-url-hash-polyfill
- ember-velcro
- encounter-playground: 0.0.5
- eslint-config-crowdstrike
- eslint-config-crowdstrike-node
- eslint-config-teselagen
- globalize-rpk
- graphql-sequelize-teselagen
- html-to-base64-image
- json-rules-engine-simplified: 0.2.4, 0.2.1
- jumpgate
- koa2-swagger-ui: 5.11.2, 5.11.1
- mcfly-semantic-release
- mcp-knowledge-base
- mcp-knowledge-graph
- mobioffice-cli
- monorepo-next
- mstate-angular
- mstate-cli
- mstate-dev-react
- mstate-react
- ng2-file-upload
- ngx-bootstrap
- ngx-color: 10.0.2
- ngx-toastr: 19.0.2
- ngx-trend: 8.0.1
- ngx-ws
- oradm-to-gql
- oradm-to-sqlz
- ove-auto-annotate
- pm2-gelf-json
- printjs-rpk
- react-complaint-image: 0.0.35
- react-jsonschema-form-conditionals: 0.3.21
- react-jsonschema-form-extras: 1.0.4
- remark-preset-lint-crowdstrike
- rxnt-authentication: 0.0.6
- rxnt-healthchecks-nestjs: 1.0.5
- rxnt-kue: 1.0.7
- swc-plugin-component-annotate: 1.9.2
- tbssnch
- teselagen-interval-tree
- tg-client-query-builder
- tg-redbird
- tg-seq-gen
- thangved-react-grid
- ts-gaussian: 3.0.6
- ts-imports
- tvi-cli
- ve-bamreader
- ve-editor
- verror-extra
- voip-callkit
- wdio-web-reporter
- yargs-help-output
- yoo-styles