---
title: "pnpm 11.7 ajoute `frozenStore` pour les systèmes de fichiers en lecture seule, délègue la résolution à pacquet, et corrige un path-traversal dans le lockfile"
description: "pnpm 11.7.0 (15 juin 2026) apporte quatre changements principaux : un mode d'installation `--frozen-store` pour les stores Nix, les couches OCI et les montages en lecture seule ; la délégation de la résolution des dépendances au port Rust pacquet (pas seulement la matérialisation) ; un flag opt-in `--batch` pour `pnpm publish --recursive` ; et un correctif de sécurité qui rejette les alias de path-traversal et les alias réservés (`.bin`, `.pnpm`, `node_modules`, `../../escape`) dans les dépendances provenant du lockfile."
date: 2026-06-17
image: "/images/heroes/2026-06-17--pnpm-11-7-frozen-store-publish-batch.png"
author: lschvn
tags: ["security", "tooling", "performance"]
tldr:
  - "pnpm 11.7.0 est sorti le 15 juin 2026 avec un nouveau mode d'installation `--frozen-store` qui ouvre l'`index.db` SQLite du store avec l'URI `immutable=1`, supprimant tout chemin d'écriture dans le store pour que `pnpm install` puisse s'exécuter contre un système de fichiers en lecture seule (store Nix, couche OCI, montage read-only). À combiner avec `--offline --frozen-lockfile` pour des installs entièrement reproductibles. Requiert Node.js 22.15+, 23.11+ ou 24+."
  - "Le port Rust pacquet de pnpm gère désormais la résolution des dépendances de bout en bout (pas seulement la matérialisation) sur une install non-frozen, à condition que la version de pacquet installée soit au moins 0.11.7. Les versions plus anciennes conservent la séparation résolution puis matérialisation, et `add`/`update`/`remove` continuent de résoudre dans pnpm (ils doivent d'abord muter les manifestes)."
  - "La release corrige aussi une véritable surface d'attaque du lockfile : un alias de dépendance malicieusement crafté pouvait être joint directement sous un répertoire `node_modules` hoisté, permettant d'écrire des fichiers de packages hors de la racine d'install ou d'écraser le layout pnpm. Le fix ajoute deux couches de validation (dans le constructeur de graphe `hoisted` et dans la porte de vérification du lockfile) qui rejettent les alias réservés comme `.bin`, `.pnpm`, `node_modules`, et tout pattern de path-traversal, quel que soit le node linker."
