TP10 : DevSecOps « Shift Left » (secrets, SAST, dépendances, images, GitHub)
Le Shift Left consiste à déplacer les contrôles de sécurité vers la gauche du cycle de vie : sur le poste du développeur et dans la CI, plutôt qu’après la mise en production. Dans ce TP, vous analysez une application volontairement vulnérable avec les outils open source standard, vous les branchez dans GitHub Actions et dans Jenkins avec des quality gates, puis vous corrigez les failles.
⚠️ L’application
vuln-app/contient des failles réelles (injection SQL, injection de commande, XSS, dépendances vulnérables) et des secrets factices. Ne l’exposez jamais en dehors de la VM de lab.
Fichiers de départ :
vuln-app.zip
1. Objectifs pédagogiques
À l’issue du TP, vous serez capable de :
- associer chaque famille de risque à un outil : secrets (Gitleaks), SAST (Semgrep, CodeQL), SCA (npm audit, OWASP Dependency-Check, Dependabot), images et configuration (Trivy) ;
- bloquer un secret avant le commit avec un hook
pre-commit; - lire un rapport et distinguer vrai positif, faux positif et risque accepté ;
- configurer des quality gates qui font échouer un pipeline ;
- sécuriser un workflow GitHub Actions (permissions minimales, Actions épinglées par SHA) et protéger la branche
main.
Les images de cette page sont des schémas explicatifs, pas des captures d’écran.
2. Environnement du TP
-
VM de lab Ubuntu Server 26.04 avec Docker, Node.js 24, Git,
pipx(sudo apt install -y pipx). -
Copier
vuln-app/dans un nouveau dépôt GitHub publicvuln-app(CodeQL et Dependabot sont gratuits sur les dépôts publics) :cd vuln-app && npm install && git init -b main && git add . && git commit -m "chore: import vuln-app"Ne poussez pas encore : la partie 2 commence par détecter les secrets avant qu’ils ne quittent le poste.
Tous les scanners sont lancés en conteneur : rien à installer.
| Famille | Question posée | Outils du TP |
|---|---|---|
| Secrets | Un mot de passe ou un token est-il dans Git ? | Gitleaks, push protection GitHub |
| SAST | Mon code contient-il des motifs dangereux ? | Semgrep, CodeQL |
| SCA | Mes dépendances ont-elles des CVE connues ? | npm audit, OWASP Dependency-Check, Dependabot |
| Image | Les paquets de l’image ont-ils des CVE ? | Trivy image |
| Config | Le Dockerfile / l’IaC sont-ils mal configurés ? | Trivy config |
3. Partie 1 : Secrets avec Gitleaks
-
Analyser le dépôt (historique compris) :
docker run --rm -v "$PWD:/repo" ghcr.io/gitleaks/gitleaks:latest git /repo --verbose -
Noter les règles déclenchées (
generic-api-key…), le fichier, la ligne et le commit. -
Déplacer les secrets dans des variables d’environnement (
process.env.JWT_SECRET…), créer un.env.exampleet ajouter.envau.gitignore. -
Relancer Gitleaks : le secret est toujours détecté dans l’historique. Pourquoi ? Que faudrait-il faire pour un vrai secret ?
Réponse attendue : révoquer le secret (le considérer comme compromis), puis éventuellement réécrire l’historique (
git filter-repo). Supprimer le fichier ne suffit jamais. -
Pour ce lab, repartir d’un historique propre (
rm -rf .git && git init -b main …), puis créer un fichier.gitleaksignorevide pour documenter les futurs faux positifs.
4. Partie 2 : Hook pre-commit
-
Installer le hook fourni dans
.pre-commit-config.yaml:pipx install pre-commit pre-commit autoupdate && pre-commit install -
Ajouter une ligne
const token = "Zk3P9xQm7Rt2Lw8Vn4Hb6Jc1Yd5Fs0Ga";dans un fichier et tenter ungit commit: il doit être refusé. -
Retirer la ligne, commiter, puis pousser le dépôt sur GitHub.
5. Partie 3 : SAST avec Semgrep
-
Lancer Semgrep avec des règles JavaScript et OWASP :
docker run --rm -v "$PWD:/src" semgrep/semgrep \ semgrep scan --config p/javascript --config p/owasp-top-ten --error /src -
Pour chaque résultat, compléter le tableau (à inclure dans votre rendu) :
| Règle | Fichier:ligne | Vrai positif ? | Correction |
|---|---|---|---|
- Corriger : requête paramétrée (
db.all("… WHERE name = ?", [name])),execFile("ping", ["-c", "1", host])avec validation dehost, échappement HTML (oures.json),expiresInsur le JWT. - Relancer Semgrep jusqu’à obtenir 0 résultat bloquant. Si vous acceptez un résultat, justifiez-le par un commentaire
// nosemgrep: <rule-id>.
6. Partie 4 : Dépendances et images
-
Analyser les dépendances :
npm audit npm audit --audit-level=high; echo "code retour : $?" -
Lancer OWASP Dependency-Check (le premier lancement télécharge la base NVD et peut prendre du temps ; une clé d’API NVD gratuite l’accélère) :
docker run --rm -v "$PWD:/src" -v dc-data:/usr/share/dependency-check/data \ owasp/dependency-check:latest --scan /src --format HTML --out /src/reports \ --project vuln-app # ajouter --nvdApiKey <clé> si vous en avez une -
Comparer les résultats de
npm auditet de Dependency-Check : mêmes CVE ? même gravité ? -
Construire l’image et l’analyser avec Trivy :
docker build -t vuln-app:1 . docker run --rm -v /var/run/docker.sock:/var/run/docker.sock aquasec/trivy:latest \ image --severity HIGH,CRITICAL vuln-app:1 docker run --rm -v "$PWD:/src" aquasec/trivy:latest config /src -
Corriger : mettre à jour les dépendances (
npm audit fix, puis à la main si nécessaire), réécrire leDockerfile(imagenode:24-alpine, multi-stage,USER node, aucun secret dansENV), relancer les trois outils et noter le nombre de vulnérabilités avant / après.
7. Partie 5 : GitHub (CodeQL, Dependabot, workflow sécurisé)
- Settings → Code security : activer Dependency graph, Dependabot alerts, Dependabot security updates, Secret scanning et Push protection.
- Ajouter un fichier
.github/dependabot.yml(écosystèmesnpmetgithub-actions, fréquence hebdomadaire) et observer les Pull Requests ouvertes par Dependabot. - Activer CodeQL (Code scanning → Set up → Default). Comparer ses alertes avec celles de Semgrep :
| Faille | Semgrep | CodeQL |
|---|---|---|
| Injection SQL | ||
| Injection de commande | ||
| XSS |
-
Compléter le workflow
.github/workflows/security.yml:permissions: contents: readau niveau du workflow ;security-events: writeuniquement là où c’est nécessaire ;- Gitleaks, Semgrep (avec export SARIF dans l’onglet Security),
npm audit, Trivy image et config ; - chaque Action tierce épinglée par SHA complet (
uses: owner/action@<sha> # vX.Y.Z).
Pourquoi épingler par SHA ? Un tag (
v4) peut être déplacé vers un code malveillant si le dépôt de l’Action est compromis. Un SHA de commit est immuable. Dependabot (github-actions) se charge ensuite de proposer les mises à jour. -
Créer une règle de protection (ruleset) sur
main: Pull Request obligatoire, 1 approbation, checkssecrets,sast,dependenciesetimageobligatoires, push direct interdit. -
Ouvrir une PR qui réintroduit l’injection SQL : elle doit être bloquée.
8. Partie 6 : Quality gates dans Jenkins
- Ajouter au
Jenkinsfiledu TP9 un stageSecurityexécutant en parallèle Gitleaks, Semgrep etnpm audit, puis un stageImage scan(Trivy,--exit-code 1 --severity CRITICAL) après le build. - Archiver les rapports (
archiveArtifacts 'reports/**'). - Démontrer un pipeline rouge (faille réintroduite) puis vert (faille corrigée).
Défi
- Écrire une règle Semgrep personnalisée qui interdit
console.log(req.body)(fuite de données personnelles dans les logs) et l’ajouter à la CI.- Créer un fichier
.trivyignorepour une CVE sans correctif disponible, avec une date d’expiration et une justification, et expliquer la différence entre « ignorer » et « accepter un risque ».
9. Livrables
Captures à déposer dans captures/ :
| Fichier | Contenu attendu |
|---|---|
gitleaks.png |
Secrets détectés par Gitleaks |
pre-commit-blocked.png |
Commit refusé par le hook |
semgrep-before-after.png |
Résultats Semgrep avant / après correction |
trivy-before-after.png |
Vulnérabilités de l’image avant / après |
dependabot-pr.png |
Pull Request ouverte par Dependabot |
codeql-alerts.png |
Alertes CodeQL dans l’onglet Security |
pr-blocked.png |
PR bloquée par un check de sécurité obligatoire |
jenkins-security.png |
Pipeline Jenkins rouge puis vert |
Ainsi que : le code corrigé, security.yml, dependabot.yml, le Jenkinsfile et les tableaux remplis.