Publier des packages npm en toute sécurité avec GitHub Actions
10 novembre 2020
0 minutes de lectureGitHub Actions gagne en popularité depuis que GitHub a annoncé sa disponibilité générale pour tous les développeurs et tous les dépôts de la plateforme GitHub. Les limites de débit que nous observons dans l’écosystème, comme les limites de facturation et de débit de Travis CI pour l’open source, inciteront encore davantage les développeurs à migrer leurs automatisations logicielles vers GitHub Actions.
Dans cet article, j’aimerais vous montrer comment j’utilise GitHub Actions pour publier les packages npm que je maintiens dans mes projets open source. Si vous suivez le workflow GitHub, qui repose sur les workflows de pull requests GitHub, cette approche unifiera encore davantage l’expérience de vos équipes et des contributeurs de la communauté autour des workflows GitHub de votre projet.
Qu’est-ce que GitHub Actions ?
GitHub Actions est une technologie développée par GitHub qui permet aux développeurs d’automatiser leurs workflows d’intégration continue : ils peuvent ainsi compiler, déployer, planifier des tâches récurrentes et bien plus encore. GitHub Actions est disponible nativement et intégré aux dépôts GitHub. Il propose de nombreux workflows réutilisables créés par des contributeurs de la communauté, pour publier des packages npm, publier des images Docker, exécuter des tests de sécurité et bien plus encore.
Comment fonctionnent les actions dans GitHub ?
L’infrastructure cloud de GitHub exécute GitHub Actions pour les utilisateurs. Pour cela, il faut créer un fichier de workflow dans un répertoire de dépôt GitHub nommé .github/workflows, puis décrire le déclencheur, la planification du job et les actions qu’il doit effectuer à l’aide de YAML.
Qu’est-ce qu’un workflow GitHub ?
Un workflow GitHub est un ensemble de jobs exécutés en fonction d’un déclencheur ou d’une planification basée sur cron. Un job comprend une ou plusieurs étapes qui constituent un workflow automatisé.
Configurer un projet Node.js pour GitHub Actions
Ajoutons une première automatisation GitHub Actions à un projet Node.js. Le job GitHub Actions installera tous les packages npm nécessaires, exécutera les tests, puis publiera notre projet sous la forme d’un package npm que les utilisateurs pourront exploiter.
Notre package npm sera une interface en ligne de commande (CLI) qui vous permettra de parcourir la formidable liste des conférences de SnykCon 2020, le tout premier événement mondial sur la sécurité organisé par Snyk, en 2020.
Le projet complet est hébergé sur GitHub. Voici un aperçu du package npm :

Pour créer un workflow CI GitHub complet, commencez par ajouter le fichier GitHub Actions suivant à la racine du dépôt : .github/workflows/main.yml
Le modèle ci-dessus est assez classique pour compiler et tester des projets Node.js. Il effectue les opérations suivantes :
Le déclencheur s’active à chaque push, sur toutes les branches.
Nous exécutons cette action avec les versions 12.x et 14.x de Node.js afin de garantir la compatibilité avec les deux versions LTS de Node.js.
Le job exécute ensuite plusieurs étapes : il récupère le code du dépôt git, effectue une installation npm sécurisée et déterministe, lance une étape de compilation si elle est définie, puis exécute les tests.
Lorsque vous poussez de nouveaux commits vers le dépôt, que vous suiviez un workflow GitHub ou un workflow de pull request GitHub, vous voyez de nouveaux jobs s’ajouter dans l’onglet Actions du dépôt. Voici un exemple montrant les tests du package npm snykcon en cours d’exécution :

