Last updated 4:00 p.m. ET on September 19, 2025
Today, we've observed a software supply chain attack targeting npm maintainers' publishing credentials, followed by the rapid release of new versions carrying malicious payloads.
To be clear, this is not the same incident as the recent chalk/debug compromise. Nor is it confirmed to be the same actor as the similar, but distinct, event — S1ngularity — which occurred in late August 2025, when attackers stole an Nx project publishing token and pushed malicious versions of several Nx packages llive on npm with the aim of exfiltrating sensitive data from users' systems over a short four-hour window.
Today's software supply chain attack concerns the Shai-Hulud campaign, which executes a multi-stage payload that steals credentials from the affected developer machine. If the payload achieves GitHub access, it then publishes the repository Shai-Hulud, which contains all exfiltrated secrets, and self-propagates by poisoning other npm packages in the project.
Sonatype, alongside other security research firms, has tracked this malicious campaign and continued to analyze indicators of compromise (IoCs). So far, collective efforts have tracked over 180 compromised packages.
Sonatype continues to track the affected packages, and our Release Integrity systems continue to discover newly affected items that we are adding to our data.
The Timeline So Far
-
August 26, 2025: The S1ngularity attack begins. Attackers exploit a vulnerable GitHub Actions workflow in the Nx repository to steal an npm publishing token. Malicious versions of several Nx packages are published to npm. Incident tracked as sonatype-2025-003584.
-
August 26-27, 2025: The malicious post-install script (telemetry.js) in the compromised Nx package begins scanning developer machines for credentials, secrets, SSH keys, and crypto-wallet files, and exfiltrates the data to attacker-controlled GitHub repositories. Tens of thousands of files and over two thousand distinct secrets are exposed.
-
August 27-28, 2025: Public repositories containing the exfiltrated secrets are created, many repositories named with "s1ngularity-repository." GitHub disables many attacker-created repositories and removes them once detected.
-
September 8, 2025: A separate yet significant campaign comes to light: widespread npm supply chain attack targeting 18 popular npm packages (including but not limited to debug, chalk, etc.), with malicious code injected in updates. These packages together see billions of weekly downloads, raising concern about broad exposure.
-
September 9, 2025: Sonatype publishes findings on compromised popular npm packages (chalk / debug campaign). Incident tracked as sonatype-2025-003716).
-
September 16, 2025: A new campaign involves over 180 compromised npm packages. Sonatype's Release Integrity systems continue to discover newly affected items. Incident tracked as sonatype-2025-003810.
Together, these incidents demonstrate a clear escalation in both scale and sophistication: from S1ngularity, which focused on a single compromised package, to the high-profile takeover of popular projects like chalk and debug, and now to a rapidly spreading campaign affecting more than 180 npm packages.
From Secrets to Spread: Technical Breakdown
The malware is engineered to both exfiltrate secrets and replicate itself across the ecosystem:
-
Credential Collection: The payload harvests system information, environment variables, and authentication tokens from npm, GitHub, AWS, and GCP. It also uses tools like TruffleHog to scan for additional secrets.
-
Exfiltration: Stolen data is funneled to attacker-controlled destinations. The only unique indicator of compromise (IoC) tied to this infection is a webhook URL: https://webhook.site/bb8ca5f6-4175-45d2-b042-fc9ebb8170b7
All other outbound connections point to legitimate cloud services used for permission checks.
-
Repository Creation: If GitHub access is available, the malware automatically creates a new repository under the victim's account named "Shai-Hulud." Into this repo, it commits a JSON file (data.json) containing the harvested tokens, credentials, and environment data.
-
Propagation via GitHub Actions: In some cases, the malware drops a malicious workflow file (.github/workflows/shai-hulud-workflow.yml). This workflow serializes secrets, exfiltrates them to the attacker's webhook, and even encodes copies directly into Actions logs.
-
Self-Propagation: Any npm tokens uncovered are then used to publish trojanized versions of packages under the control of the maintainer, expanding the infection outward. Our analysis of the bundle.js payload confirms this campaign goes beyond simple credential theft.
Below are several annotated screenshots of the payload code, showing how the malware collects system secrets, generates the Shai-Hulud repository, and installs the GitHub Actions workflow to keep exfiltrating data.
Screenshot 1, secret harvesting from multiple environments:
Screenshot 2, automated creation of a public GitHub repo named "Shai-Hulud" with harvested credentials:
Screenshot 3, injected GitHub Actions workflow for persistent exfiltration:
Here's a breakdown of how the worm functionality works:
-
Repository Iteration: The script fetches a list of repositories the user has access to, and then iterates through each one using a while loop.
-
Branch Creation: For each repository, it creates a new branch named shai-hulud.
-
Malicious Workflow Upload: It then uploads a file named .github/workflows/shai-hulud-workflow.yml to this new branch. This YAML file contains a GitHub Actions workflow that, when triggered, will exfiltrate secrets.
-
Pull Request Creation: Finally, it creates a pull request to merge the shai-hulud branch into the repository's default branch. This is the primary mechanism for propagation. If the pull request is merged, the malicious workflow becomes part of the repository and will be triggered on the next push.
Here is the specific code block that constitutes the worm's propagation mechanism:
Taken together, these behaviors demonstrate that this malware is not just stealing secrets. It is explicitly designed to spread itself, persist, and magnify its impact inside the software supply chain.
Why This Matters
Supply chain attacks of this kind highlight how fragile the global open source ecosystem can be when trusted distribution channels are compromised.
npm, as one of the world's largest package repositories, is at the heart of modern software development, meaning even a relatively small compromise can ripple across thousands of projects and organizations. Attacks like this are not isolated events, but part of a growing trend in which adversaries target the weakest links, credentials, developer accounts, or CI/CD pipelines, to maximize the impact of their campaigns.
The discovery that this variant is capable of self-propagation further elevates its significance. Unlike one-off malicious packages that need to be manually introduced, self-replicating malware accelerates the spread, making it far more difficult for developers and organizations to contain. Security researchers have warned that these worm-like traits represent an evolution in supply chain threats, raising the stakes for defenders.
What Wormable Malware Does
Wormable malware is dangerous, because it doesn't stop at a single compromise. It actively seeks ways to expand. You may have heard of WannaCry — an infamous wormable ransomware that took down hospitals in 2017.
In this campaign, which is certainly the most severe, wormable malware infects open source.
This infection follows a repeating, self-propagating cycle, powered in several areas by AI:
-
Collects secrets from the developer machine or CI environment, including tokens, environment variables, and cloud credentials.
-
Leaks the data to attacker-controlled destinations, such as GitHub repos or webhooks.
-
Publishes poisoned packages using any valid npm tokens it finds, ensuring the malware spreads into new projects.
-
Expands access by altering repositories or adding workflows that enable further credential theft and propagation.
This self-reinforcing process allows the malware to spread quickly and widely, turning one compromised package into a chain reaction across the ecosystem.
The New, New, New Normal
As we discussed in our previous blog post, this campaign continues as threat actors refine and improve upon methods that are proving successful. For a long time, we have discussed that open source malware is a key part of APTs' playbooks, as it allows far wider distribution of the malicious payload compared to traditional methods.
The persistence of this campaign also underscores that software supply chain security is no longer a theoretical issue, but an operational reality. Organizations can expect that adversaries will continue to experiment with new delivery methods, exploit trust in open source ecosystems, and adapt quickly when defenders close gaps. This creates a "new normal" where vigilance, rapid detection, and proactive defenses are essential, not optional.
How Sonatype Helps You Stay Ahead
Customers should:
-
Enable Sonatype Repository Firewall and turn on malware blocking to prevent developers or continuous integration builds from downloading the affected packages.
-
Rely on continuous monitoring alerts and automated PRs to report on affected applications.
-
Investigate software bills of materials (SBOMs) for further blast radius.
-
Pin dependency versions and avoid using open ranges in dependency manifests.
Sonatype continues to monitor the attack, and we will keep our customers informed of new developments.
Sonatype customers can rest assured that all affected components have been added to our knowledge base. Any applications impacted by this campaign are continuously monitored, with proactive alerts delivered as new developments emerge. Our customers can also leverage advanced search in Sonatype IQ Server to quickly identify and assess affected applications under monitoring.
A Full List of Vulnerable Packages and Versions
- @ahmedhfarag/ngx-perfect-scrollbar - 20.0.20
- @ahmedhfarag/ngx-virtual-scroller - 4.0.4
- @art-ws/common - 2.0.22, 2.0.28
- @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, 2.0.24
- @art-ws/di-node - 2.0.13
- @art-ws/di - 2.0.28, 2.0.32
- @art-ws/eslint - 1.0.5, 1.0.6
- @art-ws/fastify-http-server - 2.0.24, 2.0.27
- @art-ws/http-server - 2.0.21, 2.0.25
- @art-ws/openapi - 0.1.9, 0.1.12
- @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, 2.0.22
- @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, 0.4.2
- @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, 3.0.9
- @nativescript-community/sentry - 4.6.43
- @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, 1.6.13
- @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-tabs - 7.2.72, 7.2.73, 7.2.74, 7.2.75, 7.2.76
- @nativescript-community/ui-material-core - 7.2.72, 7.2.73, 7.2.74, 7.2.75, 7.2.76
- @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.35, 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-angular - 20.0.4
- @nstudio/web - 20.0.4
- @nstudio/xplat-utils - 20.0.4, 20.0.5, 20.0.6, 20.0.7
- @nstudio/xplat - 20.0.4, 20.0.5, 2.0.6, 20.0.7
- @operato/board - 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, 9.0.47, 9.0.48, 9.0.49, 9.0.50, 9.0.51
- @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, 9.0.47, 9.0.48, 9.0.49, 9.0.50, 9.0.51
- @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, 9.0.47, 9.0.48, 9.0.49, 9.0.50, 9.0.51
- @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, 9.0.47, 9.0.48
- @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, 9.0.47, 9.0.48, 9.0.49, 9.0.50, 9.0.51
- @operato/pull-to-refresh - 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, 9.0.47
- @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, 9.0.47, 9.0.48, 9.0.49, 9.0.50, 9.0.51
- @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, 6.10.20, 6.10.21, 6.10.22
- @teselagen/sequence-utils - 0.3.33
- @thangved/callback-window - 1.1.4
- @things-factory/attachment-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, 9.0.55
- @things-factory/auth-base - 9.0.42, 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, 9.0.55, 9.0.56, 9.0.57, 9.0.58, 9.0.59
- @things-factory/env - 9.0.42, 9.0.43, 9.0.44, 9.0.45
- @things-factory/integration-base - 9.0.42, 9.0.43, 9.0.44, 9.0.45
- @things-factory/integration-marketplace - 9.0.42, 9.0.43, 9.0.44, 9.0.45
- @things-factory/shell - 9.0.42, 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-yup - 1.0.1
- ember-headless-form - 1.1.2, 1.1.3
- 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-node - 4.0.3, 4.0.4
- eslint-config-crowdstrike - 11.0.2, 11.0.3
- eslint-config-teselagen - 6.1.7, 6.1.8
- globalize-rpk - 1.7.4
- graphql-sequelize-teselagen - 5.3.8, 5.3.9
- html-to-base64-image - 1.0.2
- json-rules-engine-simplified - 0.2.1, 0.2.2, 0.2.3, 0.2.4
- 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, 20.0.6
- 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, 1.1.3, 1.1.4, 1.1.5
- ove-auto-annotate - 0.0.9, 0.0.10
- pm2-gelf-json - 1.0.4, 1.0.5
- printjs-rpk - 1.6.1
- react-complaint-image - 0.0.32, 0.0.33, 0.0.34, 0.0.35
- react-jsonschema-form-conditionals - 0.3.18, 0.3.19, 0.3.20, 0.3.21
- react-jsonschema-form-extras - 1.0.1, 1.0.2, 1.0.3, 1.0.4
- 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, 1.3.2
- 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, 0.2.7
- ve-editor - 1.0.1, 1.0.2
- 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
Written by Sonatype Security Research Team
Sonatype's Security Research Team is focused on bringing real-time, in-depth intelligence and actionable information about open source and third party vulnerabilities to Sonatype customers.
Tags