Product

Overview

How it works Watch it work All 35 features Screenshots How ForgeRepo™ is secured

Keep bad packages out

Malware scanning Typosquat detection Dependency confusion protection Cooling off new releases Allow lists and block lists

When something goes wrong

Kill switch Who has it Lockdown and degraded modes Vulnerability monitoring

Developers and approvals

Requests and auto approve Check, walk and review Waivers Dry run
Formats

Languages

Private npm registry PyPI proxy and private index Private NuGet feed and proxy Maven repository proxy RubyGems mirror Composer and Packagist proxy

Apple, containers and Linux

CocoaPods CDN mirror Swift package registry Docker registry mirror RPM mirror for dnf and yum APT mirror for Debian and Ubuntu

Use it as

An npm firewall Artifacts, SBOMs and lifecycle More than one node SSO, roles and allow lists
Learn

Guides

Learn secure development Secure coding Secure pipelines Unsafe vs safe Security and development

Supply chain

Supply chain attacks, 2016 to 2026 Software supply chain security
Compare Side by side, all four ForgeRepo™ vs JFrog ForgeRepo™ vs Sonatype ForgeRepo™ vs Cloudsmith
Docs

Start

Getting started Download All documentation Questions

For developers

npm setup pip setup docker login Push a Docker image CI basics

For administrators

First setup Rules and their order Six incidents, walked through Backup and upgrade
Pricing Install it

Ten years of incidents

Software supply chain attacks, 2016 to 2026

Most code you ship was written by someone else, built on machines you do not own and delivered through registries you do not run. Every one of those hands is a way in. Here are 24 incidents from the last ten years where a registry firewall like ForgeRepo™ would have stopped the attack or limited the damage, each with its source, what it cost, what it taught, and which control did the work.

Why the supply chain is the soft spot

An attacker who compromises one popular package, one build server or one maintainer account gets into every organization that trusts it, and they get in through the front door, signed and expected.

You run far more than you wrote

A typical application pulls in hundreds of transitive dependencies. Nobody on the team chose most of them, and one install script in any of them runs with the developer's or the pipeline's rights.

Trust is the payload

A hijacked release arrives under a name you already allow, from a registry you already use, often with a valid signature. Perimeter tools see a normal download. That is why these attacks spread so far.

It moves in hours

Floating version ranges and automated pipelines pull a new release minutes after it is published. Many of the npm hijacks below were found and pulled within hours, after thousands of builds had already run them.

By the numbers

Only figures we could trace to a published source. Each links to it.

454,600+ new malicious open source packages found in 2025 Sonatype, 2026
30% of breaches involved a third party, double the year before Verizon DBIR 2025
2 billion+ weekly downloads hijacked by one phishing email, September 2025 Aikido

Where attacks land in the chain

Six stages from a developer's keyboard to production, and a real incident at each one. A registry firewall sits between stages 3 and 4, so it can only see what comes out of a registry. The rest needs other controls.

The software supply chain, with where attacks have landedSix stages from developer to production. Each stage has a red marker with an attack type and an example incident: Developer: Phished tokens, chalk and debug, 2025; Source repo: Sabotage, colors and faker, 2022; Dependencies: Typosquat, confusion, torchtriton, 2022; Build and CI: Stolen CI secrets, Nx s1ngularity, 2025; Distribution: Poisoned release, Ultralytics, 2024; Production: Vulnerable code, Log4Shell, 2021.STAGE 1Developeraccounts, laptops!Phished tokenschalk and debug, 2025STAGE 2Source repocommits, maintainers!Sabotagecolors and faker, 2022STAGE 3Dependenciespublic registries!Typosquat, confusiontorchtriton, 2022STAGE 4Build and CIrunners, secrets!Stolen CI secretsNx s1ngularity, 2025STAGE 5Distributionregistry, CDN, updates!Poisoned releaseUltralytics, 2024STAGE 6Productionapps, users!Vulnerable codeLog4Shell, 2021
Red markers show where each kind of attack gets in. Every example is in the timeline below.

Nine kinds of attack

Every incident on this page falls under one of these. The count is how many of the 24 timeline entries use it. The rest come through the registry, which is why a registry firewall can see them.

Typosquat2

A malicious package named to be mistaken for a popular one: a swapped letter, a missing dash, an extra word.

Stops it: Allow lists, lookalike name checks, and reviewing every new dependency before it lands.

Dependency confusion2

A public package published under the name of a private one, at a higher version, so a build that looks in both places picks it.

Stops it: Reserved names that are never fetched from outside, and one upstream per scope with no fallback.