faq:
  - question: "Quoi de neuf dans pnpm 11.7 ?"
    answer: "pnpm 11.7.0 (15 juin 2026) livre un mode d'install `--frozen-store` pour les systèmes de fichiers en lecture seule (stores Nix, couches OCI, montages read-only), permet au port Rust pacquet de gérer la résolution des dépendances de bout en bout sur une install non-frozen, ajoute un flag opt-in `--batch` pour `pnpm publish --recursive`, et corrige une vulnérabilité de path-traversal dans le lockfile. La release corrige aussi une régression Windows de `pnpm add`, un bug de scope de chemin dans `pnpm patch-remove`, et un cas limite de résolution déterministe des packages enfants partagés."
  - question: "Qu'est-ce que `frozenStore` et quand l'utiliser ?"
    answer: "`frozenStore` (clé de config, aussi exposée comme `--frozen-store` en CLI) est un nouveau mode d'install pour les systèmes de fichiers en lecture seule. L'`index.db` SQLite du store est ouvert avec l'URI [`immutable=1`](https://www.sqlite.org/uri.html), ce qui contourne la création du sidecar WAL/`-shm` qui échouerait sinon avec `EROFS` sur un répertoire en lecture seule. pnpm supprime aussi tout chemin d'écriture dans le store : le writer `index.db`, l'écriture du registre de projet, le cache d'effets de bord, et le `chmod` qui rend un bin exécutable. L'objectif est une install entièrement reproductible où le store est livré comme un artefact précalculé (store Nix, couche d'image OCI, montage read-only) et ne doit pas être muté pendant l'install."
  - question: "Quel est le prérequis Node.js pour `--frozen-store` ?"
    answer: "L'URI `immutable=1` requiert Node.js 22.15.0, 23.11.0 ou 24.0.0 ou ultérieur. Sur des runtimes plus anciens, `--frozen-store` échoue rapidement avec `ERR_PNPM_FROZEN_STORE_UNSUPPORTED_NODE` plutôt que d'ouvrir silencieusement le store d'une manière qui mute le WAL. Les autres flags `--frozen-*` (--offline, --frozen-lockfile) fonctionnent comme sur une install normale."
  - question: "Comment fonctionne la délégation de résolution à pacquet ?"
    answer: "Quand pacquet est déclaré dans `configDependencies` et que la version installée est au moins 0.11.7, une install par défaut (non-frozen, `nodeLinker` isolé) délègue à la fois la résolution et la matérialisation à pacquet en une seule passe : pacquet lit les manifestes, écrit `pnpm-lock.yaml`, et crée `node_modules`. Le format du lockfile reste le même. Les versions plus anciennes de pacquet ne gèrent que la matérialisation, et `pnpm add`, `pnpm update`, et `pnpm remove` continuent de résoudre dans pnpm (ils doivent d'abord muter les manifestes, puis pacquet matérialise). La détection est automatique à partir de la version de pacquet installée."
  - question: "Quelle était la vulnérabilité de path-traversal du lockfile ?"
    answer: "Avant 11.7, un attaquant qui pouvait contrôler une entrée du lockfile (un postinstall malveillant, un artefact CI altéré, un resolved spec de peer dependency empoisonnée) pouvait définir un alias de dépendance comme une chaîne de path-traversal (`../../../escape`) ou un nom réservé (`.bin`, `.pnpm`, `node_modules`). Sous `nodeLinker: hoisted`, l'alias était joint directement sous `node_modules`, permettant d'écrire des fichiers de packages hors de la racine d'install ou d'écraser le layout pnpm. La classe d'exploit est la même que celle que le [path-traversal Windows d'esbuild 0.28.1 (GHSA-g7r4-m6w7-qqqr)](/articles/2026-06-14-esbuild-0-28-1-deno-rce-windows-path-traversal) et [l'attaque de la chaîne d'approvisionnement npm d'Axios de mars 2026](/articles/2026-03-31-axios-npm-supply-chain-attack) ont chacun surfaced dans un outil différent."
  - question: "pnpm 11.7 fonctionne-t-il avec mon lockfile existant ?"
    answer: "Oui. Le format du lockfile est inchangé en 11.7. La nouvelle porte de vérification du lockfile s'exécute contre le lockfile existant à chaque install et rejette toute entrée qui échoue à la vérification d'alias, mais le format lui-même ne change pas. Le seul effet pratique pour un projet existant est qu'une entrée avec un alias de path-traversal ou un alias réservé (ce qui serait une attaque craftée, pas une entrée normale) fait maintenant échouer l'install avec une erreur claire au lieu d'être silencieusement appliquée."
  - question: "Que fait le nouveau flag `--batch` pour `pnpm publish --recursive` ?"
    answer: "Le flag `--batch` est opt-in et envoie tous les packages sélectionnés au registre dans une seule requête `PUT /-/pnpm/v1/publish` au lieu d'une requête par package. Le registre cible doit implémenter l'endpoint de publish en batch (pnpr le fait) ; les registres qui ne le font pas sont signalés avec une erreur claire `ERR_PNPM_BATCH_PUBLISH_UNSUPPORTED`. Le batch est traité en tout-ou-rien par pnpr : si un package du batch échoue à la validation, aucun des packages n'est publié."
  - question: "pnpm 11.7 est-il un breaking change pour un projet normal ?"
    answer: "Non. Le format du lockfile est inchangé, la nouvelle validation d'alias s'exécute contre les lockfiles existants (et ne rejette que les entrées qui n'auraient jamais dû s'y trouver), `frozenStore` est opt-in, la délégation de résolution à pacquet est opt-in (elle ne s'active que quand pacquet est déclaré dans `configDependencies`), et le flag `--batch` est opt-in. Le seul breaking change dans 11.7 est un fix de la régression Windows de `pnpm add` introduite en 11.6.0 (`Cannot destructure property 'manifest'`) qui était déjà corrigée en 11.7."