Publier des packages npm avec GitHub Actions
Si tous les tests réussissent et que ce workflow d’automatisation s’exécute sur notre branche principale, nous pouvons automatiser entièrement la publication des nouvelles versions !
Lors de la publication de packages, il est judicieux de prendre en compte les implications de sécurité associées. Passons-en quelques-unes en revue :
Code malveillant : Si une personne ajoute délibérément une vulnérabilité dans une contribution au code et que vous ne la repérez pas, une publication automatique lors de la fusion dans votre branche principale la rendra immédiatement accessible aux utilisateurs. Une publication manuelle immédiate n’y change rien. En revanche, plus le délai entre la fusion des pull requests et la publication est long, plus la communauté a le temps d’examiner le code et de contribuer à détecter ces problèmes.
Dépendance vulnérable : Si une personne malveillante ajoute une dépendance présentant une vulnérabilité connue et publique, celle-ci sera transmise à vos utilisateurs lors de son installation. Un outil gratuit comme Snyk, connecté à vos dépôts Git, peut ajouter une vérification d’état qui vous empêche de publier des versions contenant des dépendances vulnérables et résoudre précisément ce problème.
Dépendance malveillante via un fichier de verrouillage : Avez-vous envisagé ce qui se passerait si une contribution visant à mettre à jour les dépendances de package.json injectait en réalité un package malveillant dans le fichier de verrouillage, que personne ne prend de toute façon le temps d’examiner ? J’ai expliqué pourquoi les fichiers de verrouillage npm peuvent constituer un angle mort de sécurité permettant d’injecter des modules malveillants. Si vous n’avez pas encore envisagé ce vecteur d’attaque, prenez le temps d’approfondir le sujet.
Vol de votre jeton npm :Par le passé, plusieurs tentatives liées à des packages malveillants visaient à dérober le jeton npm d’une personne ou d’autres informations sensibles stockées dans des variables d’environnement.
La liste ci-dessus ne prétend pas couvrir tous les risques de sécurité, mais elle met clairement en évidence plusieurs points dont nous devons être conscients.
À propos des risques de sécurité, trouvez-vous cette liste intéressante et instructive ? Il s’agit d’un exercice rapide de modélisation des menaces, que vous pouvez pratiquer plus régulièrement lors du développement de fonctionnalités. Nous en parlons dans notre processus DevSecOps, et Alyssa Miller a présenté une excellente conférence à SnykCon sur le Threat modeling des user stories : l’approche DevSecOps. Découvrez-les !
Revenons à notre liste et intéressons-nous au dernier risque mentionné : le vol de votre jeton npm. Comme cet article porte sur la publication de packages npm, nous devons mettre un jeton npm à la disposition du workflow GitHub Actions. Cette pratique a longtemps été déconseillée pour les raisons suivantes :
Fonctionnalités npm : jusqu’à récemment, la publication de packages npm avec un jeton npm exigeait de désactiver l’authentification à deux facteurs pour votre compte npm. Ce n’est plus le cas, et nous allons voir comment procéder !
Vol d’un jeton lors de l’installation de packages malveillants :si vous mettez le jeton npm à disposition de votre CI sous forme de variable d’environnement, des packages malveillants présents dans l’arborescence de dépendances (au-delà de vos dépendances directes) peuvent y accéder pendant le processus d’installation npm, par exemple. Par défaut, les packages peuvent alors exécuter n’importe quelle commande.
Comment répondre à ces deux risques de sécurité bien réels ?
Tout d’abord, npm permet désormais d’utiliser l’authentification à deux facteurs et d’émettre des jetons d’automatisation : ces deux fonctionnalités ne sont donc plus incompatibles. Ensuite, GitHub Actions permet de rendre les variables d’environnement accessibles uniquement à une étape précise d’un job. Nous pouvons ainsi les rendre disponibles uniquement pour la commande npm publish, et non pour npm install, qui aurait également permis aux dépendances indirectes d’y accéder.
Commençons.
Tout d’abord, activez l’authentification à deux facteurs pour votre compte npm. Accédez aux paramètres de votre compte sur https://npmjs.com/ et activez le mode 2FA pour l’authentification et la publication.

Vous devrez associer un appareil d’authentification, par exemple l’application Google Authenticator sur votre téléphone ou 1Password si vous l’utilisez pour gérer vos mots de passe.
Accédez ensuite à la gestion des jetons d’accès sur npm et créez un nouveau jeton.

Veillez à créer un jeton de type Automation, comme le montre la capture d’écran ci-dessous. Comme indiqué dans la description, ce type de jeton contourne l’authentification à deux facteurs et peut être utilisé dans des workflows d’intégration continue (CI) :

Nous pouvons ensuite mettre ce jeton à disposition de GitHub Actions en le créant d’abord comme secret dans la gestion des secrets du dépôt GitHub, comme ceci :

