Table of contents
NPM Ecosystem Under Siege: Self-Propagating Malware Compromises 187 Packages in a Huge Supply Chain Attack

Last updated: September 16, 2025 – 11:45 AM ET
The NPM ecosystem has been rocked by one of its widest supply chain attacks to date, with over 187 popular packages compromised by advanced malware capable of self-propagation and automated credential harvesting. This attack, affecting packages with millions of weekly downloads including angulartics2, ngx-toastr, and @ctrl/tinycolor, demonstrates how cybercriminals are evolving their tactics to create “worm-like” malware that can autonomously spread across the software supply chain. This blog provides a comprehensive analysis of the attack methodology, the malicious payload’s technical capabilities, and the IOCs associated with this unprecedented supply chain compromise.
Mend.io has done an internal audit of our development environment and products and can confirm we are not impacted by this zero day event.
NPM supply chain breach overview
The attack began with a coordinated compromise of multiple package maintainer accounts across different organizations. Attackers successfully infiltrated the accounts of the maintainers, allowing them to inject malicious code into their packages.
What makes this attack particularly dangerous is the malware’s ability to self-propagate through the npm ecosystem, automatically modifying package.json files and republishing trojanized versions using legitimate maintainer credentials.
Attack surface expansion through automation
Unlike traditional supply chain attacks that require manual intervention, this malware operates as a true “worm” with automated propagation capabilities. The malicious code scans for npm projects within compromised environments, automatically modifies their dependencies, and republishes them with embedded malware, creating an exponential growth pattern that can rapidly compromise entire software ecosystems.
Malware analysis
The payload is designed to operate across multiple environments including developer workstations, CI/CD pipelines, and production servers, maximizing its potential for credential discovery and exfiltration.
Initial execution and persistence
The malware executes through npm package lifecycle hooks, typically during package installation or update processes.
Multi-stage credential harvesting
The malware operates through several coordinated stages, each designed to maximize credential discovery while maintaining stealth across different environment types.
Stage 1: Environment reconnaissance and TruffleHog deployment
The malware begins by downloading and executing TruffleHog, a legitimate open-source credential scanning tool, to systematically search for exposed secrets across the compromised system.
Stage 2: Cloud metadata endpoint exploitation
The malware specifically targets cloud environment metadata endpoints to extract cloud service credentials and instance information.
Stage 3: GitHub repository manipulation
The malware creates “Shai-Hulud” repositories under compromised GitHub accounts to store and organize stolen credentials, making them easily accessible to attackers.
Advanced propagation mechanism
Perhaps the most concerning aspect of this malware is its self-propagating capabilities, which allow it to spread autonomously across npm packages without human intervention.
Automated package modification
The malware scans for package.json files within compromised environments and automatically modifies them to include malicious dependencies or lifecycle hooks.
GitHub Actions exploitation
The malware leverages GitHub Actions to automate credential exfiltration and maintain persistence across repository updates.
Impact and ecosystem implications
This npm supply chain attack represents a significant evolution in threat actor capabilities and poses unprecedented risks to the software development ecosystem:
Exponential propagation potential
The worm-like behavior enables exponential growth across the npm ecosystem. Each compromised package can potentially compromise multiple downstream packages, creating a cascade effect that can impact thousands of projects within hours.
Developer-focused targeting
By specifically targeting developer environments, the attack compromises individuals who often have elevated privileges and access to multiple systems, repositories, and cloud environments. maximizing the potential impact per infection.
Automated credential harvesting at scale
The integration of TruffleHog enables systematic credential discovery across entire development environments, potentially exposing:
- GitHub personal access tokens
- Cloud service credentials (AWS, GCP, Azure)
- Database connection strings
- API keys and authentication tokens
- SSH private keys and certificates
CI/CD pipeline contamination
The malware’s ability to modify package.json files and republish packages means it can contaminate CI/CD pipelines, potentially affecting production deployments and creating persistent backdoors in deployed applications.
Attribution and threat landscape
The attack demonstrates characteristics consistent with those of the S1ngularity/nx threat group, known for its previous sophisticated npm supply chain attacks. The use of the “Shai-Hulud” repository naming convention matches previous attack patterns, suggesting continued activity from this threat actor.
Attack sophistication indicators
- Advanced automation capabilities
- Multi-stage payload design
- Cloud-native environment awareness
- GitHub Actions integration
- Self-propagating worm behavior
Conclusion
The sophisticated self-propagating malware analyzed in this attack represents a significant escalation in supply chain threat capabilities. By combining automated propagation, comprehensive credential harvesting, and cloud-native environment exploitation, this malware demonstrates how threat actors are adapting to modern development practices and infrastructure.
This incident highlights the critical importance of supply chain security and the need for comprehensive security measures across the entire software development lifecycle. As package ecosystems continue to grow and development practices become increasingly cloud-native, we can expect to see more sophisticated attacks targeting these foundational elements of modern software development.
Mitigation recommendations
To protect against similar supply chain attacks and automated credential harvesting, developers and organizations should implement:
Supply chain security measures
- Use package lock files to prevent unexpected version updates
- Implement private npm registries for critical applications
- Regularly audit and rotate maintainer access credentials
- Enable branch protection rules requiring pull request reviews
- Implement mandatory security scanning for all commits
- Monitor for unexpected repository creation or visibility changes
As supply chain attacks become more automated and sophisticated, proactive security measures and comprehensive monitoring become essential. The npm ecosystem’s massive scale and the development community’s reliance on third-party packages make this attack vector particularly attractive to cybercriminals, requiring enhanced vigilance and security investment from the entire software development community.
**We at Mend.io will continue tracking the situation and updating this blog once new information will become available
Affected packages
The following packages were confirmed to be compromised in this attack. Organizations should immediately audit their dependencies for these packages and versions.
Package | Compromised version(s) |
---|---|
@ahmedhfarag/ngx-perfect-scrollbar | 20.0.20 |
@ahmedhfarag/ngx-virtual-scroller | 4.0.4 |
@art-ws/common | 2.0.22 |
@art-ws/config-eslint | 2.0.4, 2.0.5 |
@art-ws/config-ts | 2.0.7, 2.0.8 |
@art-ws/db-context | 2.0.21 |
@art-ws/di | 2.0.28 |
@art-ws/di-node | 2.0.13 |
@art-ws/eslint | 1.0.5, 1.0.6 |
@art-ws/fastify-http-server | 2.0.24 |
@art-ws/http-server | 2.0.21 |
@art-ws/openapi | 0.1.9 |
@art-ws/package-base | 1.0.5, 1.0.6 |
@art-ws/prettier | 1.0.5, 1.0.6 |
@art-ws/slf | 2.0.15 |
@art-ws/ssl-info | 1.0.9, 1.0.10 |
@art-ws/web-app | 1.0.3, 1.0.4 |
@crowdstrike/commitlint | 8.1.1, 8.1.2 |
@crowdstrike/falcon-shoelace | 0.4.1 |
@crowdstrike/foundry-js | 0.19.1, 0.19.2 |
@crowdstrike/glide-core | 0.34.2, 0.34.3 |
@crowdstrike/logscale-dashboard | 1.205.1, 1.205.2 |
@crowdstrike/logscale-file-editor | 1.205.1, 1.205.2 |
@crowdstrike/logscale-parser-edit | 1.205.1, 1.205.2 |
@crowdstrike/logscale-search | 1.205.1, 1.205.2 |
@crowdstrike/tailwind-toucan-base | 5.0.1, 5.0.2 |
@ctrl/deluge | 7.2.1, 7.2.2 |
@ctrl/golang-template | 1.4.2, 1.4.3 |
@ctrl/magnet-link | 4.0.3, 4.0.4 |
@ctrl/ngx-codemirror | 7.0.1, 7.0.2 |
@ctrl/ngx-csv | 6.0.1, 6.0.2 |
@ctrl/ngx-emoji-mart | 9.2.1, 9.2.2 |
@ctrl/ngx-rightclick | 4.0.1, 4.0.2 |
@ctrl/qbittorrent | 9.7.1, 9.7.2 |
@ctrl/react-adsense | 2.0.1, 2.0.2 |
@ctrl/shared-torrent | 6.3.1, 6.3.2 |
@ctrl/tinycolor | 4.1.1, 4.1.2 |
@ctrl/torrent-file | 4.1.1, 4.1.2 |
@ctrl/transmission | 7.3.1 |
@ctrl/ts-base32 | 4.0.1, 4.0.2 |
@hestjs/core | 0.2.1 |
@hestjs/cqrs | 0.1.6 |
@hestjs/demo | 0.1.2 |
@hestjs/eslint-config | 0.1.2 |
@hestjs/logger | 0.1.6 |
@hestjs/scalar | 0.1.7 |
@hestjs/validation | 0.1.6 |
@nativescript-community/arraybuffers | 1.1.6, 1.1.7, 1.1.8 |
@nativescript-community/gesturehandler | 2.0.35 |
@nativescript-community/perms | 3.0.5, 3.0.6, 3.0.7, 3.0.8 |
@nativescript-community/sqlite | 3.5.2, 3.5.3, 3.5.4, 3.5.5 |
@nativescript-community/text | 1.6.9, 1.6.10, 1.6.11, 1.6.12 |
@nativescript-community/typeorm | 0.2.30, 0.2.31, 0.2.32, 0.2.33 |
@nativescript-community/ui-collectionview | 6.0.6 |
@nativescript-community/ui-document-picker | 1.1.27, 1.1.28 |
@nativescript-community/ui-drawer | 0.1.30 |
@nativescript-community/ui-image | 4.5.6 |
@nativescript-community/ui-label | 1.3.35, 1.3.36, 1.3.37 |
@nativescript-community/ui-material-bottom-navigation | 7.2.72, 7.2.73, 7.2.74, 7.2.75 |
@nativescript-community/ui-material-bottomsheet | 7.2.72 |
@nativescript-community/ui-material-core | 7.2.72, 7.2.73, 7.2.74, 7.2.75 |
@nativescript-community/ui-material-core-tabs | 7.2.72, 7.2.73, 7.2.74, 7.2.75 |
@nativescript-community/ui-material-ripple | 7.2.72, 7.2.73, 7.2.74, 7.2.75 |
@nativescript-community/ui-material-tabs | 7.2.72, 7.2.73, 7.2.74, 7.2.75 |
@nativescript-community/ui-pager | 14.1.36, 14.1.37, 14.1.38 |
@nativescript-community/ui-pulltorefresh | 2.5.4, 2.5.5, 2.5.6, 2.5.7 |
@nexe/config-manager | 0.1.1 |
@nexe/eslint-config | 0.1.1 |
@nexe/logger | 0.1.3 |
@nstudio/angular | 20.0.4, 20.0.5, 20.0.6 |
@nstudio/focus | 20.0.4, 20.0.5, 20.0.6 |
@nstudio/nativescript-checkbox | 2.0.6, 2.0.7, 2.0.8, 2.0.9 |
@nstudio/nativescript-loading-indicator | 5.0.1, 5.0.2, 5.0.3, 5.0.4 |
@nstudio/ui-collectionview | 5.1.11, 5.1.12, 5.1.13, 5.1.14 |
@nstudio/web | 20.0.4 |
@nstudio/web-angular | 20.0.4 |
@nstudio/xplat | 20.0.5, 20.0.6, 20.0.7 |
@nstudio/xplat-utils | 20.0.5, 20.0.6, 20.0.7 |
@operato/board | 9.0.36, 9.0.37, 9.0.38, 9.0.39, 9.0.40, 9.0.41, 9.0.42, 9.0.43, 9.0.44, 9.0.45, 9.0.46 |
@operato/data-grist | 9.0.29, 9.0.35, 9.0.36, 9.0.37 |
@operato/graphql | 9.0.22, 9.0.35, 9.0.36, 9.0.37, 9.0.38, 9.0.39, 9.0.40, 9.0.41, 9.0.42, 9.0.43, 9.0.44, 9.0.45, 9.0.46 |
@operato/headroom | 9.0.2, 9.0.35, 9.0.36, 9.0.37 |
@operato/help | 9.0.35, 9.0.36, 9.0.37, 9.0.38, 9.0.39, 9.0.40, 9.0.41, 9.0.42, 9.0.43, 9.0.44, 9.0.45, 9.0.46 |
@operato/i18n | 9.0.35, 9.0.36, 9.0.37 |
@operato/input | 9.0.27, 9.0.35, 9.0.36, 9.0.37, 9.0.38, 9.0.39, 9.0.40, 9.0.41, 9.0.42, 9.0.43, 9.0.44, 9.0.45, 9.0.46 |
@operato/layout | 9.0.35, 9.0.36, 9.0.37 |
@operato/popup | 9.0.22, 9.0.35, 9.0.36, 9.0.37, 9.0.38, 9.0.39, 9.0.40, 9.0.41, 9.0.42, 9.0.43, 9.0.44, 9.0.45, 9.0.46 |
@operato/pull-to-refresh | 9.0.36, 9.0.37, 9.0.38, 9.0.39, 9.0.40, 9.0.41, 9.0.42 |
@operato/shell | 9.0.22, 9.0.35, 9.0.36, 9.0.37, 9.0.38, 9.0.39 |
@operato/styles | 9.0.2, 9.0.35, 9.0.36, 9.0.37 |
@operato/utils | 9.0.22, 9.0.35, 9.0.36, 9.0.37, 9.0.38, 9.0.39, 9.0.40, 9.0.41, 9.0.42, 9.0.43, 9.0.44, 9.0.45, 9.0.46 |
@teselagen/bounce-loader | 0.3.16, 0.3.17 |
@teselagen/liquibase-tools | 0.4.1 |
@teselagen/range-utils | 0.3.14, 0.3.15 |
@teselagen/react-list | 0.8.19, 0.8.20 |
@teselagen/react-table | 6.10.19 |
@thangved/callback-window | 1.1.4 |
@things-factory/attachment-base | 9.0.43, 9.0.44, 9.0.45, 9.0.46, 9.0.47, 9.0.48, 9.0.49, 9.0.50 |
@things-factory/auth-base | 9.0.43, 9.0.44, 9.0.45 |
@things-factory/email-base | 9.0.42, 9.0.43, 9.0.44, 9.0.45, 9.0.46, 9.0.47, 9.0.48, 9.0.49, 9.0.50, 9.0.51, 9.0.52, 9.0.53, 9.0.54 |
@things-factory/env | 9.0.42, 9.0.43, 9.0.44, 9.0.45 |
@things-factory/integration-base | 9.0.43, 9.0.44, 9.0.45 |
@things-factory/integration-marketplace | 9.0.43, 9.0.44, 9.0.45 |
@things-factory/shell | 9.0.43, 9.0.44, 9.0.45 |
@tnf-dev/api | 1.0.8 |
@tnf-dev/core | 1.0.8 |
@tnf-dev/js | 1.0.8 |
@tnf-dev/mui | 1.0.8 |
@tnf-dev/react | 1.0.8 |
@ui-ux-gang/devextreme-angular-rpk | 24.1.7 |
@yoobic/design-system | 6.5.17 |
@yoobic/jpeg-camera-es6 | 1.0.13 |
@yoobic/yobi | 8.7.53 |
airchief | 0.3.1 |
airpilot | 0.8.8 |
angulartics2 | 14.1.1, 14.1.2 |
browser-webdriver-downloader | 3.0.8 |
capacitor-notificationhandler | 0.0.2, 0.0.3 |
capacitor-plugin-healthapp | 0.0.2, 0.0.3 |
capacitor-plugin-ihealth | 1.1.8, 1.1.9 |
capacitor-plugin-vonage | 1.0.2, 1.0.3 |
capacitorandroidpermissions | 0.0.4, 0.0.5 |
config-cordova | 0.8.5 |
cordova-plugin-voxeet2 | 1.0.24 |
cordova-voxeet | 1.0.32 |
create-hest-app | 0.1.9 |
db-evo | 1.1.4, 1.1.5 |
devextreme-angular-rpk | 21.2.8 |
ember-browser-services | 5.0.2, 5.0.3 |
ember-headless-form | 1.1.2, 1.1.3 |
ember-headless-form-yup | 1.0.1 |
ember-headless-table | 2.1.5, 2.1.6 |
ember-url-hash-polyfill | 1.0.12, 1.0.13 |
ember-velcro | 2.2.1, 2.2.2 |
encounter-playground | 0.0.2, 0.0.3, 0.0.4, 0.0.5 |
eslint-config-crowdstrike | 11.0.2, 11.0.3 |
eslint-config-crowdstrike-node | 4.0.3, 4.0.4 |
eslint-config-teselagen | 6.1.7 |
globalize-rpk | 1.7.4 |
graphql-sequelize-teselagen | 5.3.8 |
html-to-base64-image | 1.0.2 |
json-rules-engine-simplified | 0.2.1 |
jumpgate | 0.0.2 |
koa2-swagger-ui | 5.11.1, 5.11.2 |
mcfly-semantic-release | 1.3.1 |
mcp-knowledge-base | 0.0.2 |
mcp-knowledge-graph | 1.2.1 |
mobioffice-cli | 1.0.3 |
monorepo-next | 13.0.1, 13.0.2 |
mstate-angular | 0.4.4 |
mstate-cli | 0.4.7 |
mstate-dev-react | 1.1.1 |
mstate-react | 1.6.5 |
ng2-file-upload | 7.0.2, 7.0.3, 8.0.1, 8.0.2, 8.0.3, 9.0.1 |
ngx-bootstrap | 18.1.4, 19.0.3, 19.0.4, 20.0.3, 20.0.4, 20.0.5 |
ngx-color | 10.0.1, 10.0.2 |
ngx-toastr | 19.0.1, 19.0.2 |
ngx-trend | 8.0.1 |
ngx-ws | 1.1.5, 1.1.6 |
oradm-to-gql | 35.0.14, 35.0.15 |
oradm-to-sqlz | 1.1.2 |
ove-auto-annotate | 0.0.9 |
pm2-gelf-json | 1.0.4, 1.0.5 |
printjs-rpk | 1.6.1 |
react-complaint-image | 0.0.32 |
react-jsonschema-form-conditionals | 0.3.18 |
remark-preset-lint-crowdstrike | 4.0.1, 4.0.2 |
rxnt-authentication | 0.0.3, 0.0.4, 0.0.5, 0.0.6 |
rxnt-healthchecks-nestjs | 1.0.2, 1.0.3, 1.0.4, 1.0.5 |
rxnt-kue | 1.0.4, 1.0.5, 1.0.6, 1.0.7 |
swc-plugin-component-annotate | 1.9.1, 1.9.2 |
tbssnch | 1.0.2 |
teselagen-interval-tree | 1.1.2 |
tg-client-query-builder | 2.14.4, 2.14.5 |
tg-redbird | 1.3.1 |
tg-seq-gen | 1.0.9, 1.0.10 |
thangved-react-grid | 1.0.3 |
ts-gaussian | 3.0.5, 3.0.6 |
ts-imports | 1.0.1, 1.0.2 |
tvi-cli | 0.1.5 |
ve-bamreader | 0.2.6 |
ve-editor | 1.0.1 |
verror-extra | 6.0.1 |
voip-callkit | 1.0.2, 1.0.3 |
wdio-web-reporter | 0.1.3 |
yargs-help-output | 5.0.3 |
yoo-styles | 6.0.326 |