From a7e13d08fa74601734e0a67901e4dd45ba02baa8 Mon Sep 17 00:00:00 2001 From: Thilo Maier Date: Sun, 2 Aug 2026 23:03:50 +0200 Subject: [PATCH] Update PNPM post Fixes #1278 --- .../+page.md | 59 ++++++++++--------- 1 file changed, 32 insertions(+), 27 deletions(-) diff --git a/src/routes/posts/(2026)/why-pnpm-broke-my-website-to-prevent-credential-leakage/+page.md b/src/routes/posts/(2026)/why-pnpm-broke-my-website-to-prevent-credential-leakage/+page.md index 78aa67ef..923d9534 100644 --- a/src/routes/posts/(2026)/why-pnpm-broke-my-website-to-prevent-credential-leakage/+page.md +++ b/src/routes/posts/(2026)/why-pnpm-broke-my-website-to-prevent-credential-leakage/+page.md @@ -10,10 +10,9 @@ tags: - pnpm --- -Last week, I wanted to make a quick update to this website only to find out that my deployment -pipeline was broken. After some troubleshooting, it turned out that PNPM v11.5.3 introduced a -breaking change affecting anyone with a custom registry string with `_authToken` in their `.npmrc` -file. +Last week, I wanted to make a quick update to this website but found that my deployment pipeline was +broken. After some troubleshooting, it turned out that PNPM v11.5.3 introduced a breaking change +affecting anyone with a custom registry string containing `_authToken` in their `.npmrc` file. ## The issue with .npmrc files @@ -25,38 +24,38 @@ My repository `.npmrc` file looked like this: When running `pnpm install`, PNPM would replace `${NODE_AUTH_TOKEN}` with the value of the `NODE_AUTH_TOKEN` environment variable (referred to as environment variable expansion). This has -been the standard approach to authenticate to private NPM registries since basically forever. +been the standard approach to authenticating with private NPM registries for basically forever. -In response to a number of NPM package supply chain attacks in recent weeks, the PNPM team has -stepped up their game with v11 to +In response to a number of NPM package supply-chain attacks in recent weeks, the PNPM team has +stepped up its game with v11 to [mitigate common supply chain attacks](https://pnpm.io/supply-chain-security). As part of these efforts, PNPM explained in a recent post [why PNPM no longer expands environment variables in a repository's `.npmrc`](https://pnpm.io/blog/2026/06/11/env-variables-in-repository-npmrc). -Their post explains how an attacker could exfiltrate my `NODE_AUTH_TOKEN` when I clone a repository -under their control that contains an `.npmrc`, which triggers PNPM to read my `NODE_AUTH_TOKEN` and -send it straight to the attacker. +Their post explains how an attacker could exfiltrate my `NODE_AUTH_TOKEN` if I cloned a repository +under the attacker's control that contained an `.npmrc`, causing PNPM to read my `NODE_AUTH_TOKEN` +and send it straight to the attacker. That's why PNPM v11.5.3 pulled the emergency brake on environment variable expansion in repository `.npmrc` files. The consequence was a breaking change that broke package installations from private -registries locally and in cloud deploy pipelines. +registries both locally and in cloud deployment pipelines. ## Alternative authentication -So, how can I authenticate with PNPM to a private NPM registry? Let's focus on local development -first. As a first step, I removed my project `.npmrc` and the global `~/.npmrc`. That's technically -not necessary since the [PNPM authentication settings page](https://pnpm.io/npmrc) lists options -that include using `.npmrc` files. But they are meant as fallbacks to make the transition to the new -way of authenticating with private registries easier. +So, how can I authenticate PNPM with a private NPM registry? Let's focus on local development first. +As a first step, I removed my project `.npmrc` and the global `~/.npmrc`. That's technically not +necessary since the [PNPM authentication settings page](https://pnpm.io/npmrc) lists options that +include using `.npmrc` files. However, those options are meant as fallbacks to make the transition +to the new way of authenticating with private registries easier. -I decided to go the PNPM route for local development. Therefore, I added my private registry to a -workspace `pnpm-workspace.yaml` file: +I decided to use PNPM's approach for local development. Therefore, I added my private registry to +the workspace's `pnpm-workspace.yaml` file: ```yaml registries: '@maiertech': https://npm.pkg.github.com/ ``` -It basically instructs PNPM to install packages scoped to `@maiertech` from the GitHub registry. But +This instructs PNPM to install packages scoped to `@maiertech` from the GitHub registry. However, there is no authentication configuration in this file. Instead, the authentication token goes into my global PNPM config (`~/Library/Preferences/pnpm/auth.ini`) using this command: @@ -64,11 +63,18 @@ my global PNPM config (`~/Library/Preferences/pnpm/auth.ini`) using this command pnpm config set //npm.pkg.github.com/:_authToken "TOKEN" ``` +If you already have a local environment variable named `GITHUB_TOKEN`, you can use it in `auth.ini` +like this: + +```bash +//npm.pkg.github.com/:_authToken=${GITHUB_TOKEN} +``` + Note that PNPM always combines the repository config in `pnpm-workspace.yaml` with the global config. When you run `pnpm config list` from within your repository workspace, you will see the combined config. -Now you can install packages from the private NPM registry locally. But how do you authenticate to +Now you can install packages from the private NPM registry locally. But how do you authenticate with the package registry when you deploy your website? The short answer is: you need to make sure that the authentication config is in place before installing packages. There are different ways to achieve this. @@ -87,16 +93,15 @@ In a GitHub Action, you can tweak the action that configures Node: This results in a global `.npmrc` file with `_authToken=${NODE_AUTH_TOKEN}`. In this case, PNPM will expand the `NODE_AUTH_TOKEN` environment variable because the `.npmrc` file is not part of your -repository. All you have to do in your GitHub Action is set environment variable `NODE_AUTH_TOKEN` -to `secrets.GITHUB_TOKEN`. If you use a registry other than GitHub, set `NODE_AUTH_TOKEN` to that -registry's token. +repository. All you have to do in your GitHub Action is set the `NODE_AUTH_TOKEN` environment +variable to `secrets.GITHUB_TOKEN`. If you use a registry other than GitHub, set `NODE_AUTH_TOKEN` +to that registry's token. ## Example: Deployment to Railway I deploy my website to [Railway](https://railway.com/) using [Railpack](https://railpack.com/), which handles the Docker image build and deployment. Luckily, Railpack allows me to add a -`railpack.json` to my project with which I can add the `pnpm config set` command to the `install` -step: +`railpack.json` to my project that lets me add the `pnpm config set` command to the `install` step: ```json { @@ -109,8 +114,8 @@ step: } ``` -All I had to do to make this work was configure a `GITHUB_TOKEN` environment variable with -`read:packages` permission (classic token) in my Railway project. +All I had to do to make this work was configure a `GITHUB_TOKEN` environment variable containing a +classic token with `read:packages` permission in my Railway project. Both examples give you an idea of what you need to do in your specific situation: figure out the best way to put the authentication configuration in place before running `pnpm install`.