Enfin, mettons à jour notre workflow GitHub Actions pour y ajouter une étape de publication. Après le job build, ajoutez le job publish suivant :
Il est essentiel de noter l’argument --ignore-scripts de la commande npm publish, indispensable à un workflow de publication sécurisé. Il indique à la commande de publication de la CLI npm d’ignorer tous les scripts de cycle de vie définis dans le manifeste packge.json. C’est important : lorsqu’un package malveillant est installé dans le cadre du graphe de dépendances, il peut ajouter à votre propre package une entrée telle que prepack: “echo ‘do something malicious’”, qui sera exécutée lorsque vous lancerez npm publish. Comme vous pouvez l’imaginer, cette entrée malveillante peut faire bien plus qu’afficher un message à l’écran.
Le workflow que nous utilisons ici réduit considérablement le risque de vol des jetons npm ou d’exécution de commandes arbitraires visant d’autres cibles, car :
Dans notre job build, nous installons les packages sans leur permettre d’exécuter des commandes arbitraires, grâce à l’argument
--ignore-scriptstransmis ànpm ci.Notre job publish démarre à partir d’un nouveau checkout du dépôt. Ainsi, même s’il avait fallu autoriser l’exécution de scripts lors de l’installation des packages npm à l’étape précédente de build, toute tentative malveillante d’injecter des données dans le fichier
package.jsondu package serait vaine.Par mesure de précaution, notre étape publish inclut l’argument
--ignore-scriptsafin d’éviter l’exécution des scripts de cycle de vie npm pendant cette étape.Le jeton d’automatisation npm utilisé pour publier un package est uniquement mis à disposition de l’étape de publication.
Compte tenu de tout ce qui précède, vous dormiriez sans doute mieux en exigeant une authentification à deux facteurs à chaque publication d’une nouvelle version du package. Toutefois, sa mise en place dans un environnement CI est plus complexe, et nous n’aborderons pas ce sujet dans cet article.
Ce job ne s’exécute que sur la branche principale — nous ne publions donc rien lors des tests d’une pull request — et utilise une version précise de Node.js, au lieu de lancer des jobs en parallèle avec différentes versions.
Une fois une pull request GitHub fusionnée, ou un commit poussé vers la branche principale, le job publish suivant s’exécutera et publiera le package npm.

Consultez le fichier du workflow GitHub Actions complet à titre de référence.
Il est important de noter que nous n’avons pas abordé le versionnement sémantique automatisé, qui permet d’incrémenter automatiquement les versions de nos packages npm selon qu’un commit correspond à une mise à jour corrective, mineure ou majeure du code. Pour cela, je vous recommande d’évaluer semantic-release, que Snyk Advisor considère également comme un package très fiable :

Où s’exécutent les GitHub Actions ?
GitHub Actions s’applique à un dépôt spécifique. Les actions sont exécutées et gérées à l’aide des services d’infrastructure cloud de GitHub, qui prennent en charge des runners pour Mac, Windows et Linux. Il est également possible d’utiliser des runners GitHub auto-hébergés, par exemple sur Google Cloud Infrastructure.
Qu’est-ce que le workflow GitHub ?
GitHub Actions s’applique à un dépôt spécifique. Les actions sont exécutées et gérées à l’aide des services d’infrastructure cloud de GitHub, qui prennent en charge des runners pour Mac, Windows et Linux. Il est également possible d’utiliser des runners GitHub auto-hébergés, par exemple sur Google Cloud Infrastructure.
En résumé
En résumé, la création de packages npm et leur publication dans l’écosystème npm au sens large peuvent être automatisées de manière sécurisée avec GitHub Actions. L’avantage de GitHub Actions, c’est que toute l’expérience de développement liée à un workflow git reste au sein de la plateforme GitHub.
Je vous recommande également de découvrir d’autres intégrations GitHub Actions que vous pouvez facilement ajouter à votre projet :
Connectez Snyk à votre dépôt Git pour recevoir des pull requests de correction automatisées.
Vous pouvez utiliser Snyk GitHub Actions pour tester les dépendances de vos packages npm, et même les images de vos conteneurs Docker. Dépôt officiel : https://github.com/snyk/actions.
Essayez mon is-website-vulnerable GitHub Action si vous souhaitez suivre les tests de sécurité de bout en bout d’un site web (détection des bibliothèques JavaScript vulnérables).
Lancez-vous dans les compétitions Capture The Flag
Apprenez à résoudre des défis Capture The Flag en regardant à la demande notre atelier virtuel d’initiation.