12k
All articles

So verhindern Sie, dass die falschen Dateien auf npm landen

Verhindern Sie, dass npm unerwünschte Dateien oder Geheimnisse veröffentlicht. Erfahren Sie, wie .npmignore, .gitignore und files in package.json zusammenwirken, und prüfen Sie mit npm pack --dry-run.

OpenReplay Team
OpenReplay Team
So verhindern Sie, dass die falschen Dateien auf npm landen

Sobald Ihr Paket eine .npmignore-Datei enthält, berücksichtigt npm die .gitignore nicht mehr, wenn es festlegt, welche Dateien aus dem Tarball ausgeschlossen werden. Das gilt auch dann, wenn die .npmignore-Datei leer ist.

Die meisten bemerken das erst, wenn ein Tarball deutlich größer ausfällt als erwartet oder wenn eine Dotfile, die nie veröffentlicht werden sollte, plötzlich in der Registry auftaucht. Die Ignore-Regeln sahen korrekt aus, und sie waren auch korrekt, bis jemand eine zweite Ignore-Datei hinzugefügt hat.

Dieser Artikel behandelt die Fallstricke, durch die falsche Dateien in Ihr Paket gelangen: den .gitignore-Fallback, das Zusammenspiel von files und .npmignore, die festen Ein- und Ausschlusslisten von npm sowie das richtige Vorgehen, wenn eine fehlerhafte Version bereits veröffentlicht ist. Vorausgesetzt wird, dass Sie How to Create and Publish an npm Package gelesen haben.

Das Wichtigste in Kürze

  • Eine .npmignore-Datei, selbst eine leere, deaktiviert sämtliche .gitignore-Regeln, die npm sonst auf Ihren Tarball anwenden würde.
  • npm liest .npmignore und .gitignore in jedem Unterverzeichnis. Eine tief im Verzeichnisbaum liegende Ignore-Datei kann also verändern, was veröffentlicht wird.
  • Die integrierten Ausschlüsse von npm umfassen weder .env-Dateien noch private Schlüssel oder Zertifikate.
  • Gelangt ein Secret in die Registry, rotieren Sie es als Erstes. Unpublish holt bereits heruntergeladene Kopien nicht zurück.
  • Provenance dokumentiert, aus welchem Repository, welchem Commit und welchem Workflow eine Version gebaut wurde. Sie belegt nicht, dass der Code sicher ist.

Die npmignore-Falle: Eine neue Datei hebelt alle gitignore-Regeln aus

npm verwendet entweder .npmignore oder .gitignore, um zu bestimmen, was aus einem Paket ausgeschlossen wird, niemals beide. Die Regeln von npm zum Ausschließen von Dateien aus einem Paket sind kurz: Gibt es keine .npmignore, wendet npm die .gitignore an. Sobald eine .npmignore existiert, spielt die .gitignore keine Rolle mehr. Die beiden Dateien werden nie zusammengeführt.

Der typische Auslöser ist der Build-Output. dist/ steht in der .gitignore, also lässt npm das Verzeichnis aus dem Tarball weg. Sie legen eine .npmignore an, damit dist/ mitgeliefert wird. Jetzt wird dist/ ausgeliefert, aber auch alles andere, was bisher durch die .gitignore verborgen war: .env.local, coverage/ und test/fixtures/.

Das Root-Verzeichnis ist nicht der einzige Ort, an dem npm sucht. Beide Arten von Ignore-Dateien werden in jedem Verzeichnis innerhalb des Pakets ausgewertet. Eine verirrte .npmignore in einem verschachtelten Ordner kann also beeinflussen, was veröffentlicht wird, ohne dass jemand die Konfiguration im Root anfasst.

Vorher und nachher: Die Dateiliste ändert sich

Das folgende Skript erzeugt ein Paket, in dem die .gitignore die gesamte Filterung übernimmt:

mkdir pkg-trap && cd pkg-trap
npm init -y >/dev/null
mkdir -p src dist test/fixtures coverage
echo 'module.exports = 1' > src/index.js
echo 'module.exports = 1' > dist/index.js
echo 'API_KEY=replace-me' > .env.local
echo '{}' > test/fixtures/big.json
echo 'x' > coverage/lcov.info
printf 'dist/\n.env.local\ncoverage/\ntest/fixtures/\n' > .gitignore

npm pack --dry-run

npm listet Folgendes auf (nur Pfade, gekürzt):

package.json
src/index.js

dist/ fehlt. Also fügen Sie eine einzeilige .npmignore hinzu, um den Build auszuliefern und den Quellcode wegzulassen:

echo 'src/' > .npmignore
npm pack --dry-run
  package.json
- src/index.js
+ dist/index.js
+ .env.local
+ coverage/lcov.info
+ test/fixtures/big.json

Die Lösung besteht darin, jede weiterhin benötigte Regel in die .npmignore zu übernehmen:

printf 'src/\n.env*\ncoverage/\ntest/\n*.map\n' > .npmignore