Account takeover7

A real maintainer account is phished or its token stolen, and a malicious version of a trusted package is published.

Stops it: Cooling off new versions, lockfiles, a kill switch, and phishing resistant MFA for maintainers.

Malicious maintainer2

Someone gains the trust of a project, or builds a useful package, and then ships harmful code on purpose.

Stops it: Reviewing changes in dependencies, provenance, and watching for new install scripts.

Sabotage and removal3

A maintainer deletes, breaks or weaponizes their own package, as protest or in anger.

Stops it: A cache of every version you use, pinned versions, and a kill switch.

CI/CD compromise3

A pipeline, a CI service or a third party action is abused to steal secrets or publish poisoned releases.

Stops it: Least privilege CI tokens, actions pinned to a full SHA, short lived OIDC credentials, egress control.

Vulnerable dependency1

Not an attack on the supply chain itself: a serious flaw in a widely used component that everyone downstream inherits.

Stops it: An inventory of what runs where, SBOMs, vulnerability monitoring and fast patching.

Domain or CDN takeover1

An expired maintainer domain or a sold CDN is used to take over accounts or serve altered code.

Stops it: Self hosting scripts, subresource integrity, and watching maintainer email domains.

Self spreading worm3

Malware that steals publish tokens and uses them to infect more packages by itself.

Stops it: Cooling off, a kill switch, short lived publish tokens and fast token rotation.

The ten years at a glance

One square per incident on this page, colored by type. Select a square to jump to it. This counts what we chose to include, not every attack that happened, and the later years are busier partly because far more researchers are watching the registries now.

2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
2026
TyposquatDependency confusionAccount takeoverMalicious maintainerSabotage and removalCI/CD compromiseVulnerable dependencyDomain or CDN takeoverSelf spreading worm

Timeline

Major incidents, 2016 to 2026

Newest first. Each entry links to the original disclosure or a news report. Only incidents where a registry firewall such as ForgeRepo™ would have helped are listed: it would have stopped or limited 17 of them, and helped in part with 7. The green or amber box on each says how. Attacks that never pass through a package registry, such as tampered vendor installers or hijacked CI actions, are left out; the secure pipelines guide covers those.

2026

Self spreading worm npm

keyv and cacheable worm

After a maintainer GitHub account was compromised, keyv 6.0.0 carried a worm from the Shai-Hulud family that spread to more than 400 npm packages. It stole cloud credentials, developer secrets, AI tool configuration and cryptocurrency wallets.

Impact
Wiz advised treating affected machines as compromised and rotating every credential on them.
Lesson
A year after the first Shai-Hulud, the same shape of attack still works.
What would have helped
Cooling off new versions Kill switch Short lived tokens and sessions Trusted publishing
A registry firewall would have helped. Cooling off stops each newly infected version, and a kill switch removes the whole list.

Sources: Wiz · Microsoft Security

Account takeover npm

Mastra packages

A maintainer account was taken over and poisoned versions of more than 140 mastra and @mastra packages added easy-day-js, a lookalike of dayjs, whose postinstall script downloaded and ran a second stage. Microsoft attributed it to Sapphire Sleet, a North Korean group.

Impact
Any machine or build that ran npm install on a poisoned version was exposed. How long the versions were live is not confirmed in the sources.
Lesson
Lookalike names now show up as dependencies of real packages, not just as typos.
What would have helped
A registry firewall helps, in part. Cooling off and lookalike checks on easy-day-js help. Timing details are not public enough to say more.

Sources: Microsoft Security · BleepingComputer

Account takeover npm

axios hijack

After the maintainer account was compromised, axios 1.14.1 and 0.30.4 were published with a new dependency, plain-crypto-js, whose postinstall script dropped a remote access trojan for macOS, Windows and Linux and then cleaned up after itself.

Impact
One of the most used HTTP clients in JavaScript. The versions were live for about three hours.
Lesson
A new dependency appearing in a patch release is a warning sign.
What would have helped
A registry firewall would have helped. Cooling off keeps both the new axios versions and the brand new plain-crypto-js out.

Sources: StepSecurity

CI/CD compromise PyPI

LiteLLM on PyPI

Credentials exposed through the Trivy compromise were used to publish LiteLLM 1.82.7 and 1.82.8 to PyPI. They carried a credential stealer that collected environment variables, SSH keys and cloud credentials.

Impact
The versions were live for about 40 minutes before removal.
Lesson
One compromised tool in CI can lead to the next compromised package.
What would have helped
A registry firewall would have helped. Forty minute old versions never reach a build with cooling off on.

