Back openDesk Edu for a sovereign, open-source education — every vote counts.
Vote nowSave products you love by clicking the heart icon.
How an attacker chained pull_request_target cache poisoning with GitHub Actions memory extraction to publish 84 malicious npm packages — and what you must audit in your own CI/CD pipeline.
npm 12.0.0 wurde am 8. Juli 2026 veröffentlicht und am selben Tag auf den latest Tag gehoben. Es ist das bedeutendste npm-Major-Release seit npm 7, das das Lockfile-Format grundlegend überarbeitet hat. Falls Sie das Changelog noch nicht gelesen haben, sind hier die wichtigsten Punkte: drei npm install-Verhaltensweisen, die seit einem Jahrzehnt automatisch abliefen, erfordern nun eine explizite Genehmigung. Mehrere weitere Breaking Changes werden CI-Skripte und Lockfile-gesteuerte Build-Pipelines stillschweigend beeinträchtigen.
npm 12 ist bereits in Node.js 24.16.0 (veröffentlicht am 9. Juli) und Node.js 26.3.0 (veröffentlicht am 1. Juni) enthalten, und das npm-Team hat angedeutet, dass es gemäß der Node-Security-Policy auf beide aktiven LTS-Linien backportet werden könnte. Das bedeutet, dass die neuen Standardeinstellungen die Mehrheit der Node-Nutzer innerhalb von Wochen, nicht Monaten, erreichen werden.
Die Preview-Warnungen sind seit npm 11.16.0 (27. Mai) verfügbar. Wenn Sie diese noch nicht ausgeführt haben, sind Sie bereits im Rückstand.
npm 12 wandelt drei Verhaltensweisen während der Installation von automatischen Prozessen in Opt-in-Optionen um. Jede dieser Änderungen schließt einen Pfad zur Code-Ausführung, der in den letzten zwei Jahren bei realen Supply-Chain-Angriffen genutzt wurde.
npm install führt keine preinstall, install oder postinstall Skripte aus Abhängigkeitspaketen mehr aus, es sei denn, das Paket steht auf einer expliziten Allowlist. Dies schließt den impliziten node-gyp rebuild ein, den npm für jedes Paket auslöst, das eine binding.gyp Datei mitliefert, selbst wenn kein explizites Installations-Skript existiert. prepare Skripte von Git-, Datei- und Link-Abhängigkeiten werden auf die gleiche Weise blockiert.
Ihre eigenen Projektskripte im Root-Verzeichnis package.json werden weiterhin ausgeführt. Wenn Ihr postinstall patch-package ausführt oder Ihr prepare husky-Hooks installiert, ändert sich für Sie nichts. Die Blockierung zielt auf Code ab, der aus der Registry kommt.
Die Allowlist befindet sich in package.json unter einem neuen allowScripts Feld und wird durch zwei neue Befehle verwaltet:
npm approve-scripts --allow-scripts-pending
# Approve specific packages
npm approve-scripts sharp esbuild
# Deny specific packages
npm deny-scripts node-sass
Der resultierende package.json Eintrag sieht so aus:
{
"allowScripts": {
"sharp@0.33.5": true,
"esbuild@0.24.0": true
}
}
Genehmigungen sind standardmäßig an spezifische Versionen gebunden. Wenn Sie sharp@0.33.5 genehmigen, erstreckt sich diese Genehmigung nicht automatisch auf sharp@0.34.0. Sie müssen die Aktualisierung prüfen und erneut genehmigen. Das --no-allow-scripts-pin Flag umgeht diese versionsspezifische Bindung; verwenden Sie es nur, wenn Sie die damit verbundenen Kompromisse verstehen.
Der v12-Standard ist ein „Soft Skip“, kein harter Fehler. Ein nicht genehmigtes Skript wird übersprungen, npm gibt eine Warnung aus, und die Installation ist dennoch mit dem Exit-Code 0 erfolgreich. Es gibt eine separate Opt-in-Einstellung, strict-allow-scripts, die das Überspringen für CI in einen harten Fehler verwandelt.
npm install wird Git-Abhängigkeiten, ob direkt oder transitiv, nicht mehr auflösen, es sei denn, Sie setzen --allow-git. Dies schließt einen Pfad zur Code-Ausführung, bei dem das .npmrc einer transitiven Git-Abhängigkeit das Git-Executable überschreiben konnte, das npm aufruft – ein Pfad, der selbst unter --ignore-scripts offen blieb.
# Before (v11, still works but shows warning)
npm install github:some-org/private-fork#feature-branch
# After (v12, blocked)
# Error: git dependencies are not allowed by this project
# Fix: pass the flag
npm install github:some-org/private-fork#feature-branch --allow-git
# Or configure in .npmrc
echo "allow-git=true" >> .npmrc
Das npm-Team rät explizit davon ab, sich langfristig auf den --allow-git Notausgang zu verlassen. Für Paketreferenzen innerhalb eines Monorepos ist das Workspace-Protokoll (workspace:*) der richtige Ersatz. Für externe Forks veröffentlichen Sie stattdessen ein Prerelease in Ihrer Registry, anstatt auf einen Git-SHA zu pinnen.
Abhängigkeiten, die auf HTTPS-Tarball-URLs verweisen, werden nicht mehr ohne --allow-remote aufgelöst:
{
"dependencies": {
"my-package": "https://artifactory.internal/example-pkg-1.0.0.tgz"
}
}
Dies führt in v12 zu einem Resolve-Fehler, sofern --allow-remote nicht gesetzt ist. Die Flags --allow-file und --allow-directory existieren zwar, aber ihre Standardwerte ändern sich in v12 nicht.
Der primäre Migrationspfad für diese Abhängigkeiten besteht darin, sie über eine ordnungsgemäße npm-Registry bereitzustellen (die öffentliche Registry, eine private npm-Registry oder ein Registry-Proxy wie Verdaccio). Die im Mai 2026 eingeführte Staged-Publishing-Funktion von npm fügt ein Review-Fenster vor der Veröffentlichung für Registry-Pakete hinzu, deckt jedoch keine Ad-hoc-Tarballs ab.
Die drei Sicherheitsstandards dominieren die Diskussion, aber das v12-Changelog enthält auch mehrere Breaking Changes, die zu stillschweigenden Fehlern in CI-Pipelines führen werden.
npm shrinkwrap wurde entfernt. npm-shrinkwrap.json wird weder im Projekt-Root noch innerhalb von Abhängigkeits-Tarballs mehr geladen oder beachtet. Der Config-Alias shrinkwrap wurde ebenfalls entfernt.
Wenn Sie sich auf npm-shrinkwrap.json für deterministische Installationen von veröffentlichten Paketen verlassen, müssen Sie es umbenennen:
mv npm-shrinkwrap.json package-lock.json
Wenn Sie einen gesperrten Abhängigkeitsbaum innerhalb eines veröffentlichten Pakets mitliefern (der ursprüngliche Anwendungsfall für shrinkwrap), migrieren Sie stattdessen zu bundleDependencies in package.json. Die Gefahr ist hier stillschweigend: Ein veröffentlichtes Paket, das nur npm-shrinkwrap.json und kein package-lock.json mitliefert, wird von v12-Konsumenten so behandelt, dass die gepinnten Abhängigkeiten ignoriert und stattdessen frisch gegen die Registry aufgelöst werden.
In v11 gab npm view --json ein einzelnes Objekt für Pakete mit einer einzigen Version und ein Array für Pakete mit mehreren Versionen zurück. Diese Inkonsistenz war seit ihrer Einführung eine Fehlerquelle. In v12 ist die Ausgabe immer ein Array:
# v11 behavior (single version)
npm view express@4.18.2 --json
# => { "name": "express", "version": "4.18.2", ... }
# v12 behavior (always array)
npm view express@4.18.2 --json
# => [{ "name": "express", "version": "4.18.2", ... }]
Jedes Skript, das JSON.parse(output).version ausführt, ohne auf ein Array zu prüfen, wird bei jedem Aufruf abstürzen, nicht nur im Fall von Multi-Version-Paketen. Die Lösung besteht darin, das Ergebnis immer als Array zu behandeln:
// Vorher (stürzt in v12 ab)
const pkg = JSON.parse(output);
console.log(pkg.version);
// Nachher (funktioniert in beiden)
const [pkg] = JSON.parse(output);
console.log(pkg.version);
### npm pkg Output No Longer Forced to JSON
In v11, `npm pkg get version` returned a JSON-wrapped value: `{"version":"1.2.3"}`. In v12, single-value queries return a plain string:
```bash
# v11
npm pkg get version # => {"version":"1.2.3"}
# v12
npm pkg get version # => "1.2.3"
Scripts that pipe npm pkg through JSON.parse() and access .version will throw a TypeError. Unlike the npm view array change where you can add a destructuring wrapper, this one requires restructuring how the consuming code reads the output.
The --json output for both commands has been restructured in v12 to share a consistent schema. The changelog does not document the exact field-level diff, but the stated goal is unified output between the two commands. Any CI step that parses these outputs to extract tarball filenames, integrity hashes, or version numbers should be tested against v12 before deploying.
The adduser command is gone. Create and manage user accounts on the npm website, and use npm login to authenticate on the command line. For CI pipelines, this is unlikely to affect you if you already use token-based authentication via .npmrc entries:
//registry.npmjs.org/:_authToken=${NPM_TOKEN}
Legacy provisioning scripts that call npm adduser will fail with a "command not found" error.
Three commands for bookmarking packages on the registry are removed with no CLI replacement. If any CI script calls them, those calls will fail.
man npm-install and similar system man page lookups stop working. npm help install continues to work because it reads from npm's bundled documentation.
npm sbom --sbom-format=cyclonedx now reports the name field from each package's package.json instead of the on-disk directory name. The name, bom-ref, and purl of the root component and of aliased dependencies may change. In monorepo setups where the directory is packages/logger/ but the package name is @scope/logger, every SBOM identifier will change. Compliance systems that key off bom-ref to track vulnerabilities across releases will see the same package appear as a new entry.
npm init now defaults to an empty license string instead of "ISC". If not set, the license field is omitted from new packages.
npm no longer resolves the Node binary path via PATH lookup. It relies solely on process.execPath. This is fine in most setups but can break when npm is invoked through a version manager or wrapper script that symlinks Node into the PATH.
The root package.json preinstall script now runs before dependencies are installed, not after. If your root preinstall depends on any dependency being present in node_modules, it will fail.
The three security changes affect CI pipelines differently depending on how your builds are configured.
If your Dockerfile or CI configuration pins npm to a specific major version:
RUN npm install -g npm@11
You control when v12 arrives. Schedule the upgrade like any other dependency bump: audit, test on a staging branch, then update the pin. This is the safest approach.
If your Dockerfile does npm install -g npm@latest or you use a node:latest style tag, v12 arrives on its own schedule. On July 8, images rebuilt after the release will produce different install behavior with no code changes. This is how teams end up debugging a production incident on release day.
Lockfile resolution changes. If your CI runs npm ci and relies on npm-shrinkwrap.json, the entire install step will produce different dependency trees in v12. Rename to package-lock.json before upgrading.
JSON output parsing. Any CI step that captures output from npm view --json, npm pack --json, npm publish --json, or npm pkg get and parses it will produce wrong results until updated. This includes release automation, version bump scripts, and artifact publishing workflows.
Native addon compilation. Packages that compile C++ at install time (bcrypt, sharp in some configurations, older database drivers) will silently produce missing binary errors at runtime unless their scripts are on the allowlist. The install succeeds, the app crashes.
Git dependencies. Any monorepo or internal toolchain that references packages via git+https:// URLs will fail to resolve. This includes transitive dependencies: your top-level deps may be clean, but a dependency's own dependency could come from a git URL.
Downloaders. Packages whose postinstall fetches browser binaries (Puppeteer, Playwright, Cypress) will install the npm package but leave the binary behind. The failure mode is a runtime error about a missing executable, not an install error.
The entire v12 breaking change surface is available as warnings in npm 11.16.0 and later. You can audit and prepare without switching to v12.
npm install -g npm@11
npm --version # sollte 11.16.0 oder neuer sein
If you are on Node.js 24.16.0 or Node.js 26.3.0, this version is already bundled.
npm approve-scripts --allow-scripts-pending
This lists every package with lifecycle scripts that would be blocked under v12 defaults. The output includes the command each script runs.
Ignore prepare scripts from registry packages on the first pass. prepare only executes when you install from git, file, or link -- never from a normal registry install. The audit lists them because it cannot know you will not switch a dependency to a git URL tomorrow, but for triage they are noise.
Focus on install and postinstall scripts. These are the ones that run on every clean install.
For each real script entry, decide:
npm approve-scripts <package>.postinstall (sharp, esbuild) already distribute prebuilt binaries through optionalDependencies -- the install script is a fallback for unsupported platforms. If your platform has a prebuilt binary, the script is unnecessary.# Testen, ob ein Paket ohne sein Install-Script funktioniert
npm deny-scripts sharp
npm ci
node -e "require('sharp')" # wenn dies funktioniert, dauerhaft verbieten
The allowScripts field is written to your package.json. Commit it. From now on, every new dependency that wants script execution shows up as a diff in code review instead of executing silently during install.
# Alle Git-Abhängigkeiten im Verzeichnisbaum finden
grep -r 'git+' package.json package-lock.json
# Remote-Tarball-Abhängigkeiten finden
grep -r 'https://.*\.tgz' package.json package-lock.json
For each one, decide:
workspace:* protocol.--allow-git or --allow-remote in tightly scoped CI steps, and set a deadline to migrate.Check every CI pipeline that parses npm output:
# Skripte finden, die npm-JSON-Ausgaben parsen
grep -r 'npm.*--json' .github/ gitlab-ci.yml Jenkinsfile
Update parsing logic for the new output shapes. Test npm view --json and npm pkg get against v12 pre-release to confirm your parsers still work.
# Prüfen, ob eine vorhanden ist
find . -name "npm-shrinkwrap.json"
# Falls gefunden, umbenennen
mv npm-shrinkwrap.json package-lock.json
Add a specific npm version to your CI configuration:
# Vorher (floating)
RUN npm install -g npm@latest
# Nachher (gepinnt)
RUN npm install -g npm@12
If you are not ready for v12, pin to npm@11 and schedule the v12 upgrade separately.
# Mit v12-Defaults ausführen (ab npm 11.16+ nur Warnungen)
npm ci
# Überprüfen, ob native Module korrekt geladen werden
node -e "require('sharp'); require('esbuild'); console.log('OK')"
# Die vollständige Testsuite ausführen
npm test
Führen Sie dies auf einem Staging-Branch aus, bevor Sie Ihre Produktions-CI-Konfiguration ändern.
Monorepos weisen in v12 eine zusätzliche Komplexitätsebene auf.
Workspaces sind unabhängige Installationsbereiche. Die Genehmigung von sharp im Monorepo-Root bedeutet nicht, dass sie in packages/api genehmigt ist. Sie müssen npm approve-scripts in jedem Workspace ausführen, der von einem Paket mit Lifecycle-Skripten abhängt.
Git-Abhängigkeiten sind in Monorepo-Setups für Pre-Release-Querverweise üblich. Das Workspace-Protokoll (workspace:*) ist der korrekte Ersatz – es löst lokal auf, ohne Daten von Git abzurufen, und wird nicht vom --allow-git-Standard beeinflusst.
Die Root-.npmrc-Konfiguration wird nicht auf Workspaces vererbt. Wenn Sie allow-git=true in der Root-.npmrc festlegen, betrifft dies nur Operationen auf Root-Ebene. Jeder Workspace löst seine eigene effektive Konfiguration auf. Legen Sie die Konfiguration entweder in der .npmrc jedes Workspaces fest oder übergeben Sie das Flag zum Zeitpunkt der Installation: npm ci --workspaces --allow-git.
npm 7 (Oktober 2020) war das letzte Major-Update, das die grundlegende Funktionsweise von Installationen änderte. Es führte das v2-Lockfile-Format, die automatische Installation von Peer-Dependencies und die Workspace-Unterstützung ein. Der Übergang dauerte Monate – Teams, die die npm-Version gepinnt hatten, überstanden dies ohne Unterbrechungen; Teams mit floating npm stießen auf CI-Fehler und verbrachten Tage mit der Aktualisierung von Lockfiles.
npm 9 (Februar 2023) verlief relativ ruhig: Es entfernte npm audit --audit-level, änderte das Ausgabeverhalten von npm pack und verschärfte die Konfigurationsvalidierung. Die meisten Teams konnten ohne Zwischenfälle upgraden.
npm 10 (Juni 2023) entfernte npm link-Schritt-Verhaltensänderungen und das --global-Style-Flag, was ebenfalls für die meisten geringe Auswirkungen hatte.
npm 12 kommt in seinem "Blast Radius" eher npm 7 nahe. Die Sicherheits-Defaults betreffen jede Installation, und die Änderungen an der JSON-Ausgabe betreffen jede CI-Pipeline, die Metadaten aus npm-Befehlen extrahiert. Der Preview-Zeitraum (Warnungen seit npm 11.16.0 am 27. Mai verfügbar) ist kurz im Vergleich zum npm 7-Übergang, bei dem die v2-Lockfile-Warnungen Monate vor der Breaking-Change-Umstellung erschienen. Der komprimierte Zeitplan spiegelt die Dringlichkeit der Supply-Chain-Angriffswelle wider: Miasma, der TanStack-Breach und der Axios-Hijack nutzten genau die Eintrittspunkte aus, die npm 12 schließt.
pnpm blockierte Lifecycle-Skripte standardmäßig in Version 10. Bun und Deno führen für Abhängigkeiten überhaupt keine Lifecycle-Skripte aus. npm 12 bringt das Standardverhalten in Einklang mit dem Rest des Ökosystems – ein Jahrzehnt zu spät, aber endlich da.
Die Community-Diskussion im Node.js-Release-Issue und der GitHub-Blogpost decken das erwartbare Spektrum ab: Sicherheitsteams applaudieren, Maintainer nativer Module eilen mit aktualisierten Anleitungen herbei und Plattform-Teams fragen nach der Migrationsstrategie für Monorepos.
Ein konkretes Problem, das während der Prerelease-Phase auftrat: Die Ankündigung enthielt ursprünglich eine Änderung, die unbekannte Konfigurationsschlüssel in .npmrc in harte EUNKNOWNCONFIG-Fehler verwandelte. Dies wurde in PR #9729 rückgängig gemacht – unbekannte Datei-Konfigurationen lösen nun standardmäßig eine Warnung aus, mit einer optionalen strict-npmrc-Einstellung für alle, die die strengere Prüfung wünschen. Unbekannte CLI-Flags führen weiterhin zu Fehlern. Das Rollback entfernte eine echte Hürde für Teams, die Legacy- oder toolspezifische Schlüssel in ihrer Konfiguration führen, genau die Art von Reibung, die ein Sicherheits-Upgrade verzögern kann.
Eine weitere Lücke, die die Community identifiziert hat: npm approve-scripts --allow-scripts-pending listet auf, welche Pakete Skripte haben und welchen Befehl jedes einzelne ausführt, aber nicht, was diese Skripte bei der Ausführung tatsächlich tun – welche Dateien sie laden, ob sie Netzwerkaufrufe tätigen oder außerhalb des Paketverzeichnisses schreiben. Ein RFC wird derzeit diskutiert, der einen Review-Report-Modus vorschlägt, um diese Signale sichtbar zu machen.
| Datum | Ereignis |
|---|---|
| 18. Februar 2026 | --allow-git-Warnung verfügbar in npm 11.10.0 |
| 20. Mai 2026 | npm 12.0.0-pre.0 (erste Prerelease) |
| 27. Mai 2026 | npm 11.16.0 (vollständiger Warnsatz für alle v12 Breaking Changes) |
| 1. Juni 2026 | Node.js 26.3.0 (enthält npm 11) |
| 9. Juni 2026 | GitHub-Blogpost zur Ankündigung der v12 Breaking Changes |
| 19. Juni 2026 | npm 12.0.0-pre.1 (Verschärfung der allow-git/allow-remote Defaults) |
| 8. Juli 2026 | npm 12.0.0 final -- befördert zu latest |
| 9. Juli 2026 | Node.js 24.16.0 (enthält npm 12) |
| Q3 2026 | 2FA-Bypass-Token Deprecation Phase 1 (Blockieren sensibler Operationen) |
| ~Januar 2027 | 2FA-Bypass-Token Deprecation Phase 2 (Blockieren des direkten Publishings) |
npm beginnt zudem mit der Deprecation von granularen Access-Token, die so konfiguriert sind, dass sie die Zwei-Faktor-Authentifizierung umgehen. Phase 1, erwartet im August 2026, verhindert, dass diese Token sensible Konto- und Publishing-Operationen durchführen. Phase 2, etwa im Januar 2027, entfernt deren Fähigkeit zum direkten Publishing. Die Empfehlung von GitHub lautet, automatisiertes Publishing auf Trusted Publishing (OIDC) oder Staged Publishing mit einem menschlichen Genehmigungsschritt umzustellen.
npm approve-scripts --allow-scripts-pending in jedem Repo ausführen. Ignorieren Sie prepare-Einträge beim ersten Durchlauf.allowScripts-Block in package.json committen.package.json und package-lock.json suchen (grep). Migration zum Workspace-Protokoll oder zu Registry-Versionen planen.npm view --json- und npm pkg-Output-Strukturen.npm@11 (aufschieben) oder npm@12 (jetzt upgraden).Das gesamte Audit dauert für ein typisches Repository weniger als eine Stunde. Wenn Sie es jetzt erledigen, wird der Juli eine kontrollierte Migration statt einer Incident-Untersuchung.
allowScripts-Standard nun schließt. Die Phantom-Gyp-Technik, die die Verbreitung des Worms vorantrieb, basierte darauf, dass binding.gyp-Dateien während der Installation implizite node-gyp rebuild ausführten – der primäre Angriffsvektor, den npm 12 standardmäßig blockiert.