files vs. .npmignore: Allowlist oder Denylist

Das Feld files in der package.json ist eine Allowlist, die .npmignore eine Denylist. Wenn Sie beides verwenden, gibt keine einzelne Datei mehr Auskunft darüber, was Ihr Paket enthält.

MechanismusModellEine neue Datei, die Sie übersehen
filesAllowlistBleibt draußen
.npmignoreDenylistWird ausgeliefert
.gitignore (Fallback)DenylistWird ausgeliefert, sofern sie nicht ebenfalls in der .gitignore steht

Genau an der Überschneidung von files und .npmignore beginnen die Probleme. Laut der package.json-Dokumentation kann eine .npmignore im Root des Pakets nichts entfernen, was über files hinzugefügt wurde. Eine .npmignore in einem Unterordner hingegen schon:

{ "name": "my-lib", "version": "1.0.0", "main": "dist/index.js", "files": ["dist"] }
# dist/.npmignore
*.map

Hier nimmt files das Verzeichnis dist/ auf, und die verschachtelte Ignore-Datei filtert die Sourcemaps daraus wieder heraus. Zwei Dateien definieren nun gemeinsam eine Liste. Build-Tools, die eigene Ausgabeverzeichnisse schreiben, verschärfen das Problem. Prüfen Sie daher, was in dist/ landet, wenn Sie unserem pnpm-Leitfaden folgen. Die einfachste Regel lautet: Entscheiden Sie sich für einen Mechanismus und löschen Sie den anderen.

Was npm immer einschließt und immer ausschließt

Manche Dateien werden unabhängig von Ihrer Konfiguration ein- oder ausgeschlossen. Laut der package.json-Dokumentation schließt npm immer die package.json, die README, LICENSE bzw. LICENCE, die in main angegebene Datei sowie alle in bin aufgeführten Dateien ein. Immer ausgeschlossen werden Verzeichnisse der Versionsverwaltung, .npmrc, node_modules und Editor-Artefakte. Secrets stehen nicht auf dieser Liste.

Immer eingeschlossenImmer ausgeschlossen (nicht abschließend)Nur ausgeschlossen, wenn Sie es festlegen
package.json, README (und Varianten), LICENSE / LICENCE, die main-Datei, die bin-Datei(en).git, .npmrc, node_modules (außer gebündelte Abhängigkeiten), .DS_Store, npm-debug.log, config.gypi, .*.swp, *.orig, package-lock.json.env, .env.local, *.pem, *.key, Fixtures, Coverage

Sichern Sie die rechte Spalte mit einer Prüfung ab:

npm pack --dry-run 2>&1 | grep -E '\.env|\.pem$|\.key$' && { echo "STOP: secrets in tarball"; exit 1; }

Führen Sie vor jeder Veröffentlichung npm pack —dry-run aus

npm pack --dry-run gibt die exakte Liste der Dateien aus, die npm veröffentlichen würde, ohne einen Tarball zu schreiben. Damit fängt der Befehl alle oben beschriebenen Fallstricke ab. Führen Sie ihn nach jeder Änderung an einer Ignore-Datei, am Feld files oder an Ihrer Build-Konfiguration aus, und bauen Sie ihn in Ihre CI vor npm publish ein. Den grundlegenden Workflow beschreibt How to Create and Publish an npm Package.

npm pack --dry-run

Was tun, wenn die falschen Dateien bereits veröffentlicht wurden?

Gelangt ein Secret in die Registry, rotieren Sie es als Erstes und betrachten Sie es dauerhaft als öffentlich. Das Zurückziehen einer Version entfernt keine Kopien, die bereits heruntergeladen oder gespiegelt wurden. Bereinigen Sie anschließend die Registry:

  1. Rotieren Sie die Zugangsdaten beim jeweiligen Anbieter.
  2. Entfernen oder kennzeichnen Sie die fehlerhafte Version. Gemäß der Unpublish-Richtlinie von npm können Sie eine Version innerhalb von 72 Stunden nach der Veröffentlichung zurückziehen, sofern kein anderes öffentliches Paket davon abhängt. Nach Ablauf dieses Zeitfensters lassen sich nur Pakete zurückziehen, die die Kriterien der Richtlinie erfüllen. Andernfalls verwenden Sie npm deprecate.
  3. Veröffentlichen Sie einen bereinigten Patch. Eine bereits verwendete Versionsnummer kann nie wieder vergeben werden, auch nicht nach einem Unpublish.
# Within 72 hours, subject to npm's unpublish policy
npm unpublish my-lib@1.4.2

# Otherwise
npm deprecate my-lib@1.4.2 "Contains a leaked file. Upgrade to 1.4.3."

# Fix the ignore rules, confirm, republish
npm pack --dry-run
npm version patch
npm publish

Provenance belegt die Herkunft, nicht die Sicherheit

Ein npm-Provenance-Statement dokumentiert, aus welchem Repository, welchem Commit und welchem CI-Workflow eine Paketversion gebaut wurde. Es belegt weder, dass der Code in diesem Commit sicher ist, noch dass der Tarball ausschließlich die beabsichtigten Dateien enthält. In GitHub Actions benötigt der Workflow id-token: write:

