pnpm 11.7.0 erschien am 15. Juni 2026, vier Tage nach dem 11.6.0-Security-Release, der die .npmrc-Umgebungsvariablen-Expansions-Schwachstelle (GHSA-3qhv-2rgh-x77r) behoben hat. Die 11.7-Linie setzt die 11.6-Geschichte zur Härtung der Supply Chain fort und ergänzt drei Features, die verändern, wie Teams pnpm in containerisierten und reproduzierbaren Build-Umgebungen betreiben: einen --frozen-store-Install-Modus für Read-Only-Dateisysteme, die vollständige Delegation der Abhängigkeitsauflösung an den pacquet-Rust-Port und einen optionalen Batch-Modus für pnpm publish --recursive. Dazu kommt ein echter Lockfile-Security-Fix, der einen Path-Traversal-Edge-Case schließt, den die Retrospektive zum npm-Supply-Chain-Angriff im Juni 2026 als wiederkehrende Bug-Klasse im gesamten Ökosystem markiert hat.
frozenStore: Installs gegen einen Read-Only-Package-Store
Das Top-Feature von 11.7 ist frozenStore (Config-Key) und --frozen-store (CLI-Flag), ein Install-Modus für Umgebungen, in denen der Package-Store auf einem Read-Only-Dateisystem liegt: einem Nix-Store, einem OCI-Image-Layer, einem Read-Only-Bind-Mount oder einem dm-verity-Rootfs. Die index.db des Stores wird mit der immutable=1-URI geöffnet, was die WAL/-shm-Sidecar-Erstellung umgeht, die auf einem Read-Only-Verzeichnis sonst mit EROFS scheitern würde. Jeder Store-Schreibpfad wird unterdrückt: der index.db-Writer, der Projekt-Registry-Write, der Side-Effects-Cache und das chmod, das eine Bin-Datei normalerweise ausführbar macht, wenn sie eine Read/Write-Grenze überschreitet.
Die vorgesehene Kombination ist --offline --frozen-lockfile --frozen-store gegen einen vollständig befüllten Store. Unter dem Global Virtual Store (Standard seit 9.x) leben die Package-Verzeichnisse innerhalb des Stores. Fehlt im Store das Build-Output eines Pakets, dessen Lifecycle-Skripte genehmigt sind (oder das einen pnpm-Patch hat), schlägt pnpm früh mit ERR_PNPM_FROZEN_STORE_NEEDS_BUILD fehl, statt mitten im Build auf einem Read-Only-Write abzustürzen. Fehlt dem Store sein Content-Verzeichnis vollständig, schlägt der Install schnell mit ERR_PNPM_FROZEN_STORE_INCOMPLETE fehl, statt zu versuchen, ihn zu initialisieren.
Zwei harte Einschränkungen sind zu beachten. Die immutable=1-URI benötigt Node.js 22.15.0, 23.11.0 oder 24.0.0 oder neuer; auf älteren Runtimes schlägt --frozen-store mit dem klaren Fehler ERR_PNPM_FROZEN_STORE_UNSUPPORTED_NODE fehl. Und --frozen-store ist inkompatibel mit --force und mit einem konfigurierten pnpr-Server, da beide in den Store schreiben. Bin-Linking toleriert ebenfalls einen Read-Only-Store: Unter dem Global Virtual Store lebt die Bin-Quelle eines Pakets innerhalb des Stores, also würde das chmod, das sie ausführbar macht, abgelehnt. Bei EPERM/EACCES oder bei EROFS auf einem wirklich Read-Only-Dateisystem überspringt pnpm das chmod nun, wenn die Bin-Quelle bereits ausführbar ist und einen normalisierten Shebang hat, und wirft andernfalls weiterhin einen Fehler. Das chmod ist redundant, wenn der Seed seine Bin-Dateien bereits ausführbar ausliefert.
Das Ergebnis ist ein vollständig reproduzierbarer Install, der als einzelnes Artefakt zwischengespeichert und über CI, lokale Entwicklung und Produktion hinweg wiederverwendet werden kann, ohne Schreibzugriff auf den Store. Für Nix- und OCI-Nutzer ist dies das fehlende Puzzleteil: pnpm 11.6 war in diesen Umgebungen bereits nutzbar, aber jeder Install versuchte ein WAL-chmod oder einen shm-Sidecar-Write, der entweder fehlschlug oder auf einen langsameren Code-Pfad zurückfiel. 11.7 macht den Read-Only-Fall zum expliziten, unterstützten Modus.
pacquet löst nun Abhängigkeiten auf, nicht nur die Materialisierung
Das zweite Feature ist ein Meilenstein für den pacquet-Rust-Port von pnpm: Die Abhängigkeitsauflösung reiht sich in die Menge der Operationen ein, die pacquet End-to-End ausführen kann. Das neue Verhalten ist über configDependencies opt-in: Wenn pacquet in configDependencies deklariert ist und die installierte Version mindestens 0.11.7 ist, wird ein normaler Non-Frozen-Install (isolierter nodeLinker, einfaches pnpm install) in einem Durchgang an pacquet delegiert. pacquet liest die Manifeste, schreibt pnpm-lock.yaml und erstellt node_modules. pnpm erkennt die Fähigkeit anhand der installierten pacquet-Version; ältere Releases behalten die Trennung Auflösung-dann-Materialisierung.
pnpm add, pnpm update und pnpm remove lösen weiterhin in pnpm selbst auf, da diese Befehle die Manifeste mutieren müssen, bevor eine Auflösung stattfinden kann. Nach der Manifest-Mutation materialisiert pacquet. Das Lockfile-Format ändert sich nicht. Es bleibt eine optionale Vorschau der Rust-Install-Engine, verfolgt unter #11723. Für Projekte, die pacquet bereits im Frozen-Install-Modus betreiben, ist die Änderung unsichtbar: Auflösung und Materialisierung sind bereits ein einziger pacquet-Aufruf. Für Projekte, die noch auf der Node.js-Install-Engine laufen, ist das Upgrade ein No-Op, solange pacquet nicht in configDependencies steht.
Ablehnung von Path-Traversal- und reservierten Aliassen aus dem Lockfile
Der dritte Kernpunkt ist ein Security-Fix im Lockfile-Verifizierer. Vor 11.7 konnte ein Angreifer, der einen Lockfile-Eintrag kontrollieren konnte (ein bösartiges Postinstall, ein manipuliertes CI-Artefakt, ein vergifteter Resolved-Spec einer Peer-Dependency) einen Abhängigkeits-Alias auf einen Path-Traversal-String (../../../escape) oder einen reservierten Namen (.bin, .pnpm, node_modules) setzen. Unter nodeLinker: hoisted wurde der Alias direkt unter node_modules eingehängt, sodass Package-Dateien außerhalb des Install-Roots geschrieben oder das pnpm-Layout überschrieben werden konnte. Die Exploit-Klasse ist dieselbe, die der Windows-Path-Traversal in esbuild 0.28.1 (GHSA-g7r4-m6w7-qqqr) und der Axios-npm-Supply-Chain-Angriff vom März 2026 jeweils in einem anderen Tool offengelegt haben.
Der 11.7-Fix fügt zwei Schichten hinzu. Der hoisted-Graph-Builder validiert nun jeden Alias am Verzeichnis-Sink (safeJoinModulesDir), was der Validierung entspricht, die pnpm bereits für aus Manifesten stammende Aliasse durchgeführt hat. Und das Lockfile-Verifikations-Gate (verifyLockfileResolutions) führt eine immer aktive, policy-unabhängige Prüfung aus, die jeden Importer- oder Snapshot-Abhängigkeits-Alias zurückweist, der kein gültiger Package-Name ist, und den Install früh abbricht, vor jedem Fetch oder Dateisystem-Zugriff, für jeden Node-Linker gleichzeitig. Die Prüfung ist konservativ: Jeder Alias, der syntaktisch kein gültiger Package-Name ist (kein /, kein .., keine reservierten Segmente), wird mit einem klaren Fehler abgelehnt.
Für ein normales Projekt ist der praktische Effekt, dass ein bestehendes Lockfile weiterhin wie zuvor installiert wird, und ein Lockfile mit einem Path-Traversal- oder reservierten Alias (was ein zuvor präparierter Angriff wäre, kein normaler Eintrag) den Install nun mit einem klaren Fehler abbricht. Der Fix ändert das Lockfile-Format nicht. Es ist die Art von Defense-in-Depth, die bereits der .npmrc-Advisory in 11.6.0 verkörperte: Ein Projekt, das seiner Lockfile-Quelle bereits vertraut, profitiert dennoch vom Verifizierer, da dieser vor Bugs im Lockfile-Generator und vor teilweiser Korruption schützt.
--batch für pnpm publish --recursive
Das vierte Feature ist klein, aber praktisch: pnpm publish --recursive --batch sendet alle ausgewählten Pakete in einer einzigen PUT /-/pnpm/v1/publish-Anfrage an die Registry, statt einer Anfrage pro Paket. Die Ziel-Registry muss den Batch-Publish-Endpoint implementieren (pnpr tut das); Registries, die das nicht tun, werden mit einem klaren ERR_PNPM_BATCH_PUBLISH_UNSUPPORTED-Fehler gemeldet. Der Batch wird von pnpr All-or-Nothing verarbeitet: Scheitert die Validierung eines Pakets im Batch, wird keines der Pakete veröffentlicht. Für Monorepos, die N Pakete pro Release veröffentlichen, ist der Wand-Zeit-Gewinn bei pnpm publish --recursive ungefähr Nx.
Weitere Fixes für den Arbeitsalltag
Das 11.7-Release behebt zudem mehrere Korrektheits- und Regressions-Bugs, die seit 11.6 offen waren. Am auffälligsten ist eine Windows-Regression in pnpm add, die Cannot destructure property 'manifest' of 'manifestsByPath[rootDir]' as it is undefined erzeugte, wenn man außerhalb eines Workspaces lief; die Ursache war, dass selectProjectByDir den ProjectsGraph nach opts.dir statt nach project.rootDir schlüsselte, sodass nachgelagerte manifestsByPath-Lookups fehlschlugen, wenn sich die beiden Pfade unterschiedlich normalisierten (typischerweise die Groß-/Kleinschreibung des Laufwerksbuchstabens). Der Befehl pnpm patch-remove entfernt keine Dateien mehr außerhalb des konfigurierten Patches-Verzeichnisses. pnpm publish respektiert nun strictSsl: false für selbstsignierte Zertifikate auf die gleiche Weise wie pnpm install. Git-Dependencies, die auf ein Unterverzeichnis eines Repositorys zeigen (repo#commit&path:/sub/dir), behalten ihren path im Lockfile wieder, nach einer Integritäts-Pin-Regression in 11.6. Und die Auflösung geteilter Package-Kinder ist nun deterministisch, wenn dasselbe Paket über mehrere Kontexte erreicht wird, was eine Klasse von „missing peer"-Reports (#12358) behebt, bei denen das Request-Timing den Kind-Kontext entschied.
Die interaktiven Prompts von pnpm update -i und pnpm audit --fix -i haben ebenfalls einen UX-Fix erhalten: Die Zusammenfassungszeile nach dem Drücken von Enter druckte zuvor die vollständige Tabellenzeile jeder ausgewählten Auswahl (Label, aktuelle/Ziel-Versionen, Workspace, URL), mit Kommas verbunden, was eine Textwand erzeugte. Die Zusammenfassung listet nun nur die ausgewählten Package-Namen (oder Vulnerability-Keys) auf. Das ist eine Kleinigkeit, aber genau die Art von Polish, die ein 11.7-Minor-Release von einem 11.6-Patch unterscheidet.



