Normes de sécurité de la chaîne d'approvisionnement
S'applique à tous les projets. Règles révisées le 11/11/2026 (après les vers Shai-Hulud / Mini Shai-Hulud).
Paramètres par défaut au niveau machine (~/.npmrc)
minimum-release-age=1440 # pnpm uniquement — MINUTES (1440 = 24h)
min-release-age=1 # npm 12+ uniquement — JOURS (1 = 24h)
Les deux gestionnaires de paquets utilisent des noms de clés DIFFÉRENTS ET des unités DIFFÉRENTES. Vérifié le 21/08/2026 en lisant le code source de chaque outil : pnpm calcule Date.now() - minimumReleaseAge * 60 * 1e3 (minutes) ; la définition de configuration de npm pour min-release-age porte hint: '<days>'. Les deux lignes sont requises — l'une ne couvre pas l'autre, et copier 1440 dans la clé npm signifierait 1440 jours et bloquerait pratiquement toutes les installations.
npm affiche Unknown user config "minimum-release-age" lors de l'installation et prévient que cela cessera de fonctionner dans une future version majeure. C'est inévitable tant que la clé se trouve dans ~/.npmrc : pnpm 11 a supprimé l'écriture de configuration globale (pnpm config set --global redirige désormais vers un pnpm-workspace.yaml par projet), donc ~/.npmrc est le seul endroit où une barrière pnpm globale peut vivre. Acceptez l'avertissement ; ne supprimez pas la clé.
Ne pas compter sur celles-ci — vérifiées inactives
-
block-exotic-subdependencies— n'apparaît ni dans le code source de npm ni dans celui de pnpm. N'a jamais rien fait. Les dépendances exotiques doivent être détectées par la révision, pas par cette clé. -
ignore-scripts=true— ce skill prétendait auparavant qu'il était défini globalement. Il n'est pas défini, etnpm config get ignore-scriptsrenvoiefalse, donc les scripts de cycle de vie s'exécutent bien lors de l'installation.Décidé le 21/08/2026 : laissez-le désactivé. Ne l'activez pas. Le postinstall est le principal vecteur de compromission npm, c'est donc une lacune connue et acceptée — pas un oubli à corriger. L'activer globalement casse les builds natifs (esbuild, swc, sharp, node-gyp) dans tous les projets et la panne se manifeste loin de la cause. La barrière d'âge de publication de 24h ci-dessus assume ce risque à la place. Si un projet spécifique a besoin d'isolation des scripts, limitez-la à ce projet — jamais globalement.
Vérifiez ces affirmations par rapport à l'outil plutôt que par rapport à ce fichier — les trois entrées ici étaient erronées lors de l'audit.
Taxonomie des attaques sur la chaîne d'approvisionnement (référence avant audit)
1. Typosquatting / Confusion de dépendance
Nom de paquet presque identique à un populaire, ou un paquet public correspondant à un nom interne avec scope. 454 648 paquets npm malveillants publiés en 2025 seulement.
Détection : npm info <pkg> | grep -E 'created|repository' sur les paquets inconnus. Recouper avec les noms de registres internes.
2. Compromission de compte mainteneur
L'attaquant hameçonne un mainteneur, publie une nouvelle version avec une charge utile post-installation. Shai-Hulud septembre 2025 a touché chalk, debug, ansi-styles (2,6 milliards de téléchargements hebdomadaires). Axios (100M+/semaine) touché en mars 2026 via ingénierie sociale de la RPDC.
Détection : npm diff <pkg>@<dernier-connu-valide> <pkg>@latest -- package.json scripts — vérifiez les nouveaux scripts de cycle de vie ajoutés dans les versions de correctif.
3. Détournement de dépôt / Accaparement d'espace de noms
L'attaquant enregistre un nom d'utilisateur GitHub ou un espace de noms npm abandonné, recrée le dépôt, sert du code malveillant.
Détection : Vérifiez le champ repository de package.json ; signalez les comptes GitHub créés après la date de première publication npm du paquet.
4. Injection dans le pipeline CI/CD (pull_request_target / empoisonnement du cache)
L'attaquant ouvre une PR contre un dépôt avec pull_request_target qui partage un cache avec un job de publication privilégié. Empoisonne le store pnpm ; le prochain workflow de publication fuit le jeton OIDC npm. Vecteur TanStack (nov. 2025, Mini Shai-Hulud mai 2026, 300+ paquets, 16M téléchargements hebdomadaires).
Détection :
grep -r "pull_request_target" .github/workflows/
grep -r "cache" .github/workflows/ | grep -i "pnpm\|npm\|store"
Règle stricte : N'utilisez jamais pull_request_target si le workflow extrait le code de la PR ou partage un cache avec un job privilégié.
5. Empoisonnement d'image Docker
L'attaquant compromet les identifiants Docker Hub et écrase les tags d'image existants (y compris latest) avec des binaires malveillants. Trivy aquasec/trivy tags 0.69.4–0.69.6 et latest empoisonnés en mars 2026. KICS avril 2026 — cinq tags écrasés ; analyse les configurations Terraform/K8s contenant des secrets.
Détection : docker inspect <image> --format '{{.Id}}' vs. un digest épinglé dans la configuration.
Règle stricte : Épinglez toutes les images avec le digest @sha256:. N'utilisez jamais latest pour les images d'outils de sécurité.
6. Re-pointage de tag d'action GitHub
L'attaquant compromet le jeton d'un mainteneur d'action, déplace un tag de version existant pour pointer vers du code malveillant. tj-actions/changed-files CVE-2025-30066 (mars 2025, 23 000+ dépôts) a divulgué tous les secrets CI dans les logs. Mai 2026 : des tags d'actions populaires redirigés vers des commits imposteurs. Détection :
grep -r "uses:.*@v[0-9]" .github/workflows/ # signale les tags (mauvais)
grep -r "uses:.*@[a-f0-9]\{40\}" .github/workflows/ # SHA épinglé (correct)
Règle stricte : Épinglez chaque action tierce à un SHA de commit complet de 40 caractères, pas un tag de version. Les actions/* (première partie) peuvent utiliser des tags de version majeure.
7. Falsification de Lockfile / Contournement d'intégrité (classe PackageGate)
L'attaquant modifie le lockfile pour substituer une version de paquet malveillante ou supprime les hachages d'intégrité pour que la prochaine installation récupère silencieusement une archive différente. Six vulnérabilités zero-day de lockfile documentées en janvier 2026 (Koi Security). Détection :
grep -E '"resolved".*git\+|tarball' package-lock.json pnpm-lock.yaml
grep -v "integrity" pnpm-lock.yaml | grep "resolved"
Règle stricte : Le CI doit utiliser pnpm install --frozen-lockfile ou npm ci — jamais un simple npm install.
8. Ombrage de sous-dépendance
L'attaquant cible une dépendance transitive de niveau 3+. @ctrl/tinycolor était un nœud de propagation de Shai-Hulud.
Détection : npm ls --all 2>/dev/null | grep -E "postinstall|preinstall". Utilisez socket scan sur les PR.
9. Ingénierie sociale pour transfert de mainteneur
L'attaquant se fait passer pour un recruteur ou un partenaire commercial pour obtenir les droits de publication npm. UNC1069 (RPDC) a ciblé le mainteneur d'Axios en mars 2026, a également tenté contre le PDG de Socket, le créateur de Lodash, le responsable de Fastify.
Détection : npm owner ls <pkg> pour les dépendances critiques ; surveillez les nouveaux collaborateurs avant une publication suspecte.
10. Protestware (Sabotage intentionnel par le mainteneur)
Le mainteneur injecte intentionnellement du code destructeur. colors.js v1.4.44–1.4.2 (janv. 2022, boucle infinie), node-ipc v10.1.1–10.1.3 (mars 2022, fichiers supprimés sur les IP russes). Trois nouvelles versions malveillantes de node-ipc en mai 2026.
Détection :
npm diff <pkg>@<prec> <pkg>@<nouveau> -- lib/
grep -r "networkInterfaces\|geoip\|fs.rmdir\|fs.unlink" node_modules/<pkg>/
Épinglez node-ipc, colors, faker à des versions exactes connues comme sûres.
11. Malware auto-réplicatif (wormable)
La charge utile vole les jetons npm/PAT et publie de manière autonome des versions malveillantes d'autres paquets que la victime maintient. Observé pour la première fois avec Shai-Hulud sept. 2025 ; Mini Shai-Hulud mai 2026 (300+ paquets, 16M téléchargements hebdomadaires, npm + PyPI simultanément). Détection des IOC :
find .github/workflows -name "*.js" # bundle.js dans workflows
grep -ri "shai-hulud" . # chaîne du vers
find node_modules -name "data.json" -newer package.json # préparation exfiltration
find node_modules -name "cloud.json"
12. Charges utiles dormantes / conditionnelles à l'environnement
La charge utile ne s'active qu'en présence de AWS_SECRET_ACCESS_KEY, GITHUB_TOKEN, ou CI=true — contourne les scanners en bac à sable.
Détection :
grep -r "process.env.AWS_SECRET\|process.env.GITHUB_TOKEN\|process.env.NPM_TOKEN" node_modules/
Signalez tout paquet lisant ces variables sans raison légitime.
13. Outil de sécurité comme vecteur d'attaque
Les attaquants ciblent spécifiquement les outils de scan de sécurité (Trivy, KICS, Semgrep) car ils s'exécutent avec un accès élevé aux secrets et à la configuration d'infra dans le CI. Trivy et KICS ont tous deux été touchés en mars-avril 2026. Règle stricte : Épinglez toutes les images Docker des outils de sécurité avec un digest, pas un tag. Vérifiez les digests hors bande par rapport aux notes de version du fournisseur.
14. Dépendances non-registre / CDN
Les paquets résolus à partir d'URL git, d'archives GitHub ou de CDN personnalisées (par ex. cdn.sheetjs.com) contournent les barrières de sécurité et la provenance du registre, et ne sont pas non plus couverts par la barrière d'âge de publication — celle-ci ne s'applique qu'aux versions du registre. Certains manquent totalement de hachages d'intégrité. Rien ne les bloque automatiquement ; ils doivent être détectés lors de la révision.
Détection :
grep -E '"resolved".*github\.com|cdn\.' package-lock.json pnpm-lock.yaml
grep -E '"version":\s*"(git\+|https?:)' package.json
Hygiène npm/pnpm par projet
- Engagez les lockfiles. Le CI doit utiliser
pnpm install --frozen-lockfile(ounpm ci). Jamais de simplenpm installdans le CI. - Épinglez
packageManagerdanspackage.jsonpour verrouiller la version de pnpm/npm. - Épinglez les paquets dans les espaces de noms compromis à des versions exactes :
@tanstack/*,posthog-node,@mistralai/*,@langchain/mistralai,@ctrl/*,ngx-*,rxnt-*,@zapier/*,@postman/*,@uipath/*,agent-mcp,@semgrep/*,node-ipc,colors,faker. - Pas de versions avec le joker
"*"danspackage.json. - Pas d'URL
pkg.pr.new— ce sont des builds d'aperçu de PR éphémères, pas des artefacts immuables. - Pour les projets npm/yarn, reflétez les mêmes barrières npmrc dans leur
.npmrclocal. - Préférez pnpm pour les nouveaux projets. Ne migrez pas les projets fonctionnels uniquement pour la sécurité.
Durcissement de GitHub Actions
Règles strictes
- Ne jamais utiliser
pull_request_targetsi le workflow extrait le code de la PR, exécute install/build/test à partir du code de la PR, ou partage un cache avec un workflow privilégié. pull_request_targetn'est acceptable que pour lire les métadonnées de la PR (labels, commentaires, titre) sans extraction.- Ne partagez pas les caches entre les jobs déclenchés par PR et les jobs de publication/de release.
- Ne jamais utiliser
curl | bashouwget | shcontre une branche mutable (@main,@master) dans le CI. Épinglez toujours les scripts d'installation à une release spécifique et vérifiez une somme de contrôle.
Gardes workflow_run
Lorsque vous utilisez workflow_run pour enchaîner un job privilégié après un workflow parent, le if: DOIT affirmer tout ceci :
if: >
github.event_name == 'workflow_dispatch' ||
(github.event.workflow_run.conclusion == 'success' &&
github.event.workflow_run.head_branch == 'main' &&
github.event.workflow_run.head_repository.full_name == github.repository &&
github.event.workflow_run.event == 'push')
Épinglage d'action
- Épinglez chaque action tierce à un SHA de commit complet de 40 caractères, pas
@v1/@v6. - Ajoutez un commentaire avec le tag lisible pour l'auditabilité :
docker/login-action@65d0a9e # v3 - Les
actions/*(première partie) peuvent utiliser des tags de version majeure. - Obtenez les SHA actuels :
gh api repos/<owner>/<action>/git/ref/tags/<tag> - Automatisez avec :
npx pin-github-action .github/workflows/*.yml - Réduisez les
permissions:par job. Par défaut,contents: read. Ajoutezid-token: writeuniquement sur l'étape de publication.
Workflows de publication npm
- Exigez la 2FA sur le compte npm :
npm profile enable-2fa auth-and-writes. - Utilisez la publication de confiance (OIDC) au lieu de secrets
NPM_TOKENà longue durée de vie. - Ajoutez
--provenance(ouprovenance: true) pour que les consommateurs puissent vérifier l'origine. - Publiez uniquement à partir d'un
release.ymldéclenché par un push de tag surmain. Jamais à partir de workflows déclenchés par PR.
Hygiène Docker / conteneur
- Épinglez chaque
FROMà un digest :FROM node:22-slim@sha256:<hash> - N'utilisez jamais d'images
latestou sans tag dans les Dockerfiles de production ou le CI. - Les images d'outils de sécurité (Trivy, KICS, Semgrep, Snyk, Grype, Hadolint) sont les plus à risque — épinglez-les en premier.
- Obtenez les digests actuels :
docker buildx imagetools inspect node:22-slim --format '{{json .Manifest.Digest}}' - Pour les images de registre privé : publiez avec des tags immuables + digests.
Outils de développement et fichiers de configuration
- Ne stockez jamais de jetons API ou de secrets en texte clair dans
~/.cursor/mcp.json,~/.claude/settings.json, ou fichiers de configuration similaires. Utilisez des variables d'environnement ou un trousseau. - Épinglez les paquets de serveur MCP à des versions exactes (
[email protected], pasmcp-remote@latest). - Vérifiez
~/.vscode/tasks.jsonet~/.cursor/pour des hooks d'exécution automatique inattendus après tout incident de chaîne d'approvisionnement.
Détection / Surveillance
- Installez zizmor ou
snyk-labs/github-actions-scannerdans le CI pour tout dépôt avec des workflows. - Ajoutez l'application GitHub socket.dev sur tout dépôt qui publie des paquets (toute bibliothèque publiable).
- Abonnez-vous au flux RSS des avis de sécurité npm et aux flux de malwares PyPI.
Liste de contrôle IOC rapide
# IOC Shai-Hulud / vers
grep -ri "shai-hulud" .
find .github/workflows -name "*.js"
find node_modules -name "cloud.json"
# Modèles de workflow compromis
grep -r "pull_request_target" .github/workflows/
grep -r "curl.*|.*sh\|wget.*|.*sh\|base64.*eval" .github/workflows/
# Actions non épinglées
grep -r "uses:.*@v[0-9]" .github/workflows/
# Dépendances exotiques de lockfile
grep -E '"resolved".*git\+|cdn\.' package-lock.json pnpm-lock.yaml
grep -v integrity pnpm-lock.yaml | grep resolved
# Docker
grep -r "FROM.*:latest\|FROM.*[^@]$" Dockerfile* docker-compose*.yml