Open Source Supply Chain Threats: February 2026 in Review
By Juan Aguirre
February was quieter than January, 507 malicious packages across NPM and PyPI, down from 652. But the interesting story this month isn't the volume. It's the shift in where the threats are coming from.
February by the Numbers
Total malicious packages: 507.
NPM: 340 (67%).
PyPI: 167 (33%).
The PyPI Surge
Here's what caught my attention: PyPI's share of malicious packages has tripled since December.
NPM vs PyPI share of malicious packages by month:
December: NPM 89%, PyPI 11%.
January: NPM 87%, PyPI 13%.
February: NPM 67%, PyPI 33%.
NPM still leads, but the gap has significantly reduced. Part of this is NPM volume dropping, threat actors coming back from the holidays, as we noted last month. But PyPI isn't just holding steady; it's growing its share. Python's dominance in AI/ML pipelines makes it an increasingly attractive target, and we're seeing that play out in the data.
We also saw a spike February 2-4, with 131 packages published in just three days. That kind of burst usually indicates automated or bulk publishing, someone running a script rather than manually uploading packages one by one.
Spotlight: The DGXeon WhatsApp Botnet
Every once in a while, you come across a campaign that makes you pause and appreciate the creativity. Not in a "good for them" way, but in a "wow, that's not what I expected" way. The DGXeon campaign was that for me this month.
Most malicious packages we see are after the usual targets: credentials, crypto wallets, environment variables. This one? It's building a WhatsApp follower botnet.
Let me explain.
The Setup
Late February, six packages appeared on NPM, all tied to the same threat actor:
@dgxeon/libsignal-nodesocket-dgxeondgxeon-soketdgxeon-soket2dgxeon-soket3dgxeon-soket4The target: developers building WhatsApp bots using Baileys, a popular open-source WhatsApp Web API library. The method: a supply chain trojan that hijacks the library after installation.
The Attack Chain
Here's where it gets interesting. The core payload lives in @dgxeon/libsignal-node, which impersonates the legitimate Signal protocol library. But the distribution happens through dgxeon-soket, a forked copy of Baileys with one critical modification in package.json:

This uses npm's package aliasing feature. When Baileys' internal code calls require('libsignal'), it doesn't load the real Signal library—it loads the attacker's malicious package instead. Clever.
Stage 1: Silent Trigger
When the malicious @dgxeon/libsignal-node is imported, it doesn't need an install hook. Just require() is enough:

No noise, no errors, no indication anything happened.
Stage 2: Find the Victim
The payload searches for Baileys on your system:

It checks process.cwd()/node_modules, relative paths from __dirname, and require.resolve. It's hunting for one specific file: newsletter.js.
Stage 3: File Replacement
This is the part that got my attention. The malware doesn't just run malicious code—it overwrites legitimate code on disk:
That last part, forcing a process exit, ensures the patched code gets loaded fresh on restart. The .cache prevents re-infection if the package runs again. And the marker uses Iove with a capital I instead of love with a lowercase L. A small obfuscation detail, but it shows attention to craft.

Stage 4: The Payload
Here's where the goal becomes clear. The injected code activates 120 seconds after the WhatsApp socket initializes:
The attacker hosts their target list on GitHub: hxxps[:]//raw[.]githubusercontent[.]com/DGXeon13/strings/refs/heads/main/strings[.]json. The malware fetches this list, then uses the victim's authenticated WhatsApp session to auto-follow the attacker's WhatsApp Channels. Eleven seconds between each follow to mimic organic behavior and avoid rate limiting.

What This Achieves
The attacker is building a follower-count inflation botnet. Every compromised developer's WhatsApp account becomes an unwitting subscriber to whatever channels the attacker is promoting. The channel list is dynamically controlled via GitHub, so targets can change without publishing a new package version.
It's not stealing your credentials. It's not draining your crypto. It's using your authenticated session to inflate subscriber counts, probably for monetization or clout. And because the infection persists in the overwritten Baileys files, removing the npm package doesn't fix it. The legitimate library code has already been replaced on disk.
But Wait, There's More: The Pairing Code Hijack
The follower botnet is clever, but it's not the most severe finding. In the dgxeon-soket-buttonx variant, we found a hardcoded WhatsApp pairing code:

