Dès que vous ajoutez une bibliothèque à un projet (une dépendance, comme
lodash en JavaScript ou jinja2 en Python), vous faites tourner du code que
vous n'avez pas écrit. Deux choses peuvent mal tourner : cette bibliothèque
contient une faille de sécurité connue, ou bien c'est carrément un paquet
piégé (un faux paquet qui imite un vrai pour voler vos mots de passe ou vos
clés).
OSV-Scanner est un outil gratuit qui repère ces deux dangers. Il lit la liste précise des dépendances de votre projet et la compare à des bases publiques de menaces tenues à jour par la communauté. S'il trouve une dépendance dangereuse, il vous le dit — et peut bloquer la suite.
Ce guide s'adresse à un développeur ou un ingénieur DevSecOps qui veut repérer une dépendance dangereuse avant qu'elle ne s'installe. Vous installerez l'outil, scannerez un projet npm (JavaScript) et un projet Python géré par uv, puis mettrez la détection en place à trois moments : sur votre poste, au moment du commit (l'enregistrement de votre code dans Git), et dans la CI (les contrôles automatiques qui tournent à chaque modification).
Ce que vous allez apprendre
Section intitulée « Ce que vous allez apprendre »- Installer OSV-Scanner v2 et comprendre ce qu'il détecte.
- Scanner un lockfile npm et un lockfile uv contre la base OSV.
- Ajouter une dépendance en scannant avant d'installer quoi que ce soit.
- Bloquer une dépendance vérolée au commit avec un hook
pre-commit. - Brancher OSV-Scanner en CI GitHub Actions (scan de PR + scan planifié).
- Positionner le scanner par rapport à un artifact firewall.
Prérequis
Section intitulée « Prérequis »- Un projet npm (avec
package-lock.json) ou Python géré par uv (avecuv.lock). - Homebrew, Go ou un gestionnaire de paquets système pour l'installation.
- Notions de base sur les attaques via les gestionnaires de paquets et les protections de dépendances.
Ce que OSV-Scanner détecte vraiment
Section intitulée « Ce que OSV-Scanner détecte vraiment »La « liste précise des dépendances » que lit l'outil, c'est le fichier de
verrouillage (ou lockfile) : package-lock.json pour npm, uv.lock pour uv.
Il fige la version exacte de chaque bibliothèque utilisée par votre projet, y
compris celles tirées indirectement. OSV-Scanner le lit pour savoir quoi
vérifier — il n'installe rien.
Pour comparer, OSV-Scanner s'appuie sur le format OSV (Open Source Vulnerability), une façon standard de décrire un problème de sécurité, commune à tous les langages. Deux familles d'identifiants vous concernent.
Les enregistrements CVE-/GHSA-/PYSEC- décrivent des vulnérabilités
d'un paquet par ailleurs légitime — une faille à corriger en montant de version.
Les enregistrements MAL- décrivent un paquet malveillant : typosquat,
dependency confusion, ou version compromise d'un paquet populaire. C'est cette
seconde famille, alimentée par le dépôt OpenSSF Malicious Packages, qui
distingue OSV-Scanner d'un simple scanner de CVE.
Installer OSV-Scanner
Section intitulée « Installer OSV-Scanner »OSV-Scanner est un binaire Go, distribué pour Linux, macOS et Windows. Choisissez une méthode selon votre poste.
brew install osv-scannergo install github.com/google/osv-scanner/v2/cmd/osv-scanner@latestTéléchargez l'archive SLSA3 correspondant à votre OS depuis la
page des releases, puis placez
le binaire dans votre PATH.
Vérifiez l'installation. La sortie doit afficher une version 2.x :
osv-scanner --versionosv-scanner version: 2.3.8osv-scalibr version: 0.4.5Scanner un projet existant
Section intitulée « Scanner un projet existant »Premier cas d'usage, le plus simple : vous avez déjà un projet et vous voulez
savoir si ses dépendances actuelles sont saines. Le geste de base est
osv-scanner scan -L <lockfile>. L'option -L cible un
fichier de verrouillage précis ; OSV-Scanner reconnaît nativement
package-lock.json, yarn.lock, pnpm-lock.yaml, poetry.lock, uv.lock,
Pipfile.lock, go.sum et bien d'autres.
Sur un projet épinglant minimist@1.2.0 (une version réellement vulnérable) :
osv-scanner scan -L package-lock.jsonTotal 1 package affected by 2 known vulnerabilities (1 Critical, 0 High, 1 Medium) from 1 ecosystem.
+-------------------------------------+------+-----------+----------+---------+---------------+| OSV URL | CVSS | ECOSYSTEM | PACKAGE | VERSION | FIXED VERSION |+-------------------------------------+------+-----------+----------+---------+---------------+| https://osv.dev/GHSA-vh95-rmgr-6w4m | 5.6 | npm | minimist | 1.2.0 | 1.2.3 || https://osv.dev/GHSA-xvch-5gv4-984h | 9.8 | npm | minimist | 1.2.0 | 1.2.6 |+-------------------------------------+------+-----------+----------+---------+---------------+OSV-Scanner lit uv.lock directement, sans conversion en requirements.txt.
Sur un projet épinglant jinja2==2.11.2 :
osv-scanner scan -L uv.lockTotal 1 package affected by 5 known vulnerabilities (0 Critical, 1 High, 4 Medium) from 1 ecosystem.
+-------------------------------------+------+-----------+---------+---------+---------------+| OSV URL | CVSS | ECOSYSTEM | PACKAGE | VERSION | FIXED VERSION |+-------------------------------------+------+-----------+---------+---------+---------------+| https://osv.dev/PYSEC-2021-66 | 6.9 | PyPI | jinja2 | 2.11.2 | 2.11.3 || https://osv.dev/GHSA-h5c8-rqwp-cp95 | 5.4 | PyPI | jinja2 | 2.11.2 | 3.1.3 |+-------------------------------------+------+-----------+---------+---------+---------------+La colonne FIXED VERSION vous donne la cible de remédiation immédiate.
Surtout, retenez le code de sortie : OSV-Scanner renvoie 1 dès qu'il
trouve une vulnérabilité ou un paquet malveillant, 0 si tout est propre.
C'est ce code qui fera échouer un hook ou un job CI.
osv-scanner scan -L package-lock.json ; echo "code de sortie : $?"Pour scanner tout un dépôt d'un coup (il découvre les lockfiles récursivement) :
osv-scanner scan -r ./Sécuriser l'ajout d'une nouvelle dépendance
Section intitulée « Sécuriser l'ajout d'une nouvelle dépendance »Second cas d'usage : vous ajoutez une dépendance. Le réflexe
npm install <paquet> télécharge et exécute les scripts du paquet — si le
paquet est piégé, le mal est fait avant qu'un scanner intervienne. Le bon
réflexe inverse l'ordre, en quatre temps :
- créer le fichier de verrouillage sans rien installer ;
- scanner ce fichier ;
- installer seulement si le scan est vert ;
- automatiser ce contrôle au moment du commit.
1. Créer le fichier de verrouillage sans rien installer
Section intitulée « 1. Créer le fichier de verrouillage sans rien installer »npm et uv savent résoudre les versions et écrire le lockfile seul, sans télécharger ni exécuter le moindre paquet. C'est ce qui rend possible le scan avant l'installation.
L'option --package-lock-only met à jour package-lock.json sans créer
node_modules ni exécuter de script :
npm install lodash@4.17.4 --package-lock-onlyL'option --no-sync met à jour pyproject.toml et uv.lock sans créer
l'environnement virtuel :
uv add 'jinja2==2.11.2' --no-sync2. Scanner le fichier de verrouillage
Section intitulée « 2. Scanner le fichier de verrouillage »C'est la commande de base vue plus haut, appliquée au lockfile fraîchement résolu — et aucun code du nouveau paquet n'a encore tourné.
osv-scanner scan -L package-lock.jsonosv-scanner scan -L uv.lockSi le scan échoue (code 1), rien n'a été installé : vous n'avez fait
que résoudre le lockfile. Annulez la modification et n'allez pas plus loin :
git checkout -- package-lock.jsongit checkout -- pyproject.toml uv.lock3. Installer, seulement si le scan est vert
Section intitulée « 3. Installer, seulement si le scan est vert »Le scan est vert (code 0) ? Les dépendances sont saines, vous pouvez
installer pour de vrai.
npm ciuv sync --frozen4. Automatiser le contrôle au commit
Section intitulée « 4. Automatiser le contrôle au commit »Un script reste facultatif : on peut l'oublier. Le hook pre-commit
rend le scan automatique : dès que le lockfile change, il s'exécute avant
que la dépendance n'entre dans l'historique Git et ne parte vers vos collègues.
OSV-Scanner n'a pas de hook publié sur le registre pre-commit, mais un hook
local appelle directement le binaire.
Créez .pre-commit-config.yaml à la racine :
repos: - repo: local hooks: - id: osv-scanner-npm name: OSV-Scanner (npm lockfile) entry: osv-scanner scan -L app/package-lock.json language: system files: ^app/package-lock\.json$ pass_filenames: false - id: osv-scanner-uv name: OSV-Scanner (uv lockfile) entry: osv-scanner scan -L app-py/uv.lock language: system files: ^app-py/uv\.lock$ pass_filenames: falseActivez puis testez le hook :
pre-commit installpre-commit run --all-filesSur un lockfile vérolé, le commit est refusé et la sortie liste les vulnérabilités. Corrigez la version fautive, puis recommitez.
Le filet de sécurité en CI
Section intitulée « Le filet de sécurité en CI »Les étapes précédentes sécurisent votre poste. La CI est un autre niveau : le filet qui rattrape ce qui est passé entre les mailles — un collègue qui n'a pas le hook, un poste mal configuré. OSV-Scanner officiel s'y branche via des workflows réutilisables, en deux modes complémentaires, à épingler au SHA de la version (jamais un tag seul).
Le scan de pull request compare la branche cible à la branche de la PR et ne casse le build que si la PR introduit une nouvelle vulnérabilité — la dette préexistante ne bloque pas toute l'équipe :
name: OSV-Scanner PR
on: pull_request: branches: [main] merge_group: branches: [main]
permissions: actions: read security-events: write contents: read
jobs: osv-scan-pr: uses: google/osv-scanner-action/.github/workflows/osv-scanner-reusable-pr.yml@9a498708959aeaef5ef730655706c5a1df1edbc2 # v2.3.8Le scan planifié rejoue une analyse complète à intervalle régulier. Il est
indispensable : un paquet propre au moment du merge peut être reclassé
MAL- quelques heures plus tard. Le cron rattrape ce que le scan de PR ne
pouvait pas voir :
name: OSV-Scanner Scheduled
on: schedule: - cron: "30 12 * * 1" push: branches: [main]
permissions: actions: read security-events: write contents: read
jobs: osv-scan-scheduled: uses: google/osv-scanner-action/.github/workflows/osv-scanner-reusable.yml@9a498708959aeaef5ef730655706c5a1df1edbc2 # v2.3.8Ces workflows remontent leurs résultats au format SARIF dans l'onglet
Code Scanning de GitHub, d'où les permissions security-events: write et
actions: read. Ils scannent le dépôt entier : uv.lock et
package-lock.json sont couverts sans configuration supplémentaire.
Prévention en amont : fermer la fenêtre d'exécution
Section intitulée « Prévention en amont : fermer la fenêtre d'exécution »La détection ne suffit pas contre un malware du jour que la base OSV n'a pas encore classé. Deux réglages réduisent la surface, en complément du scanner.
D'abord, couper l'exécution des scripts d'installation, le principal vecteur.
Ensuite, imposer un délai de quarantaine (cooldown) : n'installer que des
versions publiées depuis assez longtemps pour avoir été analysées — la plupart
des paquets malveillants sont retirés ou classés MAL- en 24 à 72 heures.
Dans le .npmrc du projet (cooldown disponible depuis npm 11.10.0) :
# Couper l'exécution des scripts pre/postinstallignore-scripts=true
# N'installer que les versions publiées il y a plus de 7 joursmin-release-age=7Dans pyproject.toml, no-build force l'usage de wheels (pas de setup.py
exécuté). Le cooldown se passe à la résolution :
[tool.uv]no-build = true# Cooldown : exclure les versions publiées dans les 7 derniers joursuv lock --exclude-newer "7 days"OSV-Scanner et l'artifact firewall : deux étages
Section intitulée « OSV-Scanner et l'artifact firewall : deux étages »OSV-Scanner ne remplace pas un artifact firewall, il le complète. Le firewall agit au périmètre : il empêche un paquet malveillant d'entrer dans le registre interne. OSV-Scanner agit sur le poste et la CI : il détecte ce qui est déjà dans le projet, y compris quand un développeur tire un paquet directement depuis npmjs.com, contournant le registre interne.
| Contrôle | Où | Quand | Ce qu'il protège |
|---|---|---|---|
| Artifact firewall | périmètre / registre | à l'ingestion | tout ce qui passe par le registre interne |
| OSV-Scanner | poste + CI | à l'ajout, au commit, en CI | le projet, même hors registre interne |
| Cooldown + scripts coupés | poste | à l'installation | la fenêtre du malware non encore classé |
Les trois reposent en partie sur les mêmes signatures : un paquet vraiment inconnu passe à travers. C'est pourquoi le cooldown reste la seule défense temporelle contre l'inédit.
Sécurité
Section intitulée « Sécurité »Traitez OSV-Scanner comme tout outil exécuté en CI : une dépendance de votre
pipeline, donc une surface d'attaque. Épinglez le workflow réutilisable au
SHA (déjà fait plus haut) et n'accordez que les permissions minimales
nécessaires — security-events: write pour publier le SARIF, rien de plus.
N'ajoutez pas de token de registre privé au job de scan tant qu'il n'en a pas
besoin : le scan lit des lockfiles, il n'installe rien.
Dépannage
Section intitulée « Dépannage »| Symptôme | Cause probable | Solution |
|---|---|---|
min-release-age ignoré | npm < 11.10.0 | Mettre npm à jour (npm install -g npm@latest) |
| Le scan ne trouve aucun paquet | Lockfile absent ou mauvais chemin -L | Générer le lockfile (npm install --package-lock-only, uv lock) |
no-build casse l'install d'un paquet | Paquet publié uniquement en sdist | Réautoriser ce paquet précis via no-build-package |
Code de sortie 0 malgré des vulnérabilités | Sortie redirigée vers head/grep | Tester $? sur la commande osv-scanner seule |
| Beaucoup de bruit sur la dette existante | Scan complet sur une PR | Utiliser le workflow PR-diff qui ne signale que le nouveau |
À retenir
Section intitulée « À retenir »- OSV-Scanner détecte vulnérabilités (
CVE-) et paquets malveillants (MAL-). - Il lit les lockfiles statiquement : on peut scanner avant d'installer.
- Le geste clé : résoudre le lockfile → scanner → installer seulement si vert.
- Le code de sortie
1fait échouer hook et CI ;0= propre. - Le pre-commit protège l'équipe ; il ne protège pas votre machine de l'exécution.
- En CI : scan de PR (nouveautés) + scan planifié (rattrape les
MAL-tardifs). - La détection ne couvre pas le malware du jour : ajoutez
ignore-scripts/no-buildet un cooldown. - Scanner et artifact firewall sont deux étages complémentaires, pas un choix.