# npm supply-chain attacks and your website

> What an npm supply chain attack is, how recent incidents reached ordinary websites, and the build settings we use to keep a Next.js site safe.

Canonical page: https://kyvrel.com/blog/npm-supply-chain-attacks
Published: 2026-05-20 · Updated: 2026-06-02 · 6 min read · Kyvrel

An npm supply chain attack happens when someone slips malicious code into a package your website installs. You never touch the bad code yourself. Your build pulls it in, and it can steal secrets or run in your visitors' browsers. Most marketing sites install hundreds of packages, so this risk applies to you, even if your site is "just a brochure."

This post explains how these attacks reach ordinary company websites, what the recent incidents had in common, and the concrete settings we apply on every build. It is written for founders and marketing leads, with enough detail for whoever maintains your code.

## What is an npm supply chain attack?

npm is the public registry that almost every JavaScript project depends on. A modern Next.js marketing site lists a few dozen direct dependencies. Each of those has its own dependencies, so the real install tree is often several hundred packages. Your team chose only a handful of them.

An attacker does not need to break into your servers. They need one of three things:

- **A maintainer's account.** Phishing, a reused password or a leaked token lets them publish a new version of a trusted package.
- **A believable fake.** A package with a name one letter off from a popular one, waiting for a typo in an install command.
- **A maintainer who turns.** Rare, but it has happened: an owner deliberately ships harmful code in their own package.

Once the bad version is live, every project that installs it without a lock on the exact version gets the payload. That can be your developer's laptop, your CI pipeline or your production bundle.

## Recent npm incidents and what they had in common

The last few years produced a clear pattern. A few well-documented cases are worth knowing by name.

- **chalk, debug and other core utilities, September 2025.** A maintainer was phished through a fake npm support domain. The attacker published versions that hooked browser APIs to redirect crypto wallet transactions. The packages are installed billions of times a week, though clean versions returned within hours. [Endor Labs wrote up the details](https://endorlabs.com/learn/major-supply-chain-attack-compromises-popular-npm-packages-including-chalk-and-debug).
- **The Shai-Hulud worm, September 2025.** Malicious install scripts searched machines for npm, GitHub and cloud tokens. They then used stolen npm tokens to publish infected versions of other packages. CISA counted more than 500 compromised packages in [its alert](https://www.cisa.gov/news-events/alerts/2025/09/23/widespread-supply-chain-compromise-impacting-npm-ecosystem). A second wave followed in November 2025.
- **Registry changes in response.** GitHub, which runs npm, published [its plan for a more secure npm supply chain](https://github.blog/security/supply-chain-security/our-plan-for-a-more-secure-npm-supply-chain/). It narrows publishing to 2FA, short-lived granular tokens and trusted publishing from CI.

![How a compromised package travels from a phished maintainer account to the registry, a build server and a visitor's browser](https://kyvrel.com/blog/inline/npm-supply-chain-attacks-1.png)

The common thread is simple. Nobody on the victim side made an obvious mistake. They installed a package version that was fine yesterday. The defense therefore has to work even when you trust the package name.

## Why a marketing site is a real target

It is tempting to think attackers only want banks and big SaaS platforms. A marketing site still carries things worth stealing:

- **Build-time secrets.** CMS tokens, analytics keys, form webhooks and deploy credentials often sit in the build environment.
- **Your visitors.** Code that ends up in the client bundle runs on every page view, with access to forms and the page itself.
- **Your reputation.** A defaced page or a browser warning on your domain costs trust that took years to build.

The September 2025 browser payload is a good example. It did nothing on the server. It waited in the shipped JavaScript for a visitor with a crypto wallet. Any site that rebuilt during that window could have served it.

## How we lock down dependencies on every build

None of this needs exotic tooling. It needs a few defaults that are set once and then enforced. These are the settings we use on every Next.js project we ship.

### Commit the lockfile and install from it

The lockfile records the exact version and checksum of every package. CI must install from it and fail if it does not match `package.json`.

```
# pnpm
pnpm install --frozen-lockfile

# npm
npm ci
```

`npm ci` refuses to run when the lockfile and manifest disagree, as the [npm documentation](https://docs.npmjs.com/cli/commands/npm-ci) explains. That stops a quiet upgrade from slipping in during a deploy.

### Block install scripts by default

Most worms in this space run through `preinstall` or `postinstall` scripts. pnpm 10 no longer runs dependency lifecycle scripts unless you allow them. We keep an explicit allowlist for the few packages that need a native build.

```
# pnpm-workspace.yaml
onlyBuiltDependencies:
  - sharp
  - esbuild
```

With npm, the equivalent is a project `.npmrc` with `ignore-scripts=true`, plus a manual build step for anything that truly needs one.

### Wait before installing brand-new versions

Most malicious versions are found and pulled within hours or days. A short delay means you rarely meet them. pnpm added a setting for this in version 10.16, documented in the [pnpm settings reference](https://pnpm.io/settings).

```
# pnpm-workspace.yaml
minimumReleaseAge: 1440   # minutes: skip versions younger than one day
```

When a real security fix lands, we can exclude that one package and upgrade on purpose.

### Keep secrets out of the client bundle

In Next.js, only variables prefixed with `NEXT_PUBLIC_` reach the browser. We audit that list at the start of each project. CMS write tokens, webhook URLs and database strings never get the prefix.

### Fewer dependencies, reviewed upgrades

Every package you do not install is one you never have to trust. We prefer platform features and small first-party helpers over a library for each task. Upgrades arrive as reviewed pull requests, never as a silent change on deploy day.

![Layered defenses around a website build: lockfile, script allowlist, release-age delay, secret scoping and reviewed upgrades](https://kyvrel.com/blog/inline/npm-supply-chain-attacks-2.png)

## What to ask your developer or agency this week

You do not need to read code to check your exposure. Ask these questions and listen for specific answers:

1. Is the lockfile committed, and does CI install with `npm ci` or `pnpm install --frozen-lockfile`?
2. Are dependency install scripts blocked by default, with a short allowlist?
3. Is there a delay before brand-new package versions are accepted?
4. Which environment variables reach the browser, and who reviewed that list?
5. Who gets alerted when a dependency advisory affects the site, and how fast do they act?
6. Are npm and GitHub accounts on the project protected with 2FA, ideally hardware keys?

If the answers are vague, that is your signal. Supply-chain hygiene sits alongside the basics in our [website security checklist](https://kyvrel.com/blog/website-security-checklist), which covers headers, HTTPS and access control.

## How this fits into a project

We treat dependency policy as part of the build, not as a later audit. It is set in the first week, written into the repository and checked in CI. The same applies to the stack choices we described in our guide to the [Next.js marketing website](https://kyvrel.com/blog/nextjs-marketing-website): fewer moving parts make the site easier to keep safe.

Security work also shows up in how we hand over. You receive the repository, the CI configuration and a short note on what each setting does. If another team takes over later, they inherit the guardrails, not just the code. You can see where this sits in our [delivery process](https://kyvrel.com/process).

## Plan a safer build for your site

If your site depends on npm packages, and it almost certainly does, these settings are worth having in place before the next incident. We set them up as standard on every project we build, including [web app development](https://kyvrel.com/products/web-app), where an MVP starts from $25,000 and typically takes 12-16 weeks. Our pricing page answers [how much does a website cost](https://kyvrel.com/pricing) line by line, with every add-on listed.
