Das Problem
Wer ein npm-Token oder den Rechner eines Maintainers hat, aber nicht die CI-Pipeline des Projekts, veröffentlicht von Hand. Die Schadversion hat dann keine Provenance, frühere Versionen schon. So war es im März 2026 bei axios 1.14.1 und 0.30.4.
pnpm kann eine solche Version mit trustPolicy: no-downgrade bei der Installation
ablehnen. npm kann das nicht:
- npm/cli#9242 fordert dieselbe Einstellung, hat 68 Reaktionen und ist seit April 2026 offen.
- PR #9902 setzt sie um, ruht seit September, und der Ansatz ist umstritten.
- npq erkennt eine Provenance-Regression,
deckt laut README aber nur die direkten Abhängigkeiten aus der
package.jsonab und liest keine Lockfiles.
Wie das Werkzeug arbeitet
npm-trust-downgrade liest die
package-lock.json, holt die Registry-Metadaten jedes Pakets und vergleicht jede
gesperrte Version mit der Historie des Pakets. Es gibt drei Stufen: Trusted
Publisher, Provenance-Attestierung und keine. Das Werkzeug hat keine
Laufzeit-Abhängigkeiten, was bei einem Prüfwerkzeug dieser Art eine Rolle spielt.
Gemeldet wird eine Version, wenn ein Release stärkere Nachweise hat, das sowohl früher veröffentlicht als auch in der Semver-Ordnung kleiner ist. pnpm vergleicht nur nach Datum. Die Semver-Bedingung folgt einem Einwand aus der Diskussion zu PR #9902: Ein Wartungsrelease einer alten Linie soll nicht an einer neueren Linie gemessen werden, die Provenance erst später eingeführt hat.
pino@9.13.1 hat eine transitive Abhängigkeit, an der man die Ausgabe sieht:
$ npx --yes github:Jwrede/npm-trust-downgrade#v0.1.0
DOWNGRADE slow-redact@0.3.2
has: none; 0.3.1 had: trusted publisher
0.3.2 published 2025-10-11 by matteo.collina
0.3.1 published 2025-10-03 by GitHub Actions
13 checked, 1 finding(s), 0 not checked
Das ist eine Aufforderung zur Prüfung. Die beiden Publisher-Zeilen zeigen, dass
ein Maintainer von Hand veröffentlicht hat, und nichts daran besagt, dass das
Release schädlich ist. Mit --since origin/main prüft das Werkzeug nur, was ein
Pull Request ändert, und mit --exclude hält man einen geprüften Befund fest.
Außerdem meldet es Versionen, auf die eine Lockfile noch verweist, die aber aus
der Registry entfernt wurden.
Tests
Die Tests führen die CLI gegen eine lokale Mock-Registry mit gekürzten Kopien echter Metadaten aus. Dazu gehört die axios-Historie, rekonstruiert für die Stunden, in denen die Schadversion online war. Eine zweite Testreihe erzeugt Lockfiles mit echtem npm und vergleicht die Urteile mit echtem pnpm. Das CI führt beides aus, auf Node 20, 22 und 24 (die Live-Tests auf 22).
Bei einer Lockfile aus 43 verbreiteten Paketen mit 2815 verschiedenen Versionen
meldete das Werkzeug 2 Versionen, die Datumsregel 5. Die drei übrigen,
@octokit/endpoint@9.0.6, ua-parser-js@1.0.41 und
css-declaration-sorter@6.4.1, sind Releases einer älteren Linie, und pnpm
10.34.9 lehnte jede davon ab. Der Lauf dauerte 73 Sekunden. Die Stichprobe
enthält keine bekannte Schadversion. Sie misst also, wie oft die Prüfung eine
Durchsicht verlangt, und sagt nichts über Erkennungsraten.
Grenzen
- Provenance ist ein Signal, und es gibt ein ernsthaftes Argument, sie gar nicht als Sicherheitsprüfung zu verwenden. Maintainer können sie aus legitimen Gründen aufgeben, und auch das wird gemeldet.
- Wer Zugriff auf die CI-Pipeline hat, veröffentlicht mit vollen Nachweisen und wird nicht erkannt. Ein Paket, das nie Nachweise hatte, kann nicht herabgestuft werden.
- Die Semver-Bedingung hat einen Preis. Ein neues Release unterhalb der ersten attestierten Version kommt durch, wo pnpm es ablehnen würde.
- Attestierungen werden nicht kryptografisch geprüft. Das macht
npm audit signatures. - Es ist ein eigener Schritt und hält
npm installnur auf, wenn die Pipeline ihn vorher ausführt. Gelesen werden nur npm-Lockfiles, und Anfragen enthalten keine Zugangsdaten für private Registries.
Der Code liegt unter github.com/Jwrede/npm-trust-downgrade.