Sources: LiteLLM security update

2025

Self spreading worm npm

Shai-Hulud 2.0

A second wave of the worm ran during preinstall, before anything else, and spread to hundreds more packages, stealing secrets and publishing them to public GitHub repositories.

Impact
Packages from well known companies were infected, and tens of thousands of repositories held leaked secrets.
Lesson
Install scripts are the first thing that runs. Treat them as untrusted code.
What would have helped
A registry firewall would have helped. Cooling off and a kill switch stop new infected versions before they are installed.

Sources: Datadog Security Labs

Malicious maintainer npm

postmark-mcp

An unofficial npm package offering an MCP server for the Postmark email service worked normally for many versions, then one update added a hidden BCC that copied every email sent through it to the author.

Impact
Organizations that let AI agents send email through it leaked those emails.
Lesson
A package that behaves for fifteen releases can still turn on the sixteenth.
What would have helped
Reviewing dependency changes Allow lists Cooling off new versions
A registry firewall helps, in part. Allow lists and review of new versions help. The change was one small line that looked like normal code.

Sources: Snyk · The Hacker News

Self spreading worm npm

Shai-Hulud npm worm

A self spreading worm stole npm tokens from the machines that installed an infected package, then used them to publish infected versions of every package those tokens could reach. It also exposed secrets through public GitHub repositories.

Impact
Hundreds of packages were infected, including @ctrl/tinycolor. CISA issued an alert and GitHub announced changes to npm publishing.
Lesson
Long lived publish tokens on developer machines let an attack spread by itself.
What would have helped
Cooling off new versions Kill switch Trusted publishing Short lived tokens and sessions
A registry firewall would have helped. Cooling off stops each new infected version, and a kill switch removes a whole list at once.

Sources: StepSecurity · CISA · GitHub blog

Account takeover npm

chalk, debug and 16 more

A maintainer was phished by an email posing as npm support. The attacker published versions of chalk, debug and other core packages with code that swapped cryptocurrency addresses in browsers.

Impact
The affected packages had more than 2 billion weekly downloads combined. They were removed within hours.
Lesson
The most downloaded packages are one phishing email away from everyone.
What would have helped
Cooling off new versions Kill switch Phishing resistant MFA
A registry firewall would have helped. Hours old versions never reach a build with cooling off on.

Sources: Aikido

CI/CD compromise npm

Nx s1ngularity

An injectable GitHub Actions workflow let attackers steal the Nx npm publish token. Malicious versions ran a postinstall script that used AI command line tools on the machine to hunt for secrets and uploaded them to public GitHub repositories.

Impact
Thousands of secrets were exposed. The versions were live for about four hours.
Lesson
AI tools on a developer machine are now part of the attack surface.
What would have helped
A registry firewall would have helped. Four hour old versions are well inside any cooling off period.

Sources: Nx advisory · Wiz

2024

CI/CD compromise PyPI

Ultralytics

Attackers abused a GitHub Actions workflow in the Ultralytics repository with a crafted branch name, poisoned the build cache, and published PyPI releases carrying a cryptocurrency miner.

Impact
A popular computer vision package shipped miners in several releases.
Lesson
Untrusted input in workflow triggers is code execution in your release pipeline.
What would have helped
A registry firewall would have helped. For teams installing it, cooling off and a kill switch keep a poisoned release out. The root cause is the workflow.

Sources: PyPI blog

2022

Dependency confusion PyPI

PyTorch torchtriton

PyTorch nightly builds depended on torchtriton from their own index, but pip also looked at PyPI, where someone had published a malicious torchtriton. It uploaded SSH keys and other files.

Impact
Anyone who installed PyTorch nightly on Linux with pip over a few days was affected.
Lesson
Extra index URLs are a dependency confusion setting.
What would have helped
A registry firewall would have helped. A reserved name comes from exactly one place, so the public torchtriton is never fetched.

Sources: PyTorch blog

Domain or CDN takeover PyPI, Packagist

ctx and phpass

The maintainer email domain of the ctx package had expired. Someone registered it, reset the PyPI password and published versions that sent environment variables, including AWS keys, to a remote server. A similar change hit phpass.

Impact
Both packages leaked secrets from the machines that installed them.
Lesson
An expired domain can be a password reset link for someone else.
A registry firewall helps, in part. Cooling off catches the sudden new versions. The domain takeover itself is outside a registry.

Sources: Python security · BleepingComputer

Sabotage and removal npm

node-ipc protestware

The node-ipc maintainer added code that overwrote files on machines with Russian or Belarusian IP addresses, then replaced it with a package that dropped a protest message on desktops.

