Maximize Page
Tech & DevOps HubEspace Tech & DevOps: Explorez le monde du Dev, du Cloud et des outils DevOps à travers nos articles et discussions Explore the world of development, the cloud and DevOps tools

Intégration continue: la branche develop

Date de l'article:08-06-2026
CI/CD Gitlab
Mise en place de l'arborescence GitLab et démarrage de la construction du premier pipeline pour la branche develop. De son analyse jusqu'à son exécution, nous y ajouterons également les contrôles de sécurité Trivy et GitLab SAST basés sur Semgrep.
Commencons par préparer l'environement à l'arborescence GitLab, déclarer les premiers stages et includes, puis ajouter les fichiers pipelines pour toutes les branches.
Dans cet article, nous verrons aussi quelques concepts GitLab, comme:
Et nous continuerons sur la mise en place des jobs suivants:
Au travers de cet article, nous résoudrons l'erreur de build "429 Too many requests" liée à Trivy.
Nous traiterons également l'averissement "Dependency version cannot be determined" présent dans les logs.

Mise en place de l'architecture de base

Avant de plonger dans la création de modèles ou la gestion des variables, commençons par poser les fondations de nos pipelines.
Par la suite, et au fur et à mesure des articles, nous créerons et utiliserons d'autres dossiers, tels que les templates, les variables, etc.

1. Créer l'arborescence GitLab
À l'instar de GitHub qui utilise le dossier ".github/workflows/", GitLab s'appuie sur un dossier conventionnel nommé ".gitlab" pour organiser ses fichiers CI.
Pour démarrer, nous allons mettre en place l'architecture suivante :
  1. Créez le fichier .gitlab-ci.yml,
  2. Le dossier .gitlab,
  3. Son sous-dossier jobs ainsi que ses pipelines
FLASHCARDS
├───.gitlab
│   └─ jobs
│       ├── develop.yml
│       ├── staging.yml
│       └── main.yml
└─.gitlab-ci.yml
Le choix et le nombre de fichiers dépendent avant tout de la stratégie de factorisation et de l'organisation de la configuration CI/CD, plutôt que de la taille du projet.
Ceci est mon choix d'organisation, libre à vous de le personnaliser selon vos besoins

2. Déclarer les premiers [stages] et les [includes]
Le fichier .gitlab-ci.yml contient :
Les stages définissent les grandes étapes du pipeline et leur ordre d'exécution.
Les includes permettent d'importer d'autres fichiers de configuration CI/CD dans la configuration principale.

Analyse des stages pour [develop]
Si on devait les catégoriser, cela donnerait :
Java 17 + Maven + Spotless Build + Tests  JaCoCo   : test
CodeQL, qui mérite une discussion à part           : security
Trivy                                              : security
Ajoutons les premiers stages et fichiers includes:
stages:
  - test
  - security

include:
  - local: '.gitlab/jobs/develop.yml'
  - local: '.gitlab/jobs/staging.yml'
  - local: '.gitlab/jobs/main.yml'
Les stages sont définis une seule fois et peuvent être utilisés par les jobs définis pour les différentes branches.
Chaque stage sera déclaré au fur et à mesure de la création des pipelines.
Dans l'architecture GitLab CI, le fichier racine .gitlab-ci.yml n'est pas uniquement un chef d'orchestre, il peut tout à fait héberger ses propres jobs, que ce soit pour des tâches globales ou pour de petits projets. Et c'est ce que nous verrons plus loin, avec la mise en place du composant Semgrep.

3. Ajouter les fichiers pipelines
Ajoutez, dans les fichiers develop.yml, staging.yml et main.yml, ce contenu de base temporaire, que nous adapterons au fur et à mesure.
Utilisez la même structure pour staging.yml et main.yml, en les adaptant (scripts et rules) selon la branche.
develop-test:
  stage: test
  script:
    - echo "develop"
  rules:
    - if: '$CI_COMMIT_BRANCH == "develop"'

Respecter la stratégie Gitlab Flow
Ce projet suit le GitLab Flow dans sa variante "branches d'environnement":
les fonctionnalités sont développées à part, puis fusionnées de branche en branche.
Voir aussi: gitlab-flow-best-practices
Nous allons mettre en place une restriction dans la CI afin d'éviter que n'importe quel push ne déclenche la mise en route du pipeline.
On pourrait comparer cette restriction avec celle utilisée dans GitHub:
on:
  push:
    branches: [ develop ]
  pull_request:
    branches: [ develop ]
