12k
All articles

Comment éviter de publier les mauvais fichiers sur npm

Évitez la publication de fichiers ou secrets indésirables sur npm. Comprenez .npmignore, .gitignore et le champ files de package.json, puis vérifiez avec npm pack --dry-run.

OpenReplay Team
OpenReplay Team
Comment éviter de publier les mauvais fichiers sur npm

Si votre package contient un fichier .npmignore, npm cesse de lire .gitignore pour déterminer ce qu’il faut exclure de l’archive (tarball). C’est le cas même lorsque le fichier .npmignore est vide.

La plupart des développeurs le découvrent lorsqu’une archive se révèle bien plus volumineuse que prévu, ou lorsqu’un fichier caché (dotfile) qu’ils n’avaient jamais eu l’intention de publier apparaît sur le registre. Les règles d’exclusion semblaient correctes, et elles l’étaient, jusqu’à ce que quelqu’un ajoute un second fichier d’exclusion.

Cet article passe en revue les pièges qui font atterrir les mauvais fichiers dans votre package : le repli sur .gitignore, l’interaction entre files et .npmignore, les listes fixes d’inclusion et d’exclusion de npm, et la marche à suivre lorsqu’une version défectueuse est déjà en ligne. Il suppose que vous avez lu How to Create and Publish an npm Package.

Points clés

  • Un fichier .npmignore, même vide, désactive toutes les règles .gitignore que npm appliquerait autrement à votre archive.
  • npm lit les fichiers .npmignore et .gitignore de chaque sous-répertoire : un fichier d’exclusion situé profondément dans l’arborescence peut donc modifier ce qui est publié.
  • Les exclusions intégrées de npm ne couvrent ni les fichiers .env, ni les clés privées, ni les certificats.
  • Si un secret atteint le registre, commencez par le renouveler. Dépublier ne rappelle pas les copies déjà téléchargées.
  • La provenance indique quel dépôt, quel commit et quel workflow ont produit une version. Elle ne prouve pas que le code est sûr.

Le piège de npmignore : un seul nouveau fichier annule toutes les règles de gitignore

npm utilise soit .npmignore, soit .gitignore pour déterminer ce qu’il faut exclure d’un package, jamais les deux. Les règles de npm pour exclure des fichiers d’un package sont brèves. En l’absence de .npmignore, npm applique .gitignore. Dès qu’un .npmignore existe, .gitignore n’est plus pris en compte. Les deux fichiers ne sont jamais fusionnés.

Le déclencheur habituel est le résultat du build. dist/ figure dans .gitignore, donc npm l’exclut de l’archive. Vous ajoutez un .npmignore pour que dist/ soit publié. dist/ est désormais publié, mais aussi tout ce que .gitignore masquait jusque-là : .env.local, coverage/ et test/fixtures/.

Le dossier racine n’est pas le seul endroit où npm regarde. Il prend en compte les deux types de fichiers d’exclusion dans n’importe quel dossier du package : un .npmignore égaré dans un sous-dossier peut donc modifier ce qui est publié sans que personne ne touche à la configuration racine.

Avant et après : la liste des fichiers change

Le script ci-dessous crée un package dans lequel .gitignore assure tout le filtrage :

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 affiche la liste suivante (chemins uniquement, version abrégée) :

package.json
src/index.js

dist/ est absent, vous ajoutez donc un .npmignore d’une ligne pour publier le build et écarter les sources :

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

La solution consiste à recopier dans .npmignore toutes les règles dont vous avez encore besoin :

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

files ou .npmignore : liste blanche ou liste noire

Le champ files de package.json est une liste blanche (allow-list), tandis que .npmignore est une liste noire (deny-list). Si vous utilisez les deux, aucun fichier ne vous indique à lui seul ce que contient votre package.

MécanismeModèleUn nouveau fichier oublié
filesListe blancheReste exclu
.npmignoreListe noireEst publié
.gitignore (repli)Liste noireEst publié, sauf s’il est aussi ignoré par Git

C’est dans le chevauchement entre files et .npmignore que les problèmes commencent. Selon la documentation de package.json, un .npmignore placé à la racine du package ne peut rien retirer de ce que files a ajouté. Un .npmignore situé dans un sous-dossier, en revanche, le peut :

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

Ici, files inclut dist/, et le fichier d’exclusion imbriqué en retire les sourcemaps. Deux fichiers définissent désormais une seule liste. Les outils de build qui génèrent leurs propres dossiers de sortie aggravent la situation : vérifiez donc ce qui se retrouve dans dist/ si vous suivez notre guide sur pnpm. La règle la plus simple consiste à choisir un seul mécanisme et à supprimer l’autre.

Ce que npm inclut et exclut systématiquement

Certains fichiers sont inclus ou exclus quelle que soit votre configuration. Selon la documentation de package.json, npm inclut toujours package.json, le README, le fichier LICENSE ou LICENCE, le fichier désigné par main et tous les fichiers désignés par bin. npm exclut toujours les dossiers de gestion de versions, .npmrc, node_modules et les fichiers parasites des éditeurs. Les secrets ne figurent pas sur cette liste.

Toujours inclusToujours exclus (liste non exhaustive)Non exclus sauf indication explicite
package.json, README (et ses variantes), LICENSE / LICENCE, le fichier main, le ou les fichiers bin.git, .npmrc, node_modules (sauf dépendances embarquées), .DS_Store, npm-debug.log, config.gypi, .*.swp, *.orig, package-lock.json.env, .env.local, *.pem, *.key, fixtures, couverture de code

