Safeguard
Buyer's Guides

Comparing Malicious Package Tactics Across npm, PyPI, Rub...

npm, PyPI, RubyGems, and crates.io each get hit by malicious packages differently. Real incidents from 2018-2025 show how attacker tactics shift by ecosystem.

Vikram Iyer
Security Researcher
7 min read

Malicious package campaigns don't look the same on every registry. npm's flexible install-scripts and billion-download packages create the perfect surface for phishing-driven maintainer takeovers, like the September 2025 compromise of chalk and debug. PyPI's setup.py execution model favors info-stealers such as W4SP hidden inside typosquats. RubyGems users learned this the hard way in 2020, when researchers pulled 725 gems mimicking cryptocurrency tools built to hijack clipboard contents. crates.io, protected by Rust's compile step, has logged far fewer incidents but is not immune, as the 2022 "rustdecimal" build.rs backdoor proved. Comparing these four ecosystems side by side shows that the same handful of attacker tactics — typosquatting, dependency confusion, and account takeover — get reshaped by each language's install mechanics. Understanding those differences is the first step to defending a polyglot software supply chain.

Why is npm the biggest target for install-time malware?

npm is the biggest target because its scale (over 3 million published packages) combines with arbitrary preinstall/postinstall script execution, handing attackers code execution the moment npm install runs — before any application code ever loads. The pattern is well documented: the 2018 event-stream incident saw a new "maintainer" add a hidden dependency that stole Bitcoin wallet credentials from a downstream app; the October 2021 ua-parser-js hijack pushed cryptominers and password stealers to a package with roughly 7 million weekly downloads after the maintainer's npm account was compromised; and the March 2022 node-ipc "protestware" incident had the maintainer ship code that wiped files on machines with Russian or Belarusian IP addresses. The most consequential example is more recent: in September 2025, phishing emails from a spoofed "npmjs.help" domain tricked a maintainer into handing over credentials for chalk, debug, ansi-styles, and roughly 18 other packages with a combined 2 billion+ weekly downloads, briefly injecting a crypto-clipper into the JavaScript ecosystem's dependency tree. Days later, the self-propagating "Shai-Hulud" worm used stolen npm tokens to republish trojanized versions of packages automatically, without a human attacker driving each step.

How do PyPI attackers exploit Python's flexible packaging model?

PyPI attackers exploit the fact that setup.py and build backends can execute arbitrary code at install time, so a single pip install of a convincingly named package is enough to run a payload before anyone inspects the source. Typosquatting is the primary delivery method: campaigns have registered lookalikes such as "colourama" for colorama and near-miss names for requests, urllib, and beautifulsoup4. The W4SP Stealer campaign, tracked through 2022, hid credential- and Discord-token-stealing code inside dozens of typosquatted packages, several of which racked up thousands of downloads before PyPI removed them. Account takeover has also hit trusted packages directly — in May 2022, the widely used ctx package and the PHP-oriented phpass package were both compromised after their maintainer domains expired and were re-registered by an attacker, who pushed versions that exfiltrated environment variables (including cloud credentials) to a remote server. PyPI's security team has since suspended thousands of malicious or typosquatted projects, but the install-time execution model remains the ecosystem's structural weak point.

What made the 2020 RubyGems cryptocurrency-stealing campaign different?

The 2020 RubyGems campaign stands out because it was a coordinated operation rather than a one-off typosquat: researchers at ReversingLabs, working with RubyGems maintainers, identified and pulled 725 malicious gems — most impersonating cryptocurrency and blockchain-related libraries — that had been uploaded over roughly a two-month window. Once installed, the gems used a Windows-specific payload that monitored the clipboard and silently swapped any copied cryptocurrency wallet address for one the attacker controlled, a low-noise tactic that could sit undetected for months. RubyGems has also seen direct compromise of a trusted package: in 2019, the rest-client gem — with well over 100 million cumulative downloads — was hijacked after its maintainer's credentials were stolen, and versions 1.6.10 through 1.6.13 shipped with a backdoor capable of downloading and executing arbitrary Ruby code on any machine that installed it. Both incidents show RubyGems attackers favoring stealth and trust exploitation over the noisy install-script tactics common on npm.