4. Élargir la validation aux autres types de branches
Contexte: Dans l'état actuel, le pipeline ne démarrera pas sur la branche develop. La raison est que l'on ne pousse jamais directement vers cette branche.
Le flux de travail exige que les modifications soient d'abord soumises depuis une branche isolée et préfixée (feature/*, fix/*, update/*...).
Pour débloquer cette situation et automatiser les vérifications, nous allons intégrer une règle, directement inspirée de la stratégie de branchement (Git branching model) afin de leurs donner accès au pipeline.

Ajout de la règle suivant la convention minimale standard
Dans le quotidien d'une équipe de développement, des préfixes de branches sont couramment utilisés pour organiser le code.
Avec "when: never" → toutes les autres branches vers develop seront ignorées.
Ajoutez, en dessous de la condition existante:
- if: '$CI_COMMIT_BRANCH =~ /^(feature|fix|refactor|chore)\/.+$/'
- when: never
incluons les branches dans la configuration:
feature/ → nouvelle fonctionnalité
fix/ → corrections
refactor/ → refactoring sans changement fonctionnel
chore/ → maintenance, dépendances, configuration, tooling
Plutôt que d'aligner des dizaines de conditions identiques, nous centralisons ces préfixes au sein d'une unique expression régulière plus globale
Ces branches déclencheront également le pipeline de validation associé à la branche.
Attention: Pour l'instant, la branche develop n'est pas encore techniquement verrouillée. Cela signifie que le pipeline va tourner pour informer, mais rien n'empêche physiquement de cliquer sur le bouton de fusion même si le pipeline échoue.
Les pipelines de validation se déclenchent dans les "Merge requests". Elles valident le code en exécutant les pipelines des branches concernées avant la fusion.
pipelines de validations dans la MR
Et ce sera le sujet d'un prochain article où, nous verrons comment verrouiller cette étape pour forcer le fait qu'un pipeline doit réussir ("Pipelines must succeed") avant de d'activer le bouton de fusion.

[develop] Build des tests
Maintenant que notre architecture de base est en place, concentrons-nous sur notre première branche.
La question fondamentale à se poser est simple : que doit faire ce premier job? Sur la branche develop, l'objectif principal est d'obtenir un feedback rapide.
Ce job aura donc pour mission de compiler l'application et d'exécuter l'ensemble des tests (unitaires et d'intégration).
develop-build:
  stage: test
  cache:
    key:
      files:
        - pom.xml
        - .mvn/wrapper/maven-wrapper.properties
    paths:
      - .m2/repository/
    policy: pull-push
  image: "maven:3.9.11-eclipse-temurin-17"
L'image, le stage et l'optimisation du cache
Commençons par définir l'environnement d'exécution et une stratégie de cache pour accélérer les futurs builds:
L'optimisation (essentielle) du cache Maven, permet de conserver et de réutiliser le dossier .m2/repository/ d'une exécution à l'autre. Le runner n'aura pas à retélécharger toutes les dépendances du projet à chaque commit.
La clé du cache (key:files) ne sera invalidé et mis à jour que si le fichier pom.xml ou la configuration du wrapper Maven sont modifiés.
  variables:
    SPRING_PROFILES_ACTIVE: dev
    SPRING_DATASOURCE_URL: jdbc:h2:mem:itdb;DB_CLOSE_DELAY=-1
    SPRING_DATASOURCE_DRIVER_CLASS_NAME: org.h2.Driver
    SPRING_DATASOURCE_USERNAME: sa
    SPRING_DATASOURCE_PASSWORD: ""
Pour que nos tests s'exécutent correctement, nous devons injecter les propriétés de configuration de l'application:
Nous activons le profil dev et configurons une base de données en mémoire H2. C'est le choix idéal pour la branche develop car cela garantit des tests rapides.
Les tests d'intégration, utilisant une vraie base PostgreSQL, seront réservés à la branche staging
  script:
    - chmod +x mvnw
    - ./mvnw -B dependency:resolve
    - ./mvnw -B dependency:go-offline
    - ./mvnw spotless:check
    - chmod +x ci-scripts/test.sh
    - ./mvnw -B clean verify jacoco:report
On ajoute les commandes à exécuter pour valider le code:
spotless:check: Vérifie que le code respecte les normes de formatage du projet
verify: Déclenche le cycle complet de Maven (compilation, tests UT/IT)
jacoco:report: Génère le rapport de couverture de code juste après les tests
  artifacts:
    when: always
    paths:
      - target/site/jacoco/
      - .m2/repository/
    expire_in: 1 hour
  rules:
    - if: '$CI_COMMIT_BRANCH == "develop"'
    - if: '$CI_COMMIT_BRANCH =~ /^(feature|fix|hotfix|refactor|chore)\/.+$/'
    - when: never
