SCA

Software Supply Chain Attacks: How Malicious Packages Infiltrate npm and PyPI

March 18, 2026 9 min read Supply Chain Security

Modern software applications are not built from scratch. They are assembled from hundreds — sometimes thousands — of open-source packages downloaded from public registries like npm (JavaScript), PyPI (Python), Maven Central (Java), NuGet (C#), and RubyGems (Ruby). This dependency-heavy development model has revolutionized productivity, but it has also created a massive attack surface that adversaries are increasingly exploiting.

A software supply chain attack occurs when an attacker compromises a component that is trusted and consumed by downstream applications. Instead of attacking your application directly, the attacker poisons a library that your application already depends on. When you install or update that library, the malicious code executes in your environment with whatever permissions your application has — access to databases, file systems, network resources, environment variables, and cloud credentials.

The consequences are severe: attackers gain a foothold inside your infrastructure through a vector that bypasses traditional perimeter defenses entirely. Firewalls, WAFs, and network segmentation offer no protection when the malicious code is delivered as a trusted dependency inside your build pipeline.

245% Increase in supply chain attacks (2022-2024)
700+ Malicious npm packages found monthly
3.5M Packages on npm as of 2025

Attack Vectors: How Attackers Weaponize Package Registries

Supply chain attacks exploit the trust that developers place in package registries and the convenience of automated dependency management. There are several distinct attack vectors, each with its own mechanics and countermeasures.

Typosquatting

How it works

Attackers publish packages with names that closely resemble popular packages, banking on developers making typos during installation. A single wrong character is all it takes. The malicious package contains the same functionality as the legitimate one (often stolen directly) plus hidden backdoor code that executes on installation.

The typosquatting attack surface is enormous because package registries use a flat namespace with minimal naming restrictions. For a popular package like lodash, attackers can register lodahs, l0dash, lodash-utils, or lodassh. For requests on PyPI, variants like requesrs, requets, python-requests, and request have all been observed hosting malicious code.

Attack
# Developer intends to install "requests" but makes a typo
pip install requesrs

# Or in package.json, a developer misspells a dependency
{
  "dependencies": {
    "lodassh": "^4.17.0",     // Typosquat of "lodash"
    "cross-env": "^7.0.0",     // Legitimate
    "crossenv": "^7.0.0"       // Typosquat - contains crypto miner!
  }
}

The crossenv incident in 2017 was one of the first widely publicized typosquatting attacks on npm. A malicious package named crossenv mimicked the popular cross-env package (note the missing hyphen). When installed, it stole environment variables — which commonly contain API keys, database passwords, and cloud credentials — and transmitted them to an attacker-controlled server.

Dependency Confusion

How it works

Organizations frequently create internal (private) packages for shared code. If an attacker discovers the names of these internal packages — through leaked manifests, error messages, or GitHub repositories — they can publish a public package with the same name but a higher version number. Many package managers will prioritize the public registry over the private one, causing the malicious public package to be installed instead of the internal one.

This attack was famously demonstrated by security researcher Alex Birsan in 2021, who successfully injected code into the build pipelines of Apple, Microsoft, PayPal, Shopify, Netflix, Tesla, Uber, and over 35 other major organizations. By publishing higher-versioned packages to npm and PyPI that matched the names of internal packages he discovered in public configuration files, his proof-of-concept code executed inside their corporate networks, earning him over $130,000 in bug bounties.

Attack
# Company's internal .npmrc configuration
registry=https://npm.internal.company.com

# Company's package.json references private packages
{
  "dependencies": {
    "company-auth-utils": "^1.2.0",   // Internal package (v1.2.0)
    "company-data-models": "^2.0.0"    // Internal package (v2.0.0)
  }
}

# Attacker discovers package names and publishes to public npm:
# "company-auth-utils" v99.0.0 (malicious) on public npm
# Package manager may fetch v99.0.0 from public npm
# instead of v1.2.0 from internal registry!

Defenses Against Dependency Confusion

Namespace scoping and registry pinning

Use scoped packages (@company/auth-utils) that are restricted to your organization's namespace. Configure your package manager to exclusively use your private registry for scoped packages, making it impossible for a public package to override them. Pin registries per-scope in your .npmrc or pip configuration.

Defense
# .npmrc - Pin scoped packages to internal registry
@company:registry=https://npm.internal.company.com
//npm.internal.company.com/:_authToken=${NPM_INTERNAL_TOKEN}

# All @company/* packages will ONLY be fetched from internal registry
# Public npm cannot serve packages in the @company scope

Chainjacking (Maintainer Account Takeover)

How it works

Attackers compromise the accounts of legitimate package maintainers through credential stuffing, phishing, or expired domain takeover. Once they control a maintainer account, they publish a seemingly innocent "patch" or "minor" update that contains malicious code. Because the update comes from a trusted source and uses a valid version number, it is automatically pulled by applications configured for automatic dependency updates.

This vector is particularly dangerous because it poisons a package that is already trusted and widely installed. Unlike typosquatting, where the victim must make a mistake, chainjacking targets packages that are already in use. The malicious update flows through normal update channels — CI/CD pipelines, automated dependency updaters, and developer machines running npm update.

Case Study: The event-stream Incident

The event-stream incident of 2018 remains the most instructive example of a sophisticated supply chain attack targeting the npm ecosystem. It demonstrates how social engineering, patience, and multi-layered obfuscation can compromise widely-used packages.

September 2018

Social Engineering Phase

A user named "right9ctrl" approached the original maintainer of event-stream (who had moved on and was no longer actively maintaining the package) and offered to take over maintenance. The original maintainer, wanting the package to be maintained, transferred publishing rights. This was not a hack — it was a social engineering attack exploiting open-source maintainer burnout.

September 2018

Dependency Injection

The new maintainer published version 3.3.6, adding a dependency on a new package called flatmap-stream. This seemed like a legitimate refactoring — extracting functionality into a separate module is a common practice. No malicious code was visible in event-stream itself.

October 2018

Payload Delivery

The flatmap-stream package contained encrypted malicious code in its minified bundle. The encryption key was derived from the package description of a specific cryptocurrency wallet application (Copay). This meant the payload would only decrypt and execute inside the Copay build process, making it invisible to anyone analyzing flatmap-stream in any other context.

November 2018

Discovery

A developer noticed the unexpected flatmap-stream dependency and investigated. The community discovered the attack, npm removed the malicious packages, and the cryptocurrency wallet was patched. However, event-stream had been downloaded 8 million times during the window of compromise, and the malicious code had been present for nearly two months.

The event-stream attack was groundbreaking in its sophistication. The attacker targeted a specific application (Copay) through an upstream dependency (event-stream) that Copay used, making the attack a targeted supply chain compromise rather than a mass-exploitation attempt. The encrypted payload ensured that the vast majority of event-stream users — and any security researchers analyzing the package — would never see the malicious behavior.

Recent Large-Scale Campaigns

Supply chain attacks have accelerated dramatically since the event-stream incident. Modern campaigns are more frequent, more automated, and harder to detect.

npm Campaigns (2023-2025)

PyPI Campaigns (2023-2025)

How SCA Scanning Protects Against Supply Chain Attacks

Software Composition Analysis (SCA) is the primary technical defense against supply chain attacks. SCA tools analyze your application's dependencies to identify vulnerabilities, license risks, and indicators of malicious packages.

Dependency Graph Analysis

SCA tools build a complete dependency graph by parsing manifest files and lock files. This reveals not just your direct dependencies but every transitive dependency — the dependencies of your dependencies, often going six or seven levels deep. A typical application with 50 direct dependencies may have 500 or more transitive dependencies, each of which represents a potential supply chain attack vector.

Analysis
# SCA dependency tree analysis output
your-application@1.0.0
+-- express@4.18.2
|   +-- body-parser@1.20.1
|   |   +-- bytes@3.1.2
|   |   +-- depd@2.0.0
|   |   +-- raw-body@2.5.1
|   +-- cookie-signature@1.0.6
|   +-- qs@6.11.0
+-- axios@1.6.0
|   +-- follow-redirects@1.15.3   // Known CVE: SSRF via redirect
|   +-- form-data@4.0.0
+-- lodash@4.17.21

Total: 12 direct dependencies, 147 transitive dependencies
Vulnerabilities found: 3 (1 Critical, 1 High, 1 Medium)
Malicious package indicators: 0

Vulnerability Matching

SCA tools cross-reference every dependency against multiple vulnerability databases including the National Vulnerability Database (NVD), GitHub Advisory Database, and vendor-specific advisories. When a new CVE is published, the SCA tool immediately identifies which of your applications are affected, enabling rapid response.

Behavioral Analysis for Malicious Packages

Advanced SCA tools go beyond vulnerability matching to detect indicators of malicious intent. They analyze packages for suspicious behaviors such as:

SBOM: Your Software Bill of Materials

A Software Bill of Materials (SBOM) is a formal, machine-readable inventory of all components in your application, including every direct and transitive dependency, their versions, and their provenance. Think of it as a nutrition label for software — it tells you exactly what ingredients went into your application.

SBOMs are becoming mandatory. The US Executive Order 14028 on Improving the Nation's Cybersecurity requires SBOMs for all software sold to the federal government. The EU Cyber Resilience Act imposes similar requirements for products sold in European markets. Major enterprises are increasingly requiring SBOMs from their software vendors as part of procurement processes.

SBOM Formats

Format Maintained By Strengths
CycloneDX OWASP Security-focused, supports vulnerabilities and services, lightweight JSON/XML
SPDX Linux Foundation ISO standard (ISO/IEC 5962:2021), comprehensive license information, strong legal compliance
CycloneDX SBOM
{
  "bomFormat": "CycloneDX",
  "specVersion": "1.5",
  "components": [
    {
      "type": "library",
      "name": "express",
      "version": "4.18.2",
      "purl": "pkg:npm/express@4.18.2",
      "licenses": [{ "license": { "id": "MIT" } }],
      "hashes": [{
        "alg": "SHA-256",
        "content": "a1b2c3d4e5f6..."
      }]
    },
    {
      "type": "library",
      "name": "lodash",
      "version": "4.17.21",
      "purl": "pkg:npm/lodash@4.17.21"
    }
  ]
}

SBOMs enable rapid incident response during supply chain attacks. When a new malicious package is discovered (like event-stream), organizations with SBOMs can immediately query their inventory to determine which applications are affected, rather than manually searching through hundreds of repositories. This can reduce incident response time from days to minutes.

Building a Supply Chain Defense Strategy

Defending against supply chain attacks requires a multi-layered approach that combines tooling, process, and policy.

1. Lock Your Dependencies

Always commit lock files (package-lock.json, yarn.lock, poetry.lock, Pipfile.lock) to your repository. Lock files pin exact versions and include integrity hashes, ensuring that every build uses precisely the same packages that were tested and approved.

Defense
# Use --frozen-lockfile to fail the build if lock file is outdated
npm ci                      # Uses package-lock.json exactly
yarn install --frozen-lockfile
pip install --require-hashes -r requirements.txt

2. Implement SCA in Your CI/CD Pipeline

Run SCA scanning on every pull request and every build. Configure the scanner to fail builds when critical vulnerabilities or malicious package indicators are detected. This ensures that no compromised dependency can reach production without being explicitly reviewed and approved.

3. Scope Private Packages

Use namespace scoping for all internal packages (@company/package-name) and configure your package manager to only resolve scoped packages from your private registry. This eliminates the dependency confusion attack vector entirely.

4. Audit and Minimize Dependencies

Regularly audit your dependency tree and remove packages that are no longer needed. Each dependency is an attack surface. Consider whether you truly need a package or whether the functionality can be implemented with a few lines of code. The infamous left-pad incident showed how a single 11-line dependency can break thousands of applications.

5. Generate and Maintain SBOMs

Automate SBOM generation as part of your build pipeline. Store SBOMs alongside your build artifacts and update them with every release. When a new vulnerability or malicious package is announced, your SBOMs enable immediate impact assessment across your entire application portfolio.

6. Monitor for New Threats Continuously

Supply chain security is not a one-time check. New vulnerabilities are published daily, and previously safe packages can be compromised at any time (as the event-stream incident demonstrated). Continuous monitoring ensures you are alerted when a package you depend on becomes a risk.

Defense in depth: No single control is sufficient. Combine lock files (preventing unexpected updates), SCA scanning (detecting known vulnerabilities and malicious indicators), SBOM generation (enabling rapid incident response), namespace scoping (preventing dependency confusion), and continuous monitoring (detecting newly disclosed threats). Each layer catches what the others miss.

Secure Your Software Supply Chain

Security Factor 365's SCA engine analyzes your entire dependency tree, detects vulnerable and malicious packages, generates SBOMs, and monitors continuously for new threats across npm, PyPI, Maven, NuGet, and more.

Explore SCA Scanning