Impact
It reached users through Vue CLI and was tracked as CVE-2022-23812.
Lesson
Protestware is still malware for the people it lands on.
What would have helped
Cooling off new versions Reviewing dependency changes Kill switch
A registry firewall helps, in part. Cooling off and a kill switch help. The dependency arrived through a trusted tool, so review matters too.

Sources: Snyk

Sabotage and removal npm

colors and faker sabotage

The maintainer of colors and faker published versions that printed garbage in an infinite loop, and wiped faker, as a protest against unpaid open source work.

Impact
Thousands of projects, including the AWS CDK, broke. npm reverted the packages.
Lesson
Maintainers are people, and a single person can change a package overnight.
What would have helped
Cooling off new versions Lockfiles and pinned versions Kill switch
A registry firewall would have helped. Pinned versions and cooling off keep a surprise release out until someone has looked at it.

Sources: BleepingComputer

2021

Vulnerable dependency Maven

Log4Shell

A remote code execution flaw in Apache Log4j 2, CVE-2021-44228, let attackers run code by getting a crafted string logged. Log4j sat deep in the dependency trees of a vast amount of Java software.

Impact
The CISA director called it one of the most serious vulnerabilities she had seen. Teams spent weeks finding where Log4j ran.
Lesson
You cannot patch what you cannot find. Inventory is a security control.
A registry firewall helps, in part. No registry can stop a flaw in real code. But ForgeRepo™ flags the vulnerable Log4j versions it holds, and who has it names every application that pulled one, which turns weeks of searching into one query.

Sources: CISA · Apache Log4j security

Account takeover npm

coa and rc hijacks

Two weeks later, coa and rc were hijacked the same way, with malicious versions that installed a password stealer. Builds broke as the new versions appeared, which is how many teams noticed.

Impact
Both packages had millions of weekly downloads through tools like react-scripts.
Lesson
The same playbook works again until the defaults change.
A registry firewall would have helped. Brand new versions of old packages are the textbook case for cooling off.

Sources: BleepingComputer

Account takeover npm

ua-parser-js hijack

The npm account of the ua-parser-js maintainer was taken over and three malicious versions were published. They installed a password stealer and a cryptocurrency miner.

Impact
The package had around 7 million weekly downloads. CISA issued an alert. The versions were removed the same day.
Lesson
Popular packages are the ones worth stealing.
A registry firewall would have helped. Cooling off would have kept the hours old versions out, and a kill switch removes them everywhere at once.

Sources: GitHub advisory · CISA · BleepingComputer

Dependency confusion npm, PyPI, RubyGems

Dependency confusion, Alex Birsan

A researcher published public packages using the names of internal packages he found at large companies, with high version numbers. Their build systems fetched his packages instead of their own.

Impact
He reported code execution inside more than 35 organizations, including Apple, Microsoft and PayPal, through bug bounty programs.
Lesson
Asking two registries in turn and taking the highest version is the vulnerability.
What would have helped
A registry firewall would have helped. Reserved names are never fetched from public registries, which is exactly this attack.

Sources: Alex Birsan · Simon Willison

2019

Account takeover RubyGems

rest-client gem hijack

An attacker took over an old maintainer account on RubyGems and published rest-client versions that sent credentials and URLs to a remote host and could run code fetched from pastebin. It became CVE-2019-15224.

Impact
Several malicious versions were downloaded before they were yanked.
Lesson
Dormant maintainer accounts with old passwords are an open door.
What would have helped
Cooling off new versions Kill switch Phishing resistant MFA
A registry firewall helps, in part. Cooling off would have held the new versions for a few days. Anyone who installed them after that window still needed the kill switch.

Sources: rest-client issue 713

2018

Malicious maintainer npm

event-stream and flatmap-stream

A new volunteer offered to maintain event-stream, was given publish rights, and added a dependency, flatmap-stream, that carried encrypted code aimed at stealing from one bitcoin wallet app.

Impact
The package had millions of weekly downloads. The payload went unnoticed for weeks.
Lesson
Handing over a package hands over everyone who depends on it.
What would have helped
A registry firewall helps, in part. A new dependency appearing in a trusted package is visible at the registry, and a kill switch removes it fast once known. The patient setup is harder to catch.

Sources: npm blog

Account takeover npm

eslint-scope account takeover

An attacker used a maintainer password reused from another breach to publish eslint-scope 3.7.2, which stole npm tokens from the machines that installed it. It was live for about two hours.