Le job ci-dessus présente son évolution dans l'ordre d'exécution ↑
Collecte des artefacts et règles de déclenchement
Le rapport JaCoCo est conservé comme artefact afin d'être consulté.
Il pourra également être réutilisé par d'autres jobs.
On conserve les artefacts pendant 1 heure afin d'économiser l'espace de stockage

Le job se termine par l'ajout des règles, comme ajoutées plus haut.
"develop" est la seule branche où l'on ajoute la permission aux branches prefixées, de lancer le pipeline. Cela nous permettra de voir son exécution depuis la MR (avant le merge).

Résultat des logs
Une fois le job develop-build exécuté, les logs du GitLab Runner nous fournissent des informations précieuses sur la manière dont sont traités nos fichiers.
Deux répertoires sont inclus dans l'archive :
  • .m2/repository/
  • target/site/jacoco/
Utilisation du runner local :
No URL provided, cache will 
  not be uploaded to shared cache server.
Signifie que le Runner n'est pas configuré pour utiliser un stockage distant(AWS S3 ou Google Cloud Storage)
[INFO] BUILD SUCCESS
Saving cache for successful job
Creating cache 0_pom_-79def43a17a610d2f6e9cd36e0a5c031ce59b714-4-protected...
.m2/repository/: found 6087 matching artefact files and directories
No URL provided, cache will not be uploaded to shared cache server.
Cache will be stored only locally. Created cache

Uploading artifacts for successful job
target/site/jacoco/: found 47 matching artifact files and directories
.m2/repository/: found 6087 matching artifact files and directories
Uploading artifacts as "archive" to coordinator... 201 Created  
    correlation_id=a30383ad6cbfb732-BRU id=16072721047 status=201 token=72_rTjDee
Par conséquent, le cache est enregistré uniquement en local, sur la machine qui exécute ce runner spécifique.
Cela implique que, sur le même runner, le cache fonctionnera parfaitement et accélérera les prochains builds (par exemple pour réutiliser le dossier .m2/repository).

Concept de lecture des jobs GitLab

Maintenant que nous avons un aperçu du premier job, et avant d'entamer le second, explorons l'un des concepts les plus importants de GitLab CI.
Cela m'a un peu dérouté au début, et je trouve que cela vaut la peine d'une petite explication. La règle est simple: "le pipeline ne se lit pas de haut en bas"

Je répète : les pipelines ne se lisent pas de haut en bas.
Le contexte:
Dans un pipeline, nous avons plusieurs jobs: job A - job B - job C
Et chacuns d'eux décrit :
  • son stage, ses dépendances (needs) et les artefacts qu'il produit ou consomme
  • ses règles d'exécution
Ensuite, GitLab décide de l'ordre réel d'exécution:
  1. Il lit tout le fichier .gitlab-ci.yml.
  2. Il construit ensuite un graphe d'exécution à partir des stages, needs et rules
  3. Il exécute les jobs en respectant ce graphe
Autrement dit, l'ordre textuel des définitions de jobs dans le fichier YAML ne détermine pas leur ordre d'exécution.
Ce sont les stages et les relations needs qui déterminent le graphe d'exécution.

Respecter l'ordre des stages
L'ordre des stages dans le fichier .gitlab-ci.yml est important : GitLab exécute les stages de haut en bas, les uns après les autres.
Ils doivent être déclarés dans l'ordre chronologique exact dans lequel on souhaite que les tâches s'exécutent.
Par défaut, les jobs d'un stage doivent donc réussir avant que les jobs du stage suivant puissent démarrer.
L'utilisation de needs permet toutefois de construire des dépendances plus fines et de démarrer certains jobs avant la fin complète du stage précédent.
Deux règles s'appliquent
  1. Tous les jobs d'un même stage s'exécutent en parallèle
  2. Un stage ne démarre que lorsque le stage précédent est terminé (sauf utilisation particulière de needs)
Ce comportement peut aussi être modifié par des options telles que allow_failure ou needs

Les stages organisent l'exécution et permettent le parallélisme
Dans le fichier .gitlab-ci.yml, nous avons 2 stages:
stages:
  - test
  - security
Chaque job a son :
Stage test
└── develop-build