If a bot developer calls requestPairingCode(phoneNumber) without providing their own key, the pairing code is always XEONKING. The attacker knows this. They can race to link their own device to the victim's WhatsApp account before the legitimate pairing completes.
Its worth noting that removing the npm package does NOT unlink the attacker's device. They've got persistent access to the victim's WhatsApp account, and unless the developer manually checks their linked devices, they'll never know.
The Hardened Variant: dgxeon-baileys
While investigating, we found dgxeon-baileys, an evolved version of the campaign with more obfuscation and debugging traps
File comparison between buttonx and dgxeon-baileys:
chats.js — buttonx: 39KB, readable → dgxeon-baileys: 141KB, obfuscated. Change: 3.6x larger, single-line blob.
luxu.js → dugong.js — buttonx: 24KB, readable → dgxeon-baileys: 96KB, obfuscated. Change: RSA + SHA256 added.
socket.js — buttonx: 30KB → dgxeon-baileys: 27KB. Change: bug_pair removed.
The obfuscated dugong.js contains things we didn't see in the earlier variants:
randomPrime(), generate(), encrypt(), decrypt() methods for encrypting exfiltrated dataThe hardcoded pairing code changed too: XEONKING → GGGGGGGG. Different default, same attack.
A Two-Tier Campaign
The campaign is structured in layers, which is part of why it's hard to catch with static analysis:
Tier 1: Loaders — Packages: @dgxeon13/libsignal-node, dgxeon-baileys. Behavior: Pull in malicious deps, contain the install.js that patches Baileys' newsletter.js with the auto-follow botnet.
Tier 2: Full Forks — Packages: dgxeon-soket, dgxeon-soket2-4, dgxeon-soket-buttonx. Behavior: Trojanized WhatsApp bot frameworks with hardcoded pairing hijack, hidden auth sessions in .npm/, and dependency on Tier 1 for the newsletter payload.
The newsletter.js file ships clean in the Tier 2 packages. The malicious version gets injected at runtime when the Tier 1 dependency is imported. A static analysis of the tarball alone wouldn't find the payload, you have to trace the dependency chain and understand the runtime behavior.
Still Live on NPM
Safety discovered and reported additional packages from this campaign during our investigation. As of this writing, these packages have been reported but remain available on NPM:
dgxeon-soket-buttonx (published March 10)dgxeon-baileys (published March 5)@dgxeon13/libsignal-node (published February 27)If you're building WhatsApp bots with Baileys, check your dependencies.
Other Notable Findings
tensorflow-opt (PyPI) — A dependency confusion attack targeting TensorFlow extension packages. The attacker published 26 malicious versions (0.6 through 0.40), each containing a cryptominer that copies itself into the real TensorFlow installation directory to hide. A good reminder that cryptojacking is alive and well as an alternative to data theft.
Cline Supply Chain Attack — The popular AI coding assistant Cline had version 2.3.0 of its npm package compromised on February 17th. The attacker used a stolen long-lived token to bypass trusted publishing and added a post-install hook that silently installed OpenClaw globally. The malicious version was pulled the same day, but not before racking up significant downloads. A reminder that even packages with trusted publishing enabled aren't safe if token-based publication isn't disabled.
Dependency Confusion Targeting Real Companies — We identified 11+ organizations targeted by dependency confusion attacks this month, including HashiCorp, IKEA, Proton, and Qualys. Internal package names getting squatted on public registries remains a persistent problem.
React Typosquatting — 20 unique packages typosquatting React-related names. The most targeted library name this month by a wide margin.
We also published deeper dives on a few campaigns:
How Can Safety Help Protect You?
Traditional vulnerability scanning tools aren't designed to catch this kind of threat. A file-patching trojan that persists after package removal? Dependency aliasing tricks? Hidden auth sessions in .npm/ directories? These don't show up in your SBOM or your CI pipeline—they live on developer machines.
Safety gives security teams real-time visibility into what's installed and running across developer machines, including AI skills, extensions, and packages that traditional SCA never sees
The Safety Firewall analyzes every package installation request before it reaches public repositories, automatically blocking malicious, vulnerable, and policy-violating packages before they enter your systems. Prevention and observability, not just detection.
Interested in trying Safety? Reach out and we'll get you set up.
Feel free to reach out with any questions!