Ajoutez un garde-fou pour la colonne de droite :

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

Exécutez npm pack —dry-run avant chaque publication

npm pack --dry-run affiche la liste exacte des fichiers que npm publierait, sans écrire d’archive : cette commande permet donc de détecter tous les pièges décrits ci-dessus. Exécutez-la après toute modification d’un fichier d’exclusion, du champ files ou de votre configuration de build, et intégrez-la à votre CI avant npm publish. Le workflow de base est décrit dans How to Create and Publish an npm Package.

npm pack --dry-run

Que faire si vous avez déjà publié les mauvais fichiers

Si un secret atteint le registre, commencez par le renouveler et considérez-le comme définitivement public. Dépublier une version ne supprime pas les copies déjà téléchargées ou répliquées sur des miroirs. Ensuite, nettoyez le registre :

  1. Renouvelez l’identifiant auprès de son fournisseur.
  2. Supprimez ou signalez la version défectueuse. Selon la politique de dépublication de npm, vous pouvez dépublier une version dans les 72 heures suivant sa publication, à condition qu’aucun autre package public n’en dépende. Passé ce délai, seuls les packages répondant aux critères de cette politique peuvent être dépubliés. Sinon, utilisez npm deprecate.
  3. Publiez un correctif propre. Un numéro de version déjà utilisé ne peut jamais être réutilisé, même après dépublication.
# 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

La provenance prouve l’origine, pas la sécurité

Une attestation de provenance npm indique quel dépôt, quel commit et quel workflow de CI ont produit une version d’un package. Elle ne démontre ni que le code de ce commit est sûr, ni que l’archive contient uniquement les fichiers prévus. Dans GitHub Actions, le workflow a besoin de la permission 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

Ce workflow suppose que vous avez configuré la publication de confiance (trusted publishing). La publication de confiance nécessite npm CLI 11.5.1 ou version ultérieure et Node 22.14.0 ou version ultérieure. Elle génère automatiquement la provenance : le job n’a donc besoin ni de --provenance ni de NODE_AUTH_TOKEN. Si vous publiez plutôt à l’aide d’un jeton, npm publish --provenance nécessite npm 9.5.0 ou version ultérieure. Dans les deux cas, le champ repository de package.json doit correspondre exactement au dépôt de publication (en respectant la casse), et ce dépôt doit être public. Si npm ci échoue à cause de conflits de dépendances pairs (peer dependencies), consultez How to Fix npm ERESOLVE Errors. Pour la configuration de la 2FA et de la publication de confiance, consultez npm Security Best Practices.

Conclusion

Les règles d’exclusion de npm n’échouent jamais avec une erreur. L’ajout d’un seul fichier modifie silencieusement les fichiers publiés. Choisissez soit files, soit .npmignore, et supprimez l’autre ; évitez les fichiers d’exclusion dans les sous-répertoires ; et placez npm pack --dry-run accompagné d’une recherche de secrets par grep avant chaque npm publish dans votre CI.

FAQ

Quelle est la différence entre npm pack --dry-run et npm publish --dry-run ?

Les deux commandes indiquent quels fichiers seraient intégrés à l'archive sans rien téléverser : l'une comme l'autre permet donc de repérer les erreurs liées aux fichiers d'exclusion. npm pack --dry-run est la vérification la plus simple du contenu des fichiers. npm publish --dry-run simule la commande de publication elle-même. Dans les versions actuelles de npm, elle interroge aussi le registre pour savoir si cette version existe déjà et échoue si c'est le cas, mais une absence de connexion ne produit qu'un avertissement. Une exécution réussie ne garantit pas que le téléversement fonctionnera, car votre connexion et votre 2FA ne sont réellement vérifiées qu'au moment du téléversement.

Comment vérifier quels fichiers contient une version déjà publiée ?

Exécutez npm pack avec une spécification de registre, par exemple npm pack my-lib@1.4.2. npm récupère l'archive publiée, l'enregistre dans le répertoire courant sous le nom my-lib-1.4.2.tgz et affiche la liste de ses fichiers. Pour lister son contenu, utilisez tar -tzf my-lib-1.4.2.tgz. Pour obtenir l'URL de l'archive afin de l'utiliser avec curl ou un autre outil, exécutez npm view my-lib@1.4.2 dist.tarball.

npm deprecate empêche-t-il le téléchargement d'une version contenant un fichier divulgué ?

Non. npm deprecate ajoute un message d'avertissement à l'entrée du registre, que npm affiche à quiconque installe cette version, mais l'archive reste téléchargeable. Toute personne qui épingle cette version exacte récupère toujours le fichier divulgué. Seule la dépublication retire la version du registre, et elle est encadrée par la politique de dépublication de npm. Pour supprimer ultérieurement un avertissement de dépréciation, exécutez à nouveau npm deprecate avec une chaîne vide comme message.

Le champ files de package.json accepte-t-il les motifs glob ?

Oui. Vous pouvez y indiquer de simples noms de fichiers, des dossiers ou des motifs glob tels que * et **/*, et une entrée de dossier inclut tout son contenu. La syntaxe est celle de .gitignore, mais inversée : un motif correspondant ajoute un fichier à l'archive au lieu de l'exclure. Les fichiers que npm inclut systématiquement, comme package.json, le README, la LICENSE et le fichier main, sont publiés qu'ils figurent ou non dans le tableau.

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.