---

[pnpm 11.7.0](https://github.com/pnpm/pnpm/releases/tag/v11.7.0) est sorti le 15 juin 2026, quatre jours après [le release de sécurité 11.6.0 qui a corrigé la vulnérabilité d'expansion de variables d'environnement dans `.npmrc` (GHSA-3qhv-2rgh-x77r)](https://github.com/pnpm/pnpm/security/advisories/GHSA-3qhv-2rgh-x77r). La ligne 11.7 reprend là où 11.6 s'était arrêtée sur le sujet du durcissement de la chaîne d'approvisionnement et ajoute trois features qui changent la façon dont les équipes utilisent pnpm dans des environnements conteneurisés et de builds reproductibles : un mode d'install `--frozen-store` pour les systèmes de fichiers en lecture seule, la délégation complète de la résolution des dépendances au port Rust pacquet, et un mode batch opt-in pour `pnpm publish --recursive`. Il y a aussi un vrai fix de sécurité dans le lockfile qui ferme un cas limite de path-traversal que [la rétrospective de l'attaque de la chaîne d'approvisionnement npm de juin 2026](/articles/2026-06-06-npm-supply-chain-attack-red-hat-mini-shai-hulud) avait identifié comme une classe récurrente de bug dans tout l'écosystème.

## `frozenStore` : installs contre un store en lecture seule

La feature phare de 11.7 est `frozenStore` (clé de config) et `--frozen-store` (flag CLI), un mode d'install pour les environnements où le store de packages est sur un système de fichiers en lecture seule : un store Nix, une couche d'image OCI, un montage read-only, ou un rootfs `dm-verity`. L'`index.db` SQLite du store est ouvert avec l'[URI `immutable=1`](https://www.sqlite.org/uri.html), ce qui contourne la création du sidecar WAL/`-shm` qui échouerait sinon avec `EROFS` sur un répertoire en lecture seule. Tout chemin d'écriture dans le store est supprimé : le writer `index.db`, l'écriture du registre de projet, le cache d'effets de bord, et le `chmod` qui rend normalement un bin exécutable quand il franchit une frontière read/write.

Le pairing prévu est `--offline --frozen-lockfile --frozen-store` contre un store entièrement peuplé. Sous le global virtual store (le défaut depuis la 9.x), les répertoires de packages vivent à l'intérieur du store, donc si le store manque le build output d'un package dont les scripts de cycle de vie sont approuvés (ou qui a un patch pnpm appliqué), pnpm échoue en amont avec `ERR_PNPM_FROZEN_STORE_NEEDS_BUILD` plutôt que de crasher en milieu de build sur une écriture read-only. Si le store manque son répertoire de contenu entièrement, l'install échoue rapidement avec `ERR_PNPM_FROZEN_STORE_INCOMPLETE` plutôt que de tenter de l'initialiser.

Il y a deux contraintes dures à connaître. L'URI `immutable=1` requiert Node.js 22.15.0, 23.11.0, ou 24.0.0 ou ultérieur ; sur des runtimes plus anciens, `--frozen-store` échoue avec une erreur claire `ERR_PNPM_FROZEN_STORE_UNSUPPORTED_NODE`. Et `--frozen-store` est incompatible avec `--force` et avec un serveur pnpr configuré, puisque les deux écrivent dans le store. Le bin-linking tolère aussi un store read-only : sous le global virtual store, la source d'un bin d'un package vit dans le store, donc le `chmod` qui le rend exécutable serait refusé. Avec `EPERM`/`EACCES` ou avec `EROFS` sur un système de fichiers vraiment read-only, pnpm saute maintenant le `chmod` quand la source du bin est déjà exécutable et a un shebang normalisé, et sinon erreur toujours. Le `chmod` est redondant quand le seed livre déjà ses bins exécutables.

Le résultat est une install entièrement reproductible qui peut être cachée comme un artefact unique et réutilisée à travers la CI, le dev local et la production sans aucun accès en écriture au store. Pour les utilisateurs de Nix et OCI, c'est la pièce manquante : pnpm 11.6 était déjà utilisable dans ces environnements, mais chaque install tentait un `chmod` WAL ou une écriture `shm` sidecar qui soit échouait, soit tombait en fallback sur un chemin de code plus lent. 11.7 fait du cas read-only le mode explicite et supporté.

## pacquet résout désormais les dépendances, pas seulement la matérialisation

La deuxième feature est un jalon pour le [port Rust pacquet de pnpm](https://github.com/pnpm/pnpm/tree/main/pacquet) : la résolution des dépendances rejoint la matérialisation dans l'ensemble des opérations que pacquet peut faire de bout en bout. Le nouveau comportement est opt-in via `configDependencies` : quand pacquet est déclaré dans `configDependencies` et que la version installée est au moins 0.11.7, une install par défaut non-frozen (`nodeLinker` isolé, `pnpm install` simple) est déléguée à pacquet en une seule passe. pacquet lit les manifestes, écrit `pnpm-lock.yaml`, et crée `node_modules`. pnpm détecte la capacité à partir de la version de pacquet installée ; les versions plus anciennes conservent la séparation résolution puis matérialisation.

`pnpm add`, `pnpm update` et `pnpm remove` continuent de résoudre dans pnpm lui-même, parce que ces commandes doivent muter les manifestes avant toute résolution. Après la mutation du manifeste, pacquet matérialise. Le format du lockfile ne change pas. Cela reste un opt-in preview du moteur d'install Rust, suivi sous [#11723](https://github.com/pnpm/pnpm/issues/11723). Pour les projets qui font déjà tourner pacquet en mode frozen-install, le changement est invisible : la résolution et la matérialisation sont déjà une seule invocation pacquet. Pour les projets qui tournent encore sur le moteur d'install Node.js, la mise à jour est un no-op tant que pacquet n'est pas dans `configDependencies`.

## Rejet des alias de path-traversal et des alias réservés du lockfile

Le troisième point principal est un fix de sécurité dans le vérificateur de lockfile. Avant 11.7, un attaquant qui pouvait contrôler une entrée du lockfile (un postinstall malveillant, un artefact CI altéré, un resolved spec de peer dependency empoisonnée) pouvait définir un alias de dépendance comme une chaîne de path-traversal (`../../../escape`) ou un nom réservé (`.bin`, `.pnpm`, `node_modules`). Sous `nodeLinker: hoisted`, l'alias était joint directement sous `node_modules`, permettant d'écrire des fichiers de packages hors de la racine d'install ou d'écraser le layout pnpm. La classe d'exploit est la même que celle que le [path-traversal Windows d'esbuild 0.28.1 (GHSA-g7r4-m6w7-qqqr)](/articles/2026-06-14-esbuild-0-28-1-deno-rce-windows-path-traversal) et [l'attaque de la chaîne d'approvisionnement npm d'Axios de mars 2026](/articles/2026-03-31-axios-npm-supply-chain-attack) ont chacun surfaced dans un outil différent.

Le fix 11.7 ajoute deux couches. Le constructeur de graphe `hoisted` valide maintenant chaque alias au sink du répertoire (`safeJoinModulesDir`), ce qui correspond à la validation que pnpm effectuait déjà pour les alias provenant des manifestes. Et la porte de vérification du lockfile (`verifyLockfileResolutions`) exécute une vérification toujours active, indépendante de toute policy, qui rejette tout alias de dépendance d'importer ou de snapshot qui n'est pas un nom de package valide, faisant échouer l'install tôt, avant tout fetch ou travail sur le système de fichiers, pour chaque node linker en même temps. La vérification est conservatrice : tout alias qui n'est pas syntaxiquement un nom de package valide (pas de `/`, pas de `..`, pas de segments réservés) est rejeté avec une erreur claire.

Pour un projet normal, l'effet pratique est qu'un lockfile existant continue de s'installer comme avant, et un lockfile avec un alias de path-traversal ou un alias réservé (ce qui serait une attaque craftée, pas une entrée normale) fait maintenant échouer l'install avec une erreur claire. Le fix ne change pas le format du lockfile. C'est le genre de défense en profondeur qu'incarnait déjà l'advisory `.npmrc` de 11.6.0 : un projet qui fait déjà confiance à la source de son lockfile bénéficie quand même du vérificateur, parce que le vérificateur protège contre les bugs du générateur de lockfile et contre la corruption partielle.

## `--batch` pour `pnpm publish --recursive`

La quatrième feature est petite mais pratique : `pnpm publish --recursive --batch` envoie tous les packages sélectionnés au registre dans une seule requête `PUT /-/pnpm/v1/publish` au lieu d'une requête par package. Le registre cible doit implémenter l'endpoint de publish en batch (pnpr le fait) ; les registres qui ne le font pas sont signalés avec une erreur claire `ERR_PNPM_BATCH_PUBLISH_UNSUPPORTED`. Le batch est traité en tout-ou-rien par pnpr : si un package du batch échoue à la validation, aucun des packages n'est publié. Pour les monorepos qui publient N packages par release, le gain en temps réel sur `pnpm publish --recursive` est grossièrement Nx.

## Autres fixes qui affectent le travail au quotidien

La release 11.7 corrige aussi plusieurs bugs de correction et de régression ouverts depuis 11.6. Le plus notable est une régression Windows dans `pnpm add` qui produisait `Cannot destructure property 'manifest' of 'manifestsByPath[rootDir]' as it is undefined` quand on tournait hors d'un workspace ; la cause racine était `selectProjectByDir` qui keyait le `ProjectsGraph` par `opts.dir` au lieu de `project.rootDir`, donc les lookups `manifestsByPath` en aval manquaient quand les deux chemins se normalisaient différemment (typiquement la casse de la lettre de lecteur). La commande `pnpm patch-remove` ne supprime plus les fichiers hors du répertoire de patches configuré. `pnpm publish` respecte maintenant `strictSsl: false` pour les certificats auto-signés de la même manière que `pnpm install`. Les dépendances Git qui pointent vers un sous-répertoire d'un repository (`repo#commit&path:/sub/dir`) conservent leur `path` dans le lockfile à nouveau après une régression de pin d'intégrité en 11.6. Et la résolution des packages enfants partagés est désormais déterministe quand le même package est atteint par plusieurs contextes, corrigeant une classe de rapports « missing peer » ([#12358](https://github.com/pnpm/pnpm/issues/12358)) où le timing de la requête déterminait le contexte enfant.

Les prompts interactifs de `pnpm update -i` et `pnpm audit --fix -i` ont aussi reçu un fix UX : la ligne de résumé après avoir pressé Enter imprimait auparavant la ligne complète du tableau pour chaque choix sélectionné (label, versions courante/cible, workspace, URL) jointes par des virgules, produisant un mur de texte. Le résumé liste maintenant uniquement les noms de packages sélectionnés (ou les clés de vulnérabilité). C'est un petit détail, mais c'est le genre de polish qui sépare un minor release 11.7 d'un patch 11.6.