Why has crates.io seen far fewer malicious-package incidents?

crates.io has recorded far fewer incidents because Rust's compiled, statically typed model narrows the install-time attack surface — there is no npm-style default script hook that fires on every dependency install, so an attacker generally needs a project to actually build and run the crate's code. That is not the same as immunity: build.rs scripts execute arbitrary Rust at compile time, and in 2022 the crates.io security team caught "rustdecimal," a typosquat of the popular rust_decimal crate, using its build.rs to download and execute a second-stage payload on Windows and Linux hosts. The crate was pulled within roughly a day of publication, a response time credited to the ecosystem's smaller size and active community scrutiny rather than any structural defense unique to Rust. crates.io has since tightened namespace and ownership policies, but the underlying lesson holds across compiled-language registries: fewer incidents so far reflects smaller attacker ROI, not a lower ceiling of risk.

What tactics show up across all four ecosystems?

Four tactics recur across npm, PyPI, RubyGems, and crates.io regardless of language: typosquatting, maintainer account takeover via phishing, dependency confusion, and abuse of whatever install- or build-time script hook the ecosystem exposes. Dependency confusion is the clearest cross-ecosystem case — security researcher Alex Birsan's 2021 research showed that uploading public packages matching internal, private package names could get pulled automatically by misconfigured build systems, and he successfully executed code inside the networks of more than 30 companies including Apple, Microsoft, and Tesla across npm, PyPI, and RubyGems simultaneously. Phishing-driven account takeover is the second constant: npm's September 2025 "npmjs.help" campaign and PyPI's 2022 lookalike login-page campaigns both used the same playbook — a fake security notice pushing maintainers to "re-verify" credentials on a spoofed domain. The difference between ecosystems is almost entirely about what happens after that takeover succeeds: npm and PyPI attackers get instant script execution on install, RubyGems attackers get execution once the gem's code runs, and crates.io attackers need a build.rs hook or a successfully compiled dependency.

What does this mean for teams running mixed-language stacks?

Teams running polyglot stacks need ecosystem-aware detection rather than a single antivirus-style signature list, because identical attacker goals — stealing secrets, mining cryptocurrency, planting a backdoor — manifest through entirely different mechanics depending on the registry. A scanner tuned only to flag suspicious postinstall scripts will miss a RubyGems clipboard-hijacker or a crates.io build.rs payload entirely, while a tool that only checks package names against a static typosquat list will miss an npm account takeover of a legitimately named, previously trustworthy package. The practical takeaway from comparing these four registries is that any given dependency tree — especially in organizations running Node.js services alongside Python data pipelines, Ruby backends, or Rust infrastructure tooling — needs continuous monitoring for behavioral anomalies (new maintainers, sudden script changes, unexpected network calls at install or build time) layered on top of typosquat and reputation checks, not a one-time scan at intake.

How Safeguard Helps

Safeguard is built around exactly this cross-ecosystem reality. Rather than treating npm, PyPI, RubyGems, and crates.io as interchangeable feeds, Safeguard applies registry-specific behavioral analysis: it inspects preinstall/postinstall scripts and newly introduced dependencies in npm packages, flags setup.py and build-backend code execution in PyPI packages, watches for post-publish maintainer and ownership changes on RubyGems, and analyzes build.rs behavior in Rust crates before they ever reach a build pipeline. Every new or updated package is checked against typosquat and dependency-confusion patterns, maintainer account changes are correlated with the phishing infrastructure patterns seen in incidents like the 2025 npm compromise, and suspicious network calls or file-system access triggered at install or build time raise an alert before the package lands in a production artifact. For teams managing mixed-language services, Safeguard rolls all of this into a single policy layer and SBOM view, so a security team doesn't need separate tooling — and separate expertise — for every package manager in the stack. The goal is straightforward: catch the tactic, not just the ecosystem it happened to show up in first.

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.