Stage security
├── develop-trivy
└── semgrep-sast
Une fois les stages définis, GitLab applique alors deux règles simples :
Tous les jobs d'un même stage peuvent s'exécuter en parallèle.
Un stage ne démarre que lorsque le stage précédent est terminé (sauf utilisation particulière de needs).
Dans notre cas, cela donne le scénario suivant :

Les jobs d'un même stage peuvent s'exécuter en parallèle :
          Stage test
      ┌─────────────────┐
      │ develop-build   │
      └────────┬────────┘
               │
               ▼
        Stage security
      ┌────────┴─────────┐
      ▼                  ▼
develop-trivy      semgrep-sast

L'importance de "needs"
Nous avons vu les rules et les stages ; passons maintenant à needs. On utilise needs dans deux cas de figure :
1. Attendre un job spécifique (dépendance d'exécution)
Exemple :
develop-trivy:
  stage: security
  needs:
    - develop-build

Le job en cours (develop-trivy) va attendre que le job spécifié (develop-build) dans "needs" soit terminé.

Son utilité est de créer un graphe de dépendances (DAG) permettant à un job de démarrer dès que ses dépendances sont satisfaites, sans attendre la fin du stage précédent.
Sans needs : un job du stage B doit attendre que tous les jobs du stage A soient terminés.
Avec needs : un job du stage B peut démarrer dès que le job du stage A dont il dépend est terminé, même si d'autres jobs du stage A tournent encore.
2. Récupérer les artefacts produits par les jobs dont il dépend
Supposons que nous voulions récupérer les artefacts du job develop-build :
develop-build:
  //..
  artifacts:
  when: always
  paths:
    - target/site/jacoco/
    expire_in: 1 hour
Spécifier le job avec needs et artifacts: true pour les récupérer :
autre-job:
  needs:
    - job: develop-build
      artifacts: true
La documentation GitLab précise qu'un job utilisant needs ne récupère plus automatiquement tous les artefacts des stages précédents : il récupère uniquement ceux des jobs mentionnés dans needs.
Voici une illustration concrète pour bien comprendre la différence
Scénario 1 : comportement classique (sans needs)

Imaginez que le stage de test contient deux jobs: un job de tests unitaires très rapide (1 min) et un job de tests QA très long (15 min):

STAGES:       [test]                                 [deploy]
JOBS:         ├── unit-test (1 min) ───► (terminé)      └── deploy-app (attend 15 min !)
              └── qa-test   (15 min)  ──────────────────► (fin)

Un problème se pose: le job "deploy-app" serait prêt dès la première minute, car les tests unitaires ont réussi.
Et pourtant, GitLab le bloque et l'oblige à attendre 14 minutes de plus, le temps que le job "qa-test" se termine.

Scénario 2: optimisation avec "needs"

Si on ajoute needs: unit-test au job "deploy-app", on crée un raccourci :

STAGES:       [test]                                 [deploy]
JOBS:         ├── unit-test (1 min) ─(déclenche) ───────► deploy-app (démarre à la min 1 !)
              └── qa-test   (15 min)  ──────────────────► (tourne encore en arrière-plan)

Résultat : le déploiement commence dès la fin de la première minute (si toutes ses autres conditions et dépendances sont satisfaites).
On n'attend plus la fin du long traitement QA pour avancer dans le pipeline: le déploiement démarre dès que "unit-test" est terminé avec succès et, sous réserve qu'aucune autre dépendance ne le bloque.

En résumé : GitLab valide et crée le pipeline à partir de la configuration CI/CD, puis détermine les jobs et leurs dépendances avant de les exécuter.

[develop] Trivy Scan (FileSystem)
Maintenant que nous avons compris ces quelques concepts de base, penchons-nous sur le deuxième job de la branche [develop].
Le scan de système de fichiers de Trivy permet d'analyser les projets locaux, répertoires ou fichiers uniques afin de détecter des vulnérabilités, des configurations erronées, des secrets exposés ou des problèmes de licence, avant même de construire une image de conteneur.
develop-trivy:
  stage: security
  needs:
    - job: develop-build
      artifacts: true
  image:
    name: aquasec/trivy:0.72.0
    entrypoint: [""]
  cache:
    - key: trivy-db
      paths:
        - .trivycache/
      policy: pull-push
  variables:
    TRIVY_CACHE_DIR: "$CI_PROJECT_DIR/.trivycache"
  script:
    trivy fs \
      --severity HIGH,CRITICAL \
      --format table \
      --scanners vuln,misconfig \
      --skip-dirs ".trivycache" \
      --skip-dirs "target" \
      --cache-dir "$TRIVY_CACHE_DIR" \
      --offline-scan \
      --exit-code 0 \
      --no-progress \
      .
  artifacts:
    when: always
    paths:
      - trivy-report.json
  rules:
    - if: '$CI_COMMIT_BRANCH == "develop"'
    - if: '$CI_COMMIT_BRANCH =~ /^(feature|fix|hotfix|refactor|chore)\/.+$/'
    - when: never
Le contexte Trivy
Sur GitHub, Trivy a nécessité quelques adaptations liées au téléchargement de la base de vulnérabilités. Nous allons repartir de l'image officielle afin de vérifier si les versions récentes ont corrigé ce comportement.

Avec needs, on attend que le job develop-build se termine avant de lancer le scan Trivy.


Avec --scanners vuln,misconfig, Trivy analyse le système de fichiers du projet à la recherche de vulnérabilités et de mauvaises configurations dans les dépendances détectables.
→ Le scanner secret n'est pas activé ici, afin de conserver un scan ciblé et des logs plus lisibles.
Trivy effectue un scan du système de fichiers du projet en appliquant les répertoires explicitement exclus de la configuration, notamment .trivycache et target.
L'option --offline-scan empêche Trivy de contacter les dépôts Maven distants pour résoudre les dépendances. Elle ne désactive pas le téléchargement de la base de vulnérabilités Trivy.

On définit le scan avec un exit-code 0 (develop/staging) de manière à voir les vulnérabilités éventuelles sans bloquer le pipeline.

On sauvegarde un artefact :
  • trivy-report.json
Vous pouvez le récupérer ou le lire en ligne.
Le rapport généré pourrait être exploitable pour, par exemple :
Pousser automatiquement les résultats dans un outil de gestion des vulnérabilités centralisé (SonarQube).
Ou, avec un script maison, déclencher une alerte automatisée uniquement si une faille de niveau CRITICAL avec un correctif disponible est détectée.

Gestion de l'erreur "429 Too many requests"
Lors de la première mise en place du pipeline, une exécution naïve de Trivy sans préparation de l'environnement peut provoquer le crash suivant:
FATAL Error
remote Maven repository returned 429 Too Many Requests
...
The repository blocks all subsequent requests from 
this IP until the block clears.

En tentant d'analyser le projet, Trivy a essayé de télécharger
la dépendance "spring-data-bom". Les serveurs de Maven Central, voyant passer des requêtes répétées et non authentifiées provenant de l'IP du runner, ont activé leur mécanisme de protection (Rate Limiting) et bloqué le pipeline.

Résolution, en amont et mode hors-ligne
Pour contourner définitivement ce blocage, nous allons modifier légèrement la stratégie:
c'est le job de build Maven qui va télécharger proprement toutes les dépendances une bonne fois pour toutes, puis les léguer à Trivy.
Dans le job develop-build: On forçe Maven à récupérer l'intégralité des dépendances du projet et à basculer en mode autonome grâce aux commandes:
./mvnw -B dependency:resolve
./mvnw -B dependency:go-offline
ainsi que le .m2/repository/ dans ses artifacts pour le transmettre au job Trivy

Dans le job develop-trivy: et grâce à "needs", Trivy récupère ce répertoire local complet et exécute son scan avec l'argument --offline-scan.
- job: develop-build
  artifacts: true
Il n'a désormais plus besoin d'interroger internet pour analyser l'arbre des dépendances: le fichier spring-data-bom est déjà présent localement.
267.8M .m2/repository
Spring Data BOM available for Trivy
Schéma des relations entre develop-build et develop-trivy

Voici comment s'articule la relation technique entre le build et le scan de sécurité:
develop-build
  ├── dependency:resolve
  ├── dependency:go-offline
  ├── Exécution des tests & JaCoCo
  └── Export du dossier .m2/repository/
          │
          │  [Transmission via Artifact GitLab]
          ▼
    develop-trivy
        │
        ├── Récupération du dossier .m2/
        ├── Téléchargement/Actualisation de la Trivy DB (cache dédié)
        ├── Activation du mode --offline-scan
        └── Exécution de : trivy fs --format table
                │
                ├── Analyse du pom.xml    ➔ 0 vulnérabilité
                └── Analyse du Dockerfile ➔ 0 mauvaise configuration
La résolution Maven est effectuée par le build,
     son repository local est ensuite transmis à Trivy
Grâce à cette synergie, le pipeline est immunisé contre les limitations d'IP imposées par les registres publics.

Logs et analyse du résultat de Trivy
Une fois le job develop-trivy terminé, l'interface de GitLab nous affiche un rapport sous forme de tableau. Voici ce que l'on observe dans les logs:
234.65 KiB / 234.65 KiB [--------------------------------------------------------->] 100.00% ? p/s ?
234.65 KiB / 234.65 KiB [-----------------------------------------------] 100.00% 7.10 MiB p/s 200ms
2026-08-24T15:57:08Z	WARN	[pom] Dependency version cannot be determined.
Child dependencies will not be found.	details="https://trivy.dev/docs/v0.74/guide/coverage/language/java#empty-dependency-version"
2026-08-24T15:57:08Z	INFO	Number of language-specific files	num=1
2026-08-24T15:57:08Z	INFO	[pom] Detecting vulnerabilities...
2026-08-24T15:57:08Z	INFO	Detected config files	num=1
Report ┌────────────┬────────────┬─────────────────┬───────────────────┐ │ Target │ Type │ Vulnerabilities │ Misconfigurations │ ├────────────┼────────────┼─────────────────┼───────────────────┤ │ pom.xml │ pom │ 0 │ - │ ├────────────┼────────────┼─────────────────┼───────────────────┤ │ Dockerfile │ dockerfile │ - │ 0 │ └────────────┴────────────┴─────────────────┴───────────────────┘
Legend: - '-': Not scanned - '0': Clean (no security findings detected) Saving cache for successful job Creating cache trivy-db-4-protected... .trivycache/: found 746 matching artifact files and directories No URL provided, cache will not be uploaded to shared cache server. Cache will be stored only locally. Created cache Uploading artifacts for successful job... .trivycache/: found 746 matching artifact files and directories Uploading artifacts as "archive" to coordinator... 201 Created correlation_id=a3038dc86b61ba70-BRU id=16072721048 status=201 token=72_Vofrys

Le résultat indique:
0 vulnérabilité dans les dépendances que Trivy a pu identifier et analyser,
0 erreur de configuration (misconfiguration) détectée dans les fichiers de configuration analysés.
Le warning Maven rappelle toutefois qu'une partie de l'arbre de dépendances peut ne pas avoir pu être résolu.
Comprendre et gérer le WARNING [pom]
Trivy peut afficher le warning suivant:
WARN [pom] Dependency version cannot be determined.
Child dependencies will not be found.	
Signifie que Trivy n'est pas parvenu à déterminer la version d'une dépendance Maven.
D'après la documentation de Trivy, ce message apparaît dans deux scénarios précis :
  • Le POM parent permettant de déterminer la version n'est pas accessible.
  • La dépendance possède une contrainte stricte, avec plusieurs versions possibles.
→ Dans ce cas-ci, Trivy utilise une version vide et ne peut pas analyser les dépendances enfants associées.
Le spring-boot-starter-parent est bien présent, bien configuré et sa version est valide.
Dans cas-ci, la raison est que certaines dépendances ont été explicitement surchargées afin de corriger les vulnérabilités identifiées précédemment par Trivy.
Voir aussi: Comment fixer-les-vulnerabilites-trivy-d-une-application-java-springboot pour comprendre comment Trivy fonctionne avec les dépendances.
Fin d'exécution: Sauvegarde du cache Trivy
Le job se termine par la mise en cache de la base de données des vulnérabilités
Saving cache for successful job
Creating cache trivy-db-4-protected...
.trivycache/: found 746 matching artifact files and directories
Created cache

Uploading artifacts for successful job...
.trivycache/: found 746 matching artifact files and directories
Uploading artifacts as "archive" to coordinator... 201 Created ..
Tout comme pour le build Maven,
le dossier .trivycache/ contenant les définitions de vulnérabilités est stocké localement sur notre runner.
Lors du prochain build de la branche develop ou d'une feature/*,
Trivy n'aura pas besoin de retélécharger toute sa base de données, ce qui rendra le scan de sécurité instantané.

"Semgrep" pour l'analyse SAST (Java)
Pour l'analyse statique de sécurité (SAST), mon choix s'est porté sur Semgrep
Il fait partie des analyzers officiels utilisés par GitLab SAST. et, est basé sur Semgrep OSS et sur un jeu de règles géré par GitLab.
Il dispose d'un ensemble de règles permettant d'identifier différents patterns de vulnérabilités et permet également de définir des règles personnalisées.

Deux manières d'intégrer cet outil à nos pipelines
Option 1: Créer un job explicite
Cette approche consiste à déclarer manuellement le job, par exemple:
develop-semgrep:
  stage: security
  image:
    name: semgrep/semgrep:1.169.0
    entrypoint: [""]
  script:
    - semgrep --config=auto .
Avantages
On garde un contrôle total sur l'image Docker utilisée et la commande exacte exécutée. De plus, le rendu visuel dans les logs s'avère souvent plus épuré, car il s'affranchit des scripts internes de l'écosystème GitLab.
Les analyzers SAST scannent généralement le code source sans compilation préalable, sauf cas particuliers comme certains analyzers ou langages.
Option 2: Utiliser le composant officiel GitLab SAST
Le définir dans le fichier .gitlab-ci.yml:
include:
  //..
  - component: gitlab.com/components/sast/sast@3.4.0
Pour intégrer le scan de sécurité SAST (comme Semgrep), ce pipeline utilise les nouveaux CI/CD Components de GitLab à la place des anciens templates.
Le composant générera le job "semgrep-sast" dans tous les pipelines.

Le composant GitLab utilise l'analyzer Semgrep avec les règles gérées par GitLab. Le scanner détecte ensuite les langages et les fichiers pertinents du projet.
  • Il exécute Semgrep et génère un rapport SAST au format GitLab
  • Il remonte les vulnérabilités dans Security, les MR, les tableaux de bord,...

Mise en place du composant
L'avantage d'utiliser le composant est qu'il maintenu par GitLab et bénéficie donc, automatiquement des mises à jour des règles tout en s'intègrant directement aux fonctionnalités de sécurité de la plateforme. Pour ce projet, il apporte donc davantage de valeur qu'un job entièrement personnalisé.
Ajoutez le composant dans le fichier .gitlab-ci.yml
stages:
  - test
  - security
include:
  - component: gitlab.com/components/sast/sast@3.4.0
→ Le job sera exécuté dans le stage security, déjà présent
La documentation GitLab (enable-sast-by-editing-the-cicd-file) nous indique de remplacer @main par une version figée. Pour l'obtenir, allez sur sast/-/tags, depuis le menu de gauche : </> Code/Tags
On ajoute le job directement en dessous du composant.
L'ajouter dans ce fichier nous évite de le configurer dans chaque branche.
Le composant ajoute les jobs SAST à la configuration globale.
Le job sera ensuite exécuté selon ses propres règles.
semgrep-sast:
  stage: security
  variables:
    SAST_EXCLUDED_PATHS: "spec,test,tests,tmp,target,load-test"
    SAST_EXCLUDED_ANALYZERS: "spotbugs"
    SECURE_LOG_LEVEL: "info"
Pour diagnostiquer les problèmes d'analyzers,
     utilisez : SECURE_LOG_LEVEL: debug
Les variables :
GitLab SAST peut sélectionner plusieurs analyzers, notamment SpotBugs:
SpotBugs étant déjà exécuté dans le job "build Maven" du projet,
      je le désactive ici afin d'éviter deux analyses similaires
Semgrep peut relever des faux positifs dans notre code...
Par défaut, il exclut déjà les répertoires "spec,test,tests,tmp"
On lui ajoute simplement les dossiers "target,load-test", spécifique à notre projet.
SECURE_LOG_LEVEL: info permet de conserver un niveau de journalisation standard et d'éviter un niveau de détail excessif dans les logs.

Décryptage du résultat du log
Une fois le job exécuté avec succès, l'analyse du log de semgrep-sast nous montre précisément comment l'outil s'est comporté:
Checking out fec8f53a as detached HEAD (ref is develop)...
Removing "..\\..\\..\\cache\\val7304\\flashcards\\trivy-db-4-protected\\cache.zip"

Downloading artifacts for develop-build (16072721047)...
Direct download from cdn.artifacts.gitlab-static.net
Downloading artifacts from coordinator... ok
Executing "step_script" stage of the job script
Using effective pull policy of [always] for container registry.gitlab.com/security-products/semgrep:6
Using docker image sha256:177df3c8e9bdef43cf6a5e7c451074a76105c8b29392352741687c0f69ae8461
                                                           for registry.gitlab.com/security-products/semgrep:6 with digest...
$ /analyzer run
[INFO] [Semgrep] [2026-08-24T16:02:31Z] ▶ ┌─────────────┐
[INFO] [Semgrep] [2026-08-24T16:02:31Z] ▶ │ Scan Status │
[INFO] [Semgrep] [2026-08-24T16:02:31Z] ▶ └─────────────┘
[INFO] [Semgrep] [2026-08-24T16:02:31Z] ▶   Scanning 3772 files with 592 Code rules:
[INFO] [Semgrep] [2026-08-24T16:02:31Z] ▶
[INFO] [Semgrep] [2026-08-24T16:02:31Z] ▶   Language      Rules   Files          Origin   Rules
[INFO] [Semgrep] [2026-08-24T16:02:31Z] ▶  ─────────────────────────────        ────────────────
[INFO] [Semgrep] [2026-08-24T16:02:31Z] ▶          2      31          Custom     592
[INFO] [Semgrep] [2026-08-24T16:02:31Z] ▶   java            102      21
[INFO] [Semgrep] [2026-08-24T16:02:31Z] ▶   yaml              1      17
[INFO] [Semgrep] [2026-08-24T16:02:31Z] ▶   js               96       1
[INFO] [Semgrep] [2026-08-24T16:02:31Z] ▶
[INFO] [Semgrep] [2026-08-24T16:02:34Z] ▶ ┌──────────────┐
[INFO] [Semgrep] [2026-08-24T16:02:34Z] ▶ │ Scan Summary │
[INFO] [Semgrep] [2026-08-24T16:02:34Z] ▶ └──────────────┘
[INFO] [Semgrep] [2026-08-24T16:02:34Z] ▶  Scan completed successfully.
[INFO] [Semgrep] [2026-08-24T16:02:34Z] ▶  • Findings: 0 (0 blocking)
[INFO] [Semgrep] [2026-08-24T16:02:34Z] ▶  • Rules run: 201
[INFO] [Semgrep] [2026-08-24T16:02:34Z] ▶  • Targets scanned: 48
[INFO] [Semgrep] [2026-08-24T16:02:34Z] ▶  • Parsed lines: ~100.0%
[INFO] [Semgrep] [2026-08-24T16:02:34Z] ▶  • Scan skipped:
[INFO] [Semgrep] [2026-08-24T16:02:34Z] ▶    ◦ Matching --exclude patterns: 3
[INFO] [Semgrep] [2026-08-24T16:02:34Z] ▶    ◦ Files larger than  files 1.0 MB: 58
[INFO] [Semgrep] [2026-08-24T16:02:34Z] ▶    ◦ Files matching .semgrepignore patterns: 12
[INFO] [Semgrep] [2026-08-24T16:02:34Z] ▶ Ran 201 rules on 48 files: 0 findings.
[INFO] [Semgrep] [2026-08-24T16:02:36Z] ▶ Creating report

Uploading artifacts for successful job 
gl-sast-report.json: found 1 matching artifact files and directories

Sur les 592 règles initialement disponibles dans le jeu managé de GitLab, Semgrep a identifié les langages présents dans le code et a exécuté 201 règles ciblées sur 48 fichiers éligibles. Et le verdict tombe avec 0 vulnérabilité détectée.
Accéder et lire le rapport de sécurité: le job produit en sortie un artefact normé nommé gl-sast-report.json, qui peut être téléchargé depuis les artefacts du job.
Les résultats SAST peuvent également être exploités par les fonctionnalités de sécurité GitLab, notamment les vues de vulnérabilités et les Merge Requests.
Vous devez être authentifié sur GitLab pour pouvoir accéder et lire le contenu de ces rapports.
→ Depuis le menu : Build > Pipelines
→ onglet "job" (volet de droite)
download artifacts semgrep-sast
De là, on peut :
Voir ou télécharger le rapport gl-sast-report.json directement en ligne via le bouton Browse

À ce stade du projet, notre branche develop (ainsi que toutes les branches de fonctionnalités feature/* associées) bénéficie d'une couverture industrielle complète à chaque commit :
Un build Maven robuste chargé de compiler l'application
des contrôles de qualité (Spotless, tests, JaCoCo...).
d'un premier scan de sécurité avec Trivy.
d'une analyse SAST intégrée grâce à Semgrep.

Vue du pipeline
Tous les jobs du pipelines
En ouvrant la vue du pipeline, on constate que l'architecture remplit parfaitement son rôle:
Le composant s'est intégré de manière transparente et apparaît exactement au stage security.
Dans le prochain article, nous franchirons une nouvelle étape en nous attaquant à la configuration de la branche de staging, où nous verrons comment faire évoluer nos exigences d'environnement.

Laissez-moi un commentaire

En postant un commentaire anonyme, vous adhérez automatiquement aux conditions d'utilisation du site.