pnpm gegen den Wahnsinn
Vierzehn Tage Cooldown, vier Zeilen Config — und warum die Default-Plattform acht Monate gebraucht hat, um auf einen Supply-Chain zu reagieren.
Anfang Juni 2026 hat es Red Hat erwischt. 32 Pakete im @redhat-cloud-services-Namespace, 96 Versionen, kompromittiert mit einem credential-stealing Wurm namens „Miasma”.1 Der Angreifer hatte einen Red Hat Mitarbeiter-Account auf GitHub übernommen, orphan commits gepusht, GitHub Actions getriggert — und über diesen Pfad echte OIDC-Tokens geholt. Die malicious Pakete kamen mit valider SLSA-Provenance raus. Das Build-Zertifikat war korrekt. Der Build war wirklich aus dieser Pipeline. Nur eben mit Backdoor.
Das war nicht der erste 2026er Schlag. März: Axios kompromittiert — 100 Millionen Downloads pro Woche, eine phantom dependency plain-crypto-js, Remote Access Trojan beim npm install.2 April: „Shai-Hulud: The Third Coming”. Mai: 84 malicious Versionen über 42 @tanstack/*-Pakete via GitHub-Actions-Cache-Poisoning.3 Drei Tage später node-ipc (10 Millionen Downloads/Woche) mit 80 KB obfuscated credential-stealer.4
Da war für mich Schluss.
Meine erste Reaktion war Schnauze voll. Ich war seit Wochen dabei, karameht.com umzubauen — und habe in diesem Moment Astro rausgerissen. Alles auf vanilla zurück: PHP, HTML, CSS, JavaScript. Keine Builds. Keine Lockfiles. Keine 683 transitiven Pakete, die ich nie gesehen habe.
Dann den Rechner aufgeräumt. Arch Linux, einmal Altlasten weg:
sudo pacman -Rns nodejs npm yarn pnpm
rm -rf ~/.npm ~/.npm-global ~/.pnpm-store ~/.yarn ~/.config/yarn ~/.cache/node-gyp ~/.node-gyp ~/.nvm
sudo pacman -Rns $(pacman -Qtdq)
paccache -rk0
Erst das System bereinigen, dann nachdenken. Durchgeatmet. Gewartet. Recherchiert.
Und mitten in der Recherche kam die Erinnerung: September 2025 — derselbe Monat, in dem ich mein eigenes npm-Projekt eingestellt habe. @karameht/a11y-lens. Beta, framework-agnostisches Accessibility-Overlay auf Basis von axe-core. Öffentlich auf „nicht weiter gepflegt” gestellt, weil ich es allein nicht in der Tiefe halten konnte, in der so ein Tool gepflegt gehört. Es läuft heute noch in fremden Installs. Und ich habe klar kommuniziert, dass ich die Sicherheit nicht gewährleisten kann und es deswegen eingestellt habe.
Aktuell laut npm-stats über 1.000 Downloads … unfassbar, unachtsam von allen Devs, die es nutzen — und einfach so ein Repo auf npm löschen? Good luck! npm hat seit dem left-pad-Vorfall 2016 eine Unpublish-Policy: nach 24 Stunden gesperrt, sobald irgendein anderes Paket dich als Dependency führt.5 Bleibt: npm deprecate als Warning beim Install — habe ich gesetzt. Plus auf GitHub (ein eigener Post wert) das archivierte Repo gelöscht, um klar zu signalisieren: hier ist Schluss. Heute, Stand 06.06.2026, immer noch weitere Downloads. Ich bin offiziell gefangen in meinem eigenen npm-Eintrag.
Im selben Monat hieß der erste Wurm draußen Shai-Hulud6 — selbstreplizierend, Token-klauend, in öffentliche Repos leakend. Im November 2025 kam Variante 2.0 mit über 25.000 kompromittierten Repos.7 Inzwischen ist das System bei Variante vier oder fünf. Microsoft, Datadog, Elastic schreiben Detection-Updates wie andere Leute Brotrezepte.
Aber außerhalb von uns, die direkt getroffen wurden? Hat keinen interessiert. npm install läuft weiter wie immer. Blind und unbedacht — weitermachen. Das sind nicht nur Devs, auch ganze Firmen haben keine Security-Policy (aber dazu auch mal anders, ein eigenes Kapitel hier aufschlagen).
Damals, in der Modem-Generation, haben wir CDs aus der Computer-BILD vorm Brennen durchgescannt und wussten: das ist fertig. Heute halbgare Scheiße — und viele installieren 683 Pakete blind8 und tippen Enter, bevor der Kaffee durchgelaufen ist. Das ist kein Fortschritt. Das ist nur schnelleres Vertrauen in Fremde. Good luck an alle!
Das ist nicht neu
Wer jetzt „Einzelfall” sagt, hat die letzten acht Jahre verpennt.
2018 — event-stream.9 2 Mio Downloads/Woche. Der Maintainer übergab die Publishing-Rechte an einen Fremden, der „Hilfe” angeboten hatte. Der baute eine verschlüsselte Payload ein, die auf Bitcoin-Wallets über 100 BTC zielte.
2021 — ua-parser-js.10 8 Mio Downloads/Woche, im Einsatz bei Facebook, Microsoft, Amazon, Google. Vier Stunden lang Krypto-Miner plus Passwort-Stealer live.
2022 — colors.js / faker.js.11 Hier hat der Maintainer selbst sabotiert. Endlosschleife mit Gibberish, aus Protest: „No more free work for Fortune 500.” Burnout als Angriffsvektor.
2022 — node-ipc.12 Transitive Dependency von Vue CLI. Bei russischen und belarussischen IPs überschrieb sie rekursiv Dateien mit einem Herz-Emoji. Protestware.
2024 — xz-utils (nicht npm, aber Paradigm-Shift).13 CVE-2024-3094, CVSS 10.0 — die Höchstpunktzahl. „Jia Tan” arbeitete sich über drei Jahre als Co-Maintainer in xz/liblzma rein. Versteckte Schadcode in Test-Fixtures, RCE im sshd über systemd. Aufgeflogen ist das nicht durch Audit, sondern durch 500-Millisekunden-Latenz beim SSH-Login, die ein Microsoft-Entwickler merkwürdig fand. npm ist gegenüber sowas nicht besser geschützt — npm ist nur kürzer im Visier.
8. September 2025 — chalk/debug.14 Phishing-Mail von npmjs.help, Maintainer (qix-) gab seinen 2FA-Code raus. 18 Pakete kompromittiert, darunter debug (357 Mio/Woche) und ansi-styles (370 Mio). Zusammen 2,6 Milliarden Downloads pro Woche. Payload fing Web3-Wallet-Transaktionen ab, schrieb Empfänger-Adressen um. Zwei Stunden online.
Acht Jahre. Defaults unverändert. Und was trägt dazu bei, dass es so bleibt? Devs, die nie eine package.json aufgemacht haben, publishen SaaS-Produkte mit 400 Dependencies — generiert in zehn Minuten via Cursor, Bolt.new, Lovable. Deployed ohne Audit. Verkauft als „production-ready”. Kein Plan, aber rausballern und für bare Münze verkaufen — erzählen, wie toll es funktioniert, ohne Tiefe.
Diejenigen, die mit KI und ohne tiefes Wissen die Open-Source-Welt nachträglich verändern. Warum? KI-generierte Bugs, Maintainer werden überrollt mit tausenden Änderungen und stehen vor einer unlösbaren Aufgabe, alles zu reviewen. Was ist, wenn etwas durchrutscht? Was ist, wenn man etwas übersieht? Was ist, wenn man vor lauter KI-Slop nicht dazu kommt, sein eigenes Projekt zu maintainen?
Ich warte hier und wette mit euch, dass in den nächsten Jahren so viele Systeme gehackt und zerstört werden — und damit auch Existenzen von Menschen. Das darf man nicht unterschätzen — Data-Leaks und Security können richtig teuer werden. Na, wer wettet mit mir mit, dass es noch richtig knallen wird? Aber zurück zu npm!
Warum npm überhaupt so kaputtbar ist
Es liegt nicht an bösen Maintainern. Es liegt an den Defaults.
Rund 2,2 % aller npm-Pakete deklarieren ein postinstall-Script.15 Klingt nach wenig. Aber bei einer durchschnittlichen transitiven Tiefe von 4,39 trifft das praktisch jedes Projekt. Der Median an transitiven Dependencies pro JS-Projekt liegt bei 683 — ein Amplifikationsfaktor von gut 4,3×.8 Was du direkt installierst, vervierfacht sich im Hintergrund.
Und der Default macht daraus eine offene Tür:
postinstallläuft, sobald dunpm installtippst. Kein Opt-in.- Keine Wartezeit für neue Versionen. Frisch published = sofort in deinem Build.
^1.2.3als Save-Prefix heißt: automatisches Minor-Update beim nächsten Install.npm auditist ein Aufruf, kein Gate. Es warnt. Es stoppt nichts. Stell dir vor, dein Linux-Paketmanager wäre so. Wäre er nicht. Das default-npm-Verhalten würde in keinem anderen Kontext der Branche durchkommen. Defaults sind Politik — sie entscheiden, wer geschützt wird und wer Geld macht.
Was REFACTOR macht — vier Zeilen, vier Entscheidungen
# pnpm-workspace.yaml
saveExact: true
minimumReleaseAge: 20160
strictDepBuilds: true
allowBuilds:
esbuild: false
sharp: true
saveExact: true16 — Kein ^, kein ~. pnpm add astro schreibt "astro": "6.3.5", nicht "^6.3.5". Lockfile plus Pinning ergibt einen reproduzierbaren Build. Updates passieren, wenn ich es will — nicht, wenn npm es will.
minimumReleaseAge: 2016017 — 20160 Minuten sind 14 Tage Cooldown. Wird ein Maintainer-Account heute kompromittiert, sehe ich die malicious Version frühestens in zwei Wochen. Meistens ist sie bis dahin längst aus der Registry geflogen. pnpm hatte das ab v10.16 (24h-Default), seit pnpm 11.0 ist es default-on. npm zog erst in 11.10.0 nach18 — acht Monate später.
strictDepBuilds: true19 — Will eine Dependency ein Build-Script ausführen, das nicht explizit erlaubt ist, bricht pnpm install ab. Es warnt nicht. Es stoppt. Genau andersrum als npm audit.
allowBuilds20 — Die Allowlist. sharp braucht einen nativen Binary-Build, also erlaubt. esbuild nicht, also blockiert. Jede neue Dependency mit postinstall muss ich aktiv reinholen. Keine automatischen Skripte aus 683 transitiven Quellen, die ich nie gesehen habe. (Ersetzt das ab pnpm 10.26 deprecated onlyBuiltDependencies.)
Und wenn ich bei einer Dep unsicher bin? Frage ich die KI. Zwei. Drei. Dann ein paar Foren. Dann YouTube. Dann lese ich die package.json der Lib selber und schaue, was das postinstall-Script eigentlich tut. Erst dann entscheide ich.
Die KI ist dabei kein Orakel — sie ist Sparringspartner. Du fütterst sie mit der Release-Note, mit dem Source der Lib, mit deinem pnpm-workspace.yaml. Du fragst, was das Script macht, warum die Lib es braucht, ob es Alternativen gibt. Wenn drei KIs konsistent antworten und sich mit dem Forum-Stand decken, hast du Vertrauen mit Basis. Wenn die Antworten auseinanderlaufen, hast du das Bauchgefühl, das du brauchst: hier wird’s haarig.
Vertrauen ist nicht Glauben. Vertrauen ist verifiziertes Wissen.
Was npm dabei lernt — oder nicht
npm 11.10.0 brachte im Mai 2026 ein min-release-age in der .npmrc (Wert in Tagen statt Minuten). Acht Monate nach pnpm. Und mit Haken: keine Exclude-Liste, der Cooldown gilt pauschal — wenn du einen Security-Hotfix sofort brauchst, Pech. Ältere npm-Versionen ignorieren das Setting stillschweigend, was im CI mit altem CLI ein falsches Sicherheitsgefühl erzeugt.
Acht Monate. Das ist die Lichtgeschwindigkeit, mit der die Default-Plattform auf einen selbstreplizierenden Wurm reagiert. Und wir konsumieren wie Süchtlinge — statt uns zu hinterfragen, könnten wir das Package in 50 Zeilen Code wartbar selber schreiben (auch hierzu kommen wir in weiteren Beiträgen).
Was hier (noch) nicht passiert ist
Damit das ein Devlog bleibt und kein Hochglanz-Prospekt:
pnpm.overridesfür Notfall-Patches transitiver Vulns — brauche ich aktuell nicht, kommt sobald nötig.- CI-Gate
pnpm audit --audit-level=high --prod— steht auf der Liste, noch nicht im Forgejo-Workflow. - Sigstore / npm provenance — wäre der nächste Schritt. Astro hat es noch nicht durchgängig. Das Setup ist kein Endzustand. Es ist der aktuelle Stand der Verteidigung. Flexibel, anpassbar und mit Weitsicht.
Der Knoten
Ich habe dieses Setup gebaut, bevor pnpm 11 es zum Default machte. Wenn der nächste Wurm kommt, baue ich das nächste. Das ist REFACTOR — kein fertiges Produkt, sondern die Weigerung, dem Default zu vertrauen.
Und das ist der Punkt, an dem eine KI dir nicht hilft: Welche vier Zeilen in deine pnpm-workspace.yaml gehören, lässt sich nicht in 30 Sekunden ergooglen und nicht in einen Bullet summarisieren. Das weißt du erst, wenn du verstanden hast, warum die Defaults so sind, wie sie sind. Und warum das Politik ist.
Euer Mehmet
Footnotes
-
Wiz — Miasma: Supply Chain Attack Targeting RedHat npm Packages. https://www.wiz.io/blog/miasma-supply-chain-attack-targeting-redhat-npm-packages ↩
-
Trend Micro — Axios NPM Package Compromised: Supply Chain Attack Hits JavaScript HTTP Client with 100M+ Weekly Downloads. https://www.trendmicro.com/en_us/research/26/c/axios-npm-package-compromised.html ↩
-
Snyk — TanStack npm Packages Hit by Mini Shai-Hulud. https://snyk.io/blog/tanstack-npm-packages-compromised/ ↩
-
StepSecurity — Active Supply Chain Attack: Malicious node-ipc Versions Published to npm (May 2026). https://www.stepsecurity.io/blog/node-ipc-npm-supply-chain-attack ↩
-
npm Docs — Unpublish Policy (post-left-pad, 24h window). https://docs.npmjs.com/policies/unpublish/ ↩
-
Unit 42 (Palo Alto Networks) — npm Supply Chain Attack: Shai-Hulud Worm. https://unit42.paloaltonetworks.com/npm-supply-chain-attack/ ↩
-
Microsoft Security — Shai-Hulud 2.0: Guidance for Detecting, Investigating, and Defending. https://www.microsoft.com/en-us/security/blog/2025/12/09/shai-hulud-2-0-guidance-for-detecting-investigating-and-defending-against-the-supply-chain-attack/ ↩
-
Phylum — Hidden Dependencies Lurking in the Software Dependency Network. https://blog.phylum.io/hidden-dependencies-lurking-in-the-software-dependency-network/ ↩ ↩2
-
Snyk — A Post-Mortem of the Malicious event-stream Backdoor. https://snyk.io/blog/a-post-mortem-of-the-malicious-event-stream-backdoor/ ↩
-
CISA Alert — Malware Discovered in Popular npm Package, ua-parser-js. https://www.cisa.gov/news-events/alerts/2021/10/22/malware-discovered-popular-npm-package-ua-parser-js ↩
-
BleepingComputer — Dev corrupts npm libs ‘colors’ and ‘faker’ breaking thousands of apps. https://www.bleepingcomputer.com/news/security/dev-corrupts-npm-libs-colors-and-faker-breaking-thousands-of-apps/ ↩
-
Snyk — Peacenotwar: Malicious npm node-ipc Package Vulnerability. https://snyk.io/blog/peacenotwar-malicious-npm-node-ipc-package-vulnerability/ ↩
-
Sam James — xz-utils backdoor (CVE-2024-3094) canonical timeline. https://gist.github.com/thesamesam/223949d5a074ebc3dce9ee78baad9e27 ↩
-
Aikido Security — npm debug and chalk packages compromised in supply chain attack. https://www.aikido.dev/blog/npm-debug-and-chalk-packages-compromised ↩
-
nodejs-security.com — npm ignore-scripts best practices. https://www.nodejs-security.com/blog/npm-ignore-scripts-best-practices-as-security-mitigation-for-malicious-packages ↩
-
pnpm Docs — pnpm add (—save-exact). https://pnpm.io/cli/add ↩
-
pnpm Docs — Settings: minimumReleaseAge. https://pnpm.io/settings ↩
-
Brandon Pugh — npm 11.10.0 introduces min-release-age. https://www.brandonpugh.com/til/node/package-version-cooldown/ ↩
-
pnpm Release 10.16 — strictDepBuilds default-on roadmap. https://pnpm.io/blog/releases/10.16 ↩
-
pnpm Release 10.26 — allowBuilds replaces onlyBuiltDependencies. https://pnpm.io/blog/releases/10.26 ↩