Durcissement de la publication de paquets npm
Objectif
Faire passer un paquet npm de « fonctionne en local » à « honnête, inspectable et sûr à publier ». Prioriser la vérification réelle plutôt que les impressions : contenu de l'archive, contrat d'exécution, CI, paramètres GitHub et hygiène des mises à jour de dépendances.
Commencer ici
- Confirmer le dépôt exact :
pwd
git status --short --branch
git remote -v
- Inspecter la surface du paquet :
cat package.json
ls -la
find .github -maxdepth 3 -type f -print 2>/dev/null
- Détecter le gestionnaire de paquets à partir des fichiers de verrouillage :
package-lock.json-> npmpnpm-lock.yaml-> pnpmyarn.lock-> yarn
Utiliser les commandes npm ci-dessous par défaut, mais les adapter au gestionnaire de paquets détecté si nécessaire.
Contrat du paquet
Faire en sorte que package.json dise la vérité :
name: la cible de publication est intentionnelle et disponibleversion: la version actuelle prévue pour la publicationdescription,keywords: suffisamment utiles pour la recherche dans le registrelicense: présent et correspond àLICENSErepository,homepage,bugs: pointent vers le dépôt public. Si l'URL du dépôt public n'est pas encore définitive, signaler ces champs comme une tâche explicite avant publication plutôt que de les omettre silencieusementtype,exports,main,bin: correspondent à ce que les utilisateurs importeront ou exécuterontfiles: limite l'archive aux artefacts publiableengines.node: la version d'exécution minimale que le paquet prend réellement en chargeprepublishOnlyou équivalent : empêche la publication d'un artefact de construction obsolète
Pour les paquets CLI, confirmer que la cible bin est construite, exécutable le cas échéant, et testée via la commande que les utilisateurs exécuteront.
Règle de version d'exécution minimale
L'engin Node est une promesse, pas une décoration.
- Si
engines.nodeindique>=20, CI doit tester Node 20. - Maintenir
@types/nodesur la même ligne majeure que la version d'exécution minimale prise en charge. - Préférer le verrouillage exact de
@types/nodeà la dernière mise à jour sur cette ligne d'exécution. - Ne pas fusionner les sauts majeurs de
@types/nodeau-dessus de la version d'exécution minimale sauf si vous augmentez égalementengines.node. engines.nodeest indicatif : npm émet seulement un avertissement et ne bloque pas l'installation sauf si l'utilisateur définitengine-strict, donc CI sur la version minimale est ce qui applique réellement la promesse.
Pourquoi : Les types Node sont réservés au développement, mais ils modifient ce que TypeScript autorise. @types/node@26 peut permettre la compilation de code tout en utilisant des API qui échouent pour les utilisateurs sur Node 20.
Commandes de vérification
Pour les projets npm :
npm ci
npm run build --if-present
npm test --if-present
npm audit --omit=dev
npm pack --dry-run
Pour pnpm/yarn, utiliser les commandes équivalentes d'installation/construction/test/audit/empaquetage. Si la prise en charge d'audit diffère, le mentionner plutôt que de faire semblant.
Pour les CLI, exécuter également au moins un test de fumée externe :
node dist/path/to/cli.js --help
npm pack --dry-run
Si possible, installer le fichier .tgz empaqueté dans un répertoire temporaire et exécuter le binaire exactement comme un utilisateur le ferait.
Révision de l'archive
npm pack --dry-run est la source de vérité du registre. Vérifier :
- inclut la sortie construite
- inclut
README.mdetLICENSE - exclut les fichiers source uniquement, fichiers temporaires, tests, documents privés, jeux de données, bases de données locales, secrets
- la taille du paquet est raisonnable
- le chemin
bindu CLI apparaît dans l'archive
Si files est absent, l'ajouter. Ne pas se fier à .gitignore comme politique de publication.
Configuration de base pour GitHub
Ajouter ou vérifier :
.github/workflows/ci.yml.github/dependabot.yml.github/CODEOWNERS.github/pull_request_template.mdSECURITY.md
Utiliser les versions majeures actuellement prises en charge pour les actions appartenant à GitHub. Si Actions avertit qu'un environnement d'exécution d'action est obsolète, mettre à jour la version majeure de l'action après que CI soit passé.
CI devrait généralement inclure :
permissions:
contents: read
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
test:
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@CURRENT_MAJOR
- uses: actions/setup-node@CURRENT_MAJOR
with:
node-version: 20
cache: npm
- run: npm ci
- run: npm run build --if-present
- run: npm test --if-present
Ajuster node-version à la version d'exécution minimale prise en charge du paquet, et remplacer chaque @CURRENT_MAJOR par le tag majeur actuel de l'action (par exemple actions/checkout@v4) — épingler à une version majeure réelle, ne jamais valider le texte de remplacement.
Configuration de base pour Dependabot
Configurer les mises à jour des paquets et des actions GitHub :
version: 2
updates:
- package-ecosystem: npm
directory: /
schedule:
interval: weekly
open-pull-requests-limit: 5
commit-message:
prefix: deps
include: scope
- package-ecosystem: github-actions
directory: /
schedule:
interval: weekly
open-pull-requests-limit: 5
commit-message:
prefix: deps
include: scope
Lorsque Dependabot ouvre des PR immédiatement après le déploiement de la configuration, considérer cela comme une preuve que la configuration est active. Trier les PR ; ne pas fusionner aveuglément tous les contrôles verts.
Tri des PR Dependabot
Utiliser cette règle empirique :
- Correctif/mineur de dépendance d'exécution : fusionner si les contrôles passent et les notes de version sont banales.
- Majeur de dépendance d'exécution : inspecter les notes de version et tester le comportement réel du paquet.
- Majeur d'outil de développement : fusionner si la construction/test/empaquetage passent et que le contrat d'exécution n'est pas modifié.
@types/nodeau-dessus de la version d'exécution minimale : fermer ou remplacer par un épinglage à la version d'exécution minimale.- Majeur d'action GitHub Actions : fusionner si CI passe et qu'il supprime les avertissements d'obsolescence.
Si deux PR sont en conflit dans un petit fichier de configuration, fusionner l'une et mettre à jour l'autre, ou appliquer la seconde directement avec un commentaire clair expliquant qu'elle a été remplacée.
Durcissement du dépôt distant GitHub
Appliquer ce que le dépôt/compte prend en charge, puis vérifier les paramètres réels avec gh ou des lectures API.
Paramètres utiles :
gh repo edit OWNER/REPO \
--delete-branch-on-merge \
--enable-squash-merge \
--enable-merge-commit=false \
--enable-rebase-merge=false \
--allow-update-branch
gh api --method PUT repos/OWNER/REPO/vulnerability-alerts --silent
gh api --method PUT repos/OWNER/REPO/automated-security-fixes --silent
gh api --method PUT repos/OWNER/REPO/actions/permissions/workflow \
-f default_workflow_permissions=read \
-F can_approve_pull_request_reviews=false --silent
Pour les listes d'autorisation d'Actions, privilégier les actions appartenant à GitHub uniquement lorsque le workflow utilise uniquement ces actions. Si des actions tierces sont nécessaires, autoriser explicitement le plus petit ensemble.
Les dépôts publics débloquent généralement davantage de contrôles de sécurité. Les dépôts privés sur les formules gratuites peuvent bloquer :
- la protection de branche ou les ensembles de règles
- l'analyse de code
- l'analyse de secrets
- la protection contre les push
Signaler clairement les blocages. Ne pas signaler les contrôles indisponibles comme activés.
Protection de branche
Lorsque disponible, protéger la branche par défaut avec :
- exiger une pull request avant la fusion
- exiger les contrôles d'état CI
- exiger que les branches soient à jour si cela convient au projet
- exiger la résolution des conversations
- bloquer les push forcés
- bloquer les suppressions
- exiger un historique linéaire si la fusion par squash uniquement est utilisée
Après avoir activé la protection, cesser de pousser directement vers main sauf si l'utilisateur le demande explicitement et comprend la dérogation.
Outils de révision/portail PR
Si vous utilisez un outil de révision/portail automatisé, assurez-vous que le dépôt distant a une branche par défaut/de base avant de demander au portail de créer une PR. Les nouveaux dépôts vides ont souvent besoin d'un premier push main d'abord ; sinon, le portail peut valider et pousser une branche de fonctionnalité mais échouer la création de la PR car il n'y a pas de référence de base.
Liste de contrôle avant publication
Avant npm publish :
npm whoamiaffiche le compte prévunpm view NOM_DU_PAQUET name versionconfirme l'état du nom- CI est verte sur le commit en cours de publication
npm pack --dry-runa été révisé- les exemples d'installation/utilisation du README correspondent au comportement réel
repository,homepageetbugspointent vers le dépôt public réel (ou sont explicitement signalés comme À FAIRE si l'URL n'est pas définitive)- aucune alerte de sécurité ou Dependabot bloquant la publication ne subsiste
- le changement de version est intentionnel, tagué dans git (
git tag vX.Y.Z && git push --tags) et reflété dans un CHANGELOG ou une release GitHub - les paquets scopés (
@scope/nom) sont publiés avec--access public(les paquets scopés par défaut sont restreints/privés)
Pour les comptes utilisant l'authentification web/passkey/2FA :
npm publish --auth-type=web
Privilégier la publication depuis CI avec provenance pour un lien de chaîne d'approvisionnement vérifiable. La provenance nécessite un paquet public, un CI pris en charge tel que GitHub Actions, et l'autorisation id-token: write sur le job :
npm publish --provenance --access public
Après la publication :
npm view NOM_DU_PAQUET version
npm view NOM_DU_PAQUET bin files engines repository --json
npx -y NOM_DU_PAQUET --help
Si publié avec provenance, confirmer que le registre affiche l'attestation de provenance / « Built and signed » sur la page du paquet.
Réponse finale
Signaler uniquement ce qui a été réellement vérifié :
- contrôles du paquet effectués
- résultat de l'archive
- décisions sur les PR de dépendances
- commits/PR créés ou fusionnés
- paramètres GitHub confirmés
- contrôles bloqués par le plan/la visibilité
- tag de version et statut de provenance, si publié
- commande de publication ou vérification après publication si encore détenue par l'utilisateur