Ein schädliches Zod-Paket der Version 4.6.6 wurde nicht auf npm veröffentlicht. Am 21. September 2026 gelangten manipulierte Commits in das GitHub-Repository von Zod, doch der Veröffentlichungsworkflow lief für diese Änderungen nicht. Auch Standard Schema erreichte die Veröffentlichung nicht. Eine geplante Version `@nubjs/nub@0.9.4` gelangte in die Validierung von npm und wurde gesperrt, bevor sie öffentlich installiert werden konnte. Das sind drei verschiedene Ergebnisse desselben Vorfalls, wie aus dem [Vorfallsbericht von npm und GitHub](https://github.com/orgs/community/discussions/208477) und der [Richtigstellung von Zod-Maintainer Colin McDonnell](https://news.ycombinator.com/item?id=50052814) hervorgeht.

Wenn Ihr Repository kompromittiert wurde, reicht die Frage nach Schadcode auf GitHub nicht aus. Prüfen Sie, **welche Grenze der Code überschritten hat**: vom Quellcode über die Freigabe zur Veröffentlichung und die Validierung durch die Registry bis zum Paketinhalt und schließlich zur Nutzung durch Dritte. Jeder Schritt verlangt eigene Belege und eine passende Reaktion.

## Was geschah bei Zod, Standard Schema und Nub?

Am 21. September erschienen zusammenhängende schädliche Änderungen in den öffentlichen Repositories von Zod, Standard Schema und Nub. Das [Team, das den Vorfall untersuchte](https://github.com/orgs/community/discussions/208477), verlinkte die betroffenen Commits und beschrieb Code, der bei der Installation weiteres JavaScript herunterladen und ausführen sollte, **falls er in ein Paket gelangte und dieses installiert wurde**. Die veröffentlichten Erkenntnisse belegen nicht, dass dieser Code auf einem Rechner von Paketnutzern ausgeführt wurde. Auch aus den Commit-Metadaten lässt sich nicht ableiten, wie der Angreifer Zugriff erhielt.

Entscheidend ist, wie weit die Änderungen bei den drei Projekten gelangten:

| Projekt | Bis wohin die schädliche Änderung gelangte | Ergebnis auf npm |
| --- | --- | --- |
| `zod` | Commit im öffentlichen Repository | Gelangte nicht in die npm-Veröffentlichung; keine schädliche Zod-Version war installierbar |
| `standard-schema` | Commit im öffentlichen Repository | Gelangte nicht in die npm-Veröffentlichung; keine schädliche Standard-Schema-Version war installierbar |
| `@nubjs/nub@0.9.4` | Veröffentlichungsprozess von npm | Während der Validierung zurückgehalten und vor der öffentlichen Bereitstellung gesperrt |

Die Tabelle stützt sich auf die [Veröffentlichungsdaten des Untersuchungsteams](https://github.com/orgs/community/discussions/208477). Eine GitHub-Commit-Seite allein enthält diese Informationen nicht. Damit ist auch eine wichtige Unterscheidung klar: **Nub erreichte den Veröffentlichungsprozess von npm, wurde aber kein öffentlich installierbares Paket.** Dass eine Version eingereicht wurde, bedeutet nicht, dass Nutzer sie regulär abrufen konnten.

Das Untersuchungsteam behandelte die drei Repositories gemeinsam. McDonnell wandte jedoch ein, die Formulierung könne den Eindruck erwecken, npm habe Zod durch einen Scan gestoppt. In seinem [Bericht aus erster Hand in derselben Diskussion](https://github.com/orgs/community/discussions/208477) erklärt er, dass die schädlichen Änderungen den Zod-Veröffentlichungsworkflow nicht auslösten. Auch Standard Schema wurde nicht veröffentlicht. Der npm-Scanner erkannte dagegen die geplante Nub-Version. Die detaillierte Tabelle und McDonnells Richtigstellung stimmen bei den Ergebnissen überein; die pauschale Darstellung kann dennoch zu einem falschen Schluss führen.

## Warum gelangte der Schadcode aus dem Zod-Repository nicht auf npm?

Ein Git-Push verändert das Repository. Er gibt für sich genommen keine npm-Veröffentlichung frei. Laut McDonnell nutzte Zod für seine Veröffentlichung in CI eine eigene GitHub-Umgebung und [npm Trusted Publishing](https://docs.npmjs.com/trusted-publishers/). Ein gewöhnlicher Commit- oder Tag-Push konnte diesen Job nicht auslösen. Er hielt einen kompromittierten Git-Push-Zugang für wahrscheinlich, sagte aber zugleich, dass der genaue Zugriffsweg noch untersucht werde. Das ist eine Hypothese, kein gesicherter Befund. [Sein Bericht](https://github.com/orgs/community/discussions/208477) belegt den hier entscheidenden engeren Punkt: Die schädlichen Commits starteten den Zod-Veröffentlichungsworkflow nicht.

Trusted Publishing autorisiert Veröffentlichungen anhand einer konfigurierten CI-Identität und OIDC, ohne dem Workflow ein langlebiges npm-Token zu geben. Es regelt, **wer oder was eine Veröffentlichung anfordern darf**, nicht ob der veröffentlichte Code harmlos ist. [GitHub-Umgebungen](https://docs.github.com/en/actions/reference/workflows-and-actions/deployments-and-environments) können zulässige Branches einschränken und eine Freigabe verlangen, bevor ein Job läuft oder Umgebungsgeheimnisse erhält. Wie gut Ihr Projekt geschützt ist, hängt von den tatsächlichen Auslösern, Berechtigungen, Umgebungseinstellungen und weiteren Veröffentlichungswegen ab. Ein kompromittierter Workflow oder ein anderer berechtigter Publisher verändert die Lage.

Nub zeigt die nächste Grenze. Laut McDonnell konnte dort das Anlegen eines Tags die Veröffentlichungs-CI auslösen. Das geplante Paket `0.9.4` erreichte npm. Bei der Validierung vor der Veröffentlichung wurde es erkannt und zurückgehalten. Die [Beschreibung des npm-Scans bei der Veröffentlichung](https://github.blog/changelog/2026-07-28-npm-publish-time-malware-scanning-and-dual-use-metadata/) unterscheidet zwischen einem eingereichten und einem zur Installation verfügbaren Paket. Der Scan stoppte diese geplante Nub-Version. Er stoppte weder Zod noch Standard Schema, denn deren Änderungen erreichten diesen Prozess nicht.

Dass diese Veröffentlichungswege blockiert waren, schließt eine Nutzung als direkte Git-Abhängigkeit, über ein heruntergeladenes Quellarchiv oder aus dem kompromittierten Verlauf nicht aus. Der [Vorfallsbericht](https://github.com/orgs/community/discussions/208477) nennt weder eine nachgewiesene Ausführung auf diesen Wegen noch eine bestätigte Zahl betroffener Installationen.

## Warum wirkten die Release Notes zu Zod 4.6.6 verdächtig?

Am 12. Oktober verwies ein [Hacker-News-Thread zur möglichen Kompromittierung von Zod 4.6.6](https://news.ycombinator.com/item?id=50052814) auf einen schädlichen September-Commit. Er stand in der automatisch erzeugten Commit-Liste auf GitHubs [Release-Seite zu `v4.6.6`](https://github.com/colinhacks/zod/releases/tag/v4.6.6). Dieser Eintrag war ein sinnvoller Anlass zur Untersuchung. Er bewies nicht, dass der Schadcode im npm-Tarball steckte.

Die schädliche Änderung war rückgängig gemacht worden; ihr Commit blieb aber im Bereich, aus dem die Release Notes erzeugt wurden. McDonnell [erklärte](https://news.ycombinator.com/item?id=50052814), dass der Code im veröffentlichten Paket nicht enthalten war. Genau hier wird eine Frage zum Quellcodeverlauf leicht mit einem Befund zum Paketinhalt verwechselt. Ein Tag kann auch den rückgängig machenden Commit enthalten, und eine Release-Seite kann sowohl die Einschleusung als auch ihre Entfernung aufführen. Prüfen Sie das gebaute Paket und seine Verfügbarkeit in der Registry, bevor Sie eine Installation als infiziert bezeichnen.

Am **12. Oktober** folgte eine weitere Maßnahme, getrennt von der Reaktion auf den 21. September. McDonnell zufolge entfernte er die schädlichen Commits aus dem Verlauf. Dabei wog er die Transparenz gegen das Risiko ab, dass ein historischer Checkout, etwa mit `git bisect`, den Code ausführen könnte. Er berichtete auch, dass `v4.6.6` durch diese Bereinigung die npm-Provenienz verlor und danach [`v4.6.7` erschien](https://github.com/colinhacks/zod/releases/tag/v4.6.7). [Seine Aktualisierung](https://github.com/orgs/community/discussions/208477) beschreibt eine spätere Folge der Bereinigung des Verlaufs. Dass die Provenienz nach dieser Umschreibung verloren ging, ist kein Beleg für Malware im vorherigen Paket 4.6.6.

Provenienz erfüllt einen bestimmten Zweck. Laut [npm-Dokumentation](https://docs.npmjs.com/viewing-package-provenance/) verbinden Attestierungen ein veröffentlichtes Paket mit seinem Quell-Repository und Build-Workflow. Diese Verbindung hilft nachzuvollziehen, wie ein Paket entstand. Sie ist kein Malware-Scan. Auch eine automatisch erzeugte GitHub-Release-Zusammenfassung ersetzt nicht die Prüfung des Pakets.

## Welche vier Grenzen sollten Maintainer prüfen?

Die folgenden vier Prüfungen eignen sich für jeden Verdacht auf einen Angriff vom Repository bis zum Paket. Beginnen Sie mit dem beobachteten Ereignis, sichern Sie Belege und gehen Sie erst weiter, wenn Sie auch für die nächste Grenze Nachweise haben. Die Methode leitet sich aus dem Zod-Vorfall sowie den Veröffentlichungsfunktionen von [npm](https://docs.npmjs.com/trusted-publishers/) und [GitHub](https://docs.github.com/en/actions/reference/workflows-and-actions/deployments-and-environments) ab. Sie behauptet nicht, dass alle folgenden Schritte im September stattfanden.

### 1. Änderungen im Repository feststellen

Halten Sie die IDs verdächtiger Commits, betroffene Dateien, Autoren- und Push-Zeitstempel, Branches und Tags sowie den Zeitpunkt der Entfernung fest. Sichern Sie den ursprünglichen Diff und die Logs, bevor Sie den Verlauf bereinigen. Prüfen Sie Paketmanifest, Lifecycle-Skripte, Build-Skripte und Workflow-Dateien getrennt. Eine Änderung im Repository kann erst beim Build oder bei der Installation wirksam werden. Umgekehrt kann ein bedrohlich wirkender Commit vor jedem Build rückgängig gemacht worden sein.

Formulieren Sie Beobachtung und Erklärung getrennt: „Commit X fügte Code in Datei Y ein und wurde zum Zeitpunkt Z gepusht“ ist eine Beobachtung. Für die Aussage „Ein gestohlener SSH-Schlüssel war die Ursache“ brauchen Sie Belege zum Schlüssel oder Zugriff. McDonnells veröffentlichter Bericht klärt den Zugriffsweg nicht abschließend. Eine Meldung zum Vorfall sollte daher keinen erfinden. Der [Vorfallsbericht](https://github.com/orgs/community/discussions/208477) verlinkt die öffentlichen Änderungen, belegt aber nicht, welches Zugangsmittel zunächst kompromittiert wurde.

Diese Prüfung zeigt, **wo schädlicher Quellcode vorhanden war**, auch in historischen Checkouts. Ob eine Veröffentlichung lief, beantwortet sie nicht. Sichern Sie die Belege, falls Sie später den öffentlichen Verlauf umschreiben.

### 2. Prüfen, ob ein Veröffentlichungsjob laufen konnte und lief

Lesen Sie die Workflow-Konfiguration, statt von einem Tag auf eine Veröffentlichung zu schließen. Welches Ereignis startet den Veröffentlichungsjob: ein Push, das Anlegen eines Tags oder ein manueller Start? Muss die Umgebung den Job freigeben, bevor er läuft? Welche Muster für Branches und Tags sind zulässig? Wer darf den Workflow und die Umgebungsregeln ändern? Akzeptiert npm nur den vorgesehenen OIDC-Publisher, oder sind auch Zugangsdaten für eine direkte Veröffentlichung und ein anderer Workflow berechtigt?

Prüfen Sie anschließend das Ausführungsprotokoll für den fraglichen Zeitraum. Halten Sie die Lauf-ID, Version der Workflow-Datei, auslösende Git-Referenz, handelnde Person, Freigabe der Umgebung und Ergebnis der Veröffentlichung fest. „Der Workflow lässt sich manuell starten“ und „Es gab keine relevante Ausführung“ sind verschiedene Aussagen. Ein Screenshot der Konfiguration nach der Behebung beweist nicht, welche Regel während des Angriffs galt. Sichern Sie die damalige Konfiguration und den Audit-Verlauf.

Für diesen Vorfall sagt McDonnell, dass der Zod-Veröffentlichungsworkflow für die schädlichen Commits **nicht** lief. Das ist aussagekräftiger als die Vermutung, npm hätte sie erkannt. [Trusted Publishing](https://docs.npmjs.com/trusted-publishers/) und [GitHub-Umgebungen](https://docs.github.com/en/actions/reference/workflows-and-actions/deployments-and-environments) erläutern die allgemeinen Schutzmechanismen; [seine Aussage](https://github.com/orgs/community/discussions/208477) liefert das Ergebnis für dieses Projekt.

### 3. Registry-Status feststellen und Paket untersuchen

Unterscheiden Sie vier Zustände: Eine Veröffentlichung wurde versucht; eine Version gelangte in die Validierung; sie wurde zurückgehalten oder gesperrt; oder sie war öffentlich installierbar. Erfassen Sie für jede öffentliche Version den genauen Paketnamen, die Versionsnummer, den Veröffentlichungszeitpunkt, die Tarball-URL, den Digest und die Dateien im Archiv. Vergleichen Sie das Paket mit dem verdächtigen Code und dem Build-Ergebnis. Eine npm-Versionsnummer allein verrät nicht, welche Bytes Nutzer erhielten.

Die Registry-Betreiber verfügen möglicherweise über interne Validierungsprotokolle. Halten Sie fest, aus welcher Quelle jeder Schluss stammt. Der [Vorfallsbericht](https://github.com/orgs/community/discussions/208477) beschreibt die Validierung und Sperre von Nub vor der Bereitstellung; für die schädlichen Commits von Zod und Standard Schema nennt er keinen Veröffentlichungsversuch. Eine Release-Seite allein könnte diese Fragen nicht klären.

Falls Ihr Paket über Provenienz verfügt, nutzen Sie diese, um es dem Quellcode und dem erzeugenden Workflow zuzuordnen. Fehlt sie, benennen und untersuchen Sie den Grund. Weder ein fehlendes Badge ist ein Malware-Befund noch ein vorhandenes Badge ein Beweis für sauberen Code. Der [npm-Leitfaden zur Provenienz](https://docs.npmjs.com/viewing-package-provenance/) erklärt, welche Verbindung die Attestierung nachweist.

### 4. Feststellen, wer den Code genutzt haben könnte

Sobald klar ist, ob ein schädliches Paket öffentlich installierbar war, untersuchen Sie die nachgelagerten Spuren. Prüfen Sie Lockfiles, Installationsprotokolle, Caches, Deployment-Manifeste und Build-Protokolle auf die genaue Version und den Digest. Prüfen Sie Git-URLs, direkte Downloads von Quellcode, lokale Tarballs und historische Checkouts gesondert. Eine Sperre in der Registry begrenzt die Verbreitung über die Registry. Sie beantwortet nicht, ob jemand denselben Code auf anderem Weg erhielt.

Unterscheiden Sie **Verfügbarkeit**, **Abruf** und **Ausführung**. Ein öffentliches Paket kann verfügbar gewesen sein, ohne in Ihren Builds vorzukommen. Ein Lockfile kann eine Version nennen, ohne zu beweisen, dass ein Lifecycle-Skript in einer bestimmten Umgebung lief. Auch ein Quellarchiv kann heruntergeladen worden sein, ohne dass der Code ausgeführt wurde. Ihre Mitteilung sollte dort enden, wo die Belege enden.

Sichern Sie Logs, bevor Token-Rotation, das Löschen von Caches oder die Bereinigung des Repositories den zu prüfenden Zeitraum unkenntlich machen. Rotieren oder widerrufen Sie Zugangsdaten zügig, wenn es angezeigt ist. Prüfen Sie aber auch, ob ein alter Zugang tatsächlich nicht mehr funktioniert. Unsere [Checkliste zum Schließen einer Meldung über offengelegte Zugangsdaten](/blog/leaked-credential-report-closure-checklist) trennt die Entfernung offengelegter Zugangsdaten, ihren Widerruf und die Untersuchung. Sie ersetzt nicht die Prüfung des Veröffentlichungswegs.

## Wie sollten Sie das Ergebnis mitteilen und den Veröffentlichungsweg verbessern?

Veröffentlichen Sie einen kurzen Bericht, der die einzelnen Grenzen benennt. Nennen Sie die veränderten Repositories, die ausgeführten und nicht ausgeführten Workflows, jede Version in der Validierung und jedes installierbare Paket. Geben Sie für jede Aussage die Belegquelle an und markieren Sie offene Fragen. Maintainer können sagen: „Wir fanden schädlichen Quellcode im Verlauf, aber keine schädliche npm-Veröffentlichung.“ Das verharmlost den Angriff auf das Repository nicht. War ein schädliches Paket öffentlich verfügbar, muss ein Team dagegen die genauen Versionen und betroffenen Nutzer nennen; „Das Repository wurde kompromittiert“ genügt nicht.

Trennen Sie auch die Daten. Am 21. September kam es zum Angriff auf den Quellcode und zur gesperrten Nub-Einreichung. Laut McDonnells **Ergänzung vom 24. September** stellten seine Projekte danach auf umgebungsgebundenes, manuell ausgelöstes [Staged Publishing](https://github.com/orgs/community/discussions/208477) um. Das war eine Reaktion, nicht der Schutzmechanismus, der Zod zuvor aufgehalten hatte. Laut [npm-Dokumentation zum Staged Publishing](https://docs.npmjs.com/staged-publishing/) erfordert das Einstellen einer Version in die Vorstufe selbst keine Zwei-Faktor-Authentifizierung; erforderlich ist sie für die Freigabe zur öffentlichen Bereitstellung. Ein Trusted-Publisher-Zugang, der nur diese Vorstufe erlaubt, kann direkte Veröffentlichungen verhindern. Die [npm-Hinweise zur Konfiguration](https://github.blog/changelog/2026-09-03-multiple-trusted-publishing-configurations-for-npm/) erlauben jedoch auch mehrere Publisher. Prüfen Sie, ob unbeabsichtigt ein weiterer direkter Weg offen bleibt.

Wenn mehrere Projekte von demselben Vorfall betroffen sind, stimmen Sie die externe Mitteilung ab, ohne ihre Ergebnisse gleichzusetzen. Ein Vorfall nur im Repository, eine gesperrte Einreichung und ein öffentliches Schadpaket erfordern jeweils andere Empfänger und Formulierungen. Unser [Leitfaden zur koordinierten Offenlegung bei mehreren Anbietern](/blog/coordinated-disclosure-multi-vendor-vulnerabilities) behandelt die gesonderte Frage gemeinsamer Zuständigkeiten und Fristen.

Kit kann die Meldung eines Forschers über einen öffentlichen [VDP-Meldeweg](/blog/how-to-set-up-vulnerability-disclosure-program) entgegennehmen, eine verantwortliche Person zuweisen, Eingangsbestätigung und Behebung verfolgen und die Offenlegung dokumentieren. Im CSIRT-Workflow lassen sich Links zu einem Fix oder externen Issue festhalten. Das Engineering-Team muss Git-Verlauf, Workflow-Berechtigung, npm-Status und Paketinhalt weiterhin selbst untersuchen. Kit scannt keine npm-Pakete und bescheinigt nicht, dass eine Veröffentlichung frei von Schadcode ist.

Aus Zod lässt sich eine genaue Lehre für viele Vorfälle ziehen: **Benennen Sie erst die überschrittene Grenze, dann das Ausmaß der Gefährdung.** Stellen Sie Belege zu Repository, Workflow, Registry und Paket auf einer Seite zusammen. Teilen Sie Nutzern anschließend genau mit, was betroffen war, was gestoppt wurde und was ungeklärt bleibt. Für die nächste Meldung finden Sie im [Leitfaden zum Einrichten eines VDP](/blog/how-to-set-up-vulnerability-disclosure-program) einen klaren Ausgangspunkt.