Impact
npm revoked every token created before the incident, for all users.
Lesson
Reused passwords on maintainer accounts put everyone downstream at risk.
What would have helped
Cooling off new versions Kill switch Phishing resistant MFA
A registry firewall would have helped. A few days of cooling off would have kept a two hour malicious release away from every build.

Sources: ESLint postmortem

2017

Typosquat PyPI

PyPI typosquats found by SK-CSIRT

The Slovak national CSIRT found malicious PyPI packages with names like urllib, copying standard library modules, that ran code during installation and sent host details to a remote server.

Impact
The packages had been downloaded, and some were included in projects, before removal.
Lesson
Install time code runs with your permissions before you have looked at anything.
A registry firewall would have helped. Unknown lookalike names are exactly what an allow list and name checks catch.

Sources: python-dev mailing list · SK-CSIRT advisory

Typosquat npm

crossenv and about 40 typosquats

A user published crossenv and dozens of similar names that mimicked popular packages like cross-env. On install they sent the environment variables of the machine, often including credentials, to the attacker.

Impact
Anyone who mistyped a name leaked secrets. npm removed the packages and the user.
Lesson
One missing dash is enough. Names need checking, not just versions.
A registry firewall would have helped. An allow list refuses unknown names, and lookalike checks flag crossenv next to cross-env.

Sources: npm blog

2016

Sabotage and removal npm

left-pad unpublished

After a naming dispute, a maintainer unpublished all of his npm packages, including left-pad, an 11 line function. Builds across the JavaScript world broke within minutes, and npm restored the package and changed its unpublish rules.

Impact
Thousands of builds failed at once, including projects like Babel and React that depended on it indirectly.
Lesson
A dependency you do not hold a copy of is a dependency someone else can take away.
What would have helped
Lockfiles and pinned versions Allow lists
A registry firewall would have helped. A cached copy of every version you use keeps builds working when a package disappears upstream.

Sources: npm blog

Patterns across ten years

What keeps happening

The names change every year. The shapes do not.

New versions are the danger

Most registry attacks were live for minutes to hours: eslint-scope, ua-parser-js, chalk, Nx, LiteLLM, axios. A few days of cooling off would have kept every one of them away from builds.

Stolen tokens, not clever exploits

Account takeover, phished maintainers and leaked CI tokens are behind most incidents. Short lived tokens, phishing resistant MFA and trusted publishing remove the prize.

The build system is production

SolarWinds, CCleaner, Webmin, 3CX and xz were all about what happens between source and release. Isolated, audited build systems and provenance close that gap.

Attacks cascade

X_TRADER led to 3CX. reviewdog led to tj-actions. Trivy led to LiteLLM. One compromised tool becomes the route into everything that trusts it.

Install scripts run first

crossenv, Shai-Hulud, axios and Mastra all ran code during install, before anyone looked. Disable scripts where you can and scan what you cannot.

Inventory decides the response

Log4Shell and every worm since came down to one question: where is it? Teams that knew who pulled what answered in minutes. Others took weeks.

What teams should do

No single product covers the whole chain. These are the habits that would have blunted most of the incidents above, roughly in the order they pay off.

  1. Put a registry you control in the path

    Every install goes through one place that can check, cache and refuse. That is what makes cooling off, allow lists, reserved names and a kill switch possible.

  2. Hold new versions for a few days

    Most malicious releases are found within hours. Letting the world test them first costs almost nothing.

  3. Lock and pin everything

    Lockfiles for packages, digests for images, full commit SHAs for CI actions. A tag or a range is a promise someone else can change.

  4. Treat CI as production

    Least privilege tokens, short lived OIDC credentials instead of stored secrets, ephemeral runners and control over what builds can reach. See secure pipelines.

  5. Scan your own code too

    Many incidents started with injectable workflows or leaked secrets in repositories. Run SAST and secret scanning on every pull request, for example with Git Code Review.

  6. Know what runs where

    Keep SBOMs and a record of who downloaded what, so the next Log4Shell is a search, not a project.

  7. Practice the bad day

    Decide in advance who can kill a package, rotate secrets and tell developers what to do. Security and development teams need to have done it together once. See working together.

Where ForgeRepo™ fits: it covers the registry step, what enters from npm, PyPI, Docker Hub and the rest, and who pulled it. See supply chain security for the whole picture. For your own code and the secrets in it, the same author wrote Git Code Review, a free SAST tool.

Put a gate in front of your dependencies

ForgeRepo™ is a free, self hosted registry firewall for npm, PyPI, Docker images and eight more formats. Cooling off, reserved names, a kill switch and a record of who pulled what, in one container.