name: publish
on:
  release:
    types: [published]
permissions:
  contents: read
  id-token: write
jobs:
  publish:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v7
      - uses: actions/setup-node@v7
        with:
          node-version: 24
          registry-url: https://registry.npmjs.org
      - run: npm ci
      - run: npm pack --dry-run
      - run: npm publish

Dieser Workflow setzt voraus, dass Sie Trusted Publishing eingerichtet haben. Trusted Publishing erfordert npm CLI 11.5.1 oder neuer sowie Node 22.14.0 oder neuer und erzeugt Provenance automatisch. Der Job benötigt daher weder --provenance noch NODE_AUTH_TOKEN. Wenn Sie stattdessen mit einem Token veröffentlichen, erfordert npm publish --provenance npm 9.5.0 oder neuer. In beiden Fällen muss das Feld repository in der package.json exakt mit dem veröffentlichenden Repository übereinstimmen (unter Beachtung der Groß- und Kleinschreibung), und das Repository muss öffentlich sein. Falls npm ci an Peer-Konflikten scheitert, lesen Sie How to Fix npm ERESOLVE Errors. Zur Einrichtung von 2FA und Trusted Publishing siehe npm Security Best Practices.

Fazit

Die Ignore-Regeln von npm schlagen nicht mit einer Fehlermeldung fehl. Schon eine zusätzliche Datei ändert stillschweigend, welche Dateien veröffentlicht werden. Entscheiden Sie sich entweder für files oder für .npmignore und löschen Sie das jeweils andere, halten Sie Ignore-Dateien aus Unterverzeichnissen heraus, und schalten Sie in Ihrer CI vor jedes npm publish ein npm pack --dry-run samt grep-Prüfung auf Secrets.

FAQs

Was ist der Unterschied zwischen npm pack --dry-run und npm publish --dry-run?

Beide zeigen an, welche Dateien in den Tarball aufgenommen würden, ohne etwas hochzuladen. Mit beiden lassen sich also Fehler in Ignore-Dateien erkennen. npm pack --dry-run ist die einfachere Prüfung des Dateiinhalts. npm publish --dry-run simuliert den eigentlichen Publish-Befehl. In aktuellen npm-Versionen fragt er zudem bei der Registry nach, ob die Version bereits existiert, und schlägt in diesem Fall fehl. Eine fehlende Anmeldung erzeugt jedoch nur eine Warnung. Ein erfolgreicher Durchlauf garantiert nicht, dass der Upload gelingt, denn Anmeldung und 2FA werden erst beim tatsächlichen Upload geprüft.

Wie prüfe ich, welche Dateien in einer bereits veröffentlichten Version enthalten sind?

Führen Sie npm pack mit einer Registry-Angabe aus, zum Beispiel npm pack my-lib@1.4.2. npm lädt den veröffentlichten Tarball herunter, speichert ihn im aktuellen Verzeichnis als my-lib-1.4.2.tgz und gibt die enthaltenen Dateinamen aus. Den Inhalt listen Sie mit tar -tzf my-lib-1.4.2.tgz auf. Die URL des Tarballs zur Verwendung mit curl oder einem anderen Tool erhalten Sie mit npm view my-lib@1.4.2 dist.tarball.

Verhindert npm deprecate, dass eine Version mit einer geleakten Datei heruntergeladen wird?

Nein. npm deprecate fügt dem Registry-Eintrag eine Warnmeldung hinzu, die npm allen anzeigt, die diese Version installieren. Der Tarball bleibt jedoch weiterhin herunterladbar. Wer die exakte Version pinnt, erhält die geleakte Datei nach wie vor. Nur ein Unpublish entfernt die Version aus der Registry, und das ist durch die Unpublish-Richtlinie von npm eingeschränkt. Um eine Deprecation später wieder aufzuheben, führen Sie npm deprecate erneut mit einem leeren String als Meldung aus.

Kann das Feld files in der package.json Glob-Muster verwenden?

Ja. Sie können einfache Dateinamen, Ordner oder Glob-Muster wie * und **/* angeben. Ein Ordnereintrag schließt den gesamten Inhalt des Ordners ein. Die Syntax entspricht der von .gitignore, nur mit umgekehrter Wirkung: Ein passendes Muster nimmt eine Datei in den Tarball auf, statt sie auszuschließen. Die Dateien, die npm immer einschließt, etwa package.json, README, LICENSE und die main-Datei, werden ausgeliefert, unabhängig davon, ob sie im Array stehen.

DevTools for the frontend

Gain Debugging Superpowers

Unleash the power of session replay to reproduce bugs, track slowdowns and uncover frustrations in your app. Get complete visibility into your frontend with OpenReplay — the most advanced open-source session replay tool for developers.

Star on GitHub12k

We use cookies to improve your experience. By using our site, you accept cookies.