Contactez-nous
-

MegaLinter : un linter pour les gouverner tous

MegaLinter : un linter pour les gouverner tous

Sommaire

MegaLinter est un outil Open Source créé par OX Security qui analyse votre code, configuration et scripts dans vos dépôts Git ou localement, afin de garantir que toutes les sources de vos projets soient propres et formatées quel que soit l'IDE / outil utilisé.

Il gère actuellement 59 langages, 24 formats, 20 outils de formatage (voir section Liens pour plus de détails), s’intègre facilement à GitHub ou GitLab (pour ne citer qu’eux) et apporte des fonctionnalités supplémentaires telles que :

  • La génération d’un rapport lors de l'exécution de vos pipelines CI/CD et l'intégration de ce dernier en tant que commentaire dans les Pull / Merge Requests
  • La possibilité de corriger et formater automatiquement le code directement depuis le dépôt Git source ou sous forme d’artefact contenant les fichiers corrigés (sous réserve de compatibilité des linters du langage supporté)
  • La détection d'informations sensibles, failles de sécurité, problèmes de conformité

Au-delà des fonctionnalités apportées, il permet également d’enseigner et/ou rappeler aux différents intervenants les bonnes pratiques du langage/format utilisé afin de maintenir un code propre et facilement maintenable a posteriori.

Dans la suite de cet article, nous allons découvrir comment installer et utiliser MegaLinter localement puis dans un pipeline GitLab sur un dépôt Git contenant des fichiers Markdown et Terraform erronés ainsi que des informations d'identification AWS.

Go 🚀

Prérequis

Avant de commencer, assurez-vous d’avoir pré-installé les éléments suivants :

  • Docker
  • NodeJS
  • Un dépôt Git sur GitLab.com (ou votre propre instance et un runner opérationnel)

Installation

Localement

OX Security fournit un utilitaire appelé “mega-linter-runner” permettant de simplifier l'exécution de MegaLinter en utilisant une image Docker contenant tous les outils nécessaires pour n’importe quel langage supporté ou une image Docker “flavored” plus légère ne contenant que le strict nécessaire pour un langage en particulier (voir la section Liens pour la liste complète des images “flavored” disponibles et leur contenu)

npm install mega-linter-runner -g

Pipeline GitLab

L’utilitaire mega-linter-runner dispose d’un mode interactif permettant de créer les fichiers nécessaires au bon fonctionnement de MegaLinter dans un pipeline. Il suffit alors d'exécuter la commande suivante et de répondre aux quelques questions :

mega-linter-runner --install

Configuration intéractive d’un dépôt Git

Configuration

MegaLinter étant installé, passons maintenant à sa configuration. Celle-ci s’effectue par l’intermédiaire du fichier .mega-linter.yml à la racine de votre dépôt Git et/ou un ensemble de variables d’environnement.

Durant l’étape précédente, nous avons utilisé mega-linter-runner pour générer les fichiers nécessaires. Regardons de plus près le contenu de ces derniers :

.mega-linter.yml : ce fichier contient les paramètres que va utiliser MegaLinter lors de son exécution.

  • APPLY_FIXES active la correction automatique des problèmes détectés
  • ENABLE permet d’activer la vérification uniquement pour les langages ou formats spécifiés
  • ENABLE_LINTERS permet d’activer la vérification uniquement pour les linters spécifiés
  • DISABLE permet de désactiver la vérification de règles spécifiques
  • SHOW_ELAPSED_TIME affiche la durée d'exécution de chaque vérification
  • FILEIO_REPORTER permet d’envoyer le rapport généré vers file.io (dans notre cas nous utiliserons un artefact GitLab)
  • DISABLE_ERRORS indique si le pipeline doit continuer lors de la détection d’un problème

L’avantage de pouvoir activer ou désactiver un langage, un format ou un linter est de réduire le temps d'exécution du pipeline car les images “flavored” fournies par l’éditeur peuvent tout de même contenir des outils non pertinents mais qui seront toutefois exécutés. C’est une alternative à construire votre propre image Docker qui nécessiterait un temps non négligeable afin de suivre les évolutions de l’outil.

La liste complète des paramètres est disponible dans la section Liens.

.mega-linter.yml
# Configuration file for MegaLinter
#
# See all available variables at https://megalinter.io/latest/config-file/ and in
# linters documentation

# all, none, or list of linter keys
APPLY_FIXES: all

# If you use ENABLE variable, all other languages/formats/tooling-formats will
# be disabled by default
# ENABLE:

# If you use ENABLE_LINTERS variable, all other linters will be disabled by
# default
# ENABLE_LINTERS:

#
DISABLE:

 # - COPYPASTE # Uncomment to disable checks of excessive copy-pastes
 # - SPELL # Uncomment to disable checks of spelling mistakes

SHOW_ELAPSED_TIME: true

FILEIO_REPORTER: false

# Uncomment if you want MegaLinter to detect errors but not block CI to pass
# DISABLE_ERRORS: true

.gitlab-ci.yml : ce fichier contient le job qui va être exécuté par le pipeline. Toutes les variables définies dans le fichier .mega-linter.yml peuvent être surchargées par des variables d'environnement GitLab à définir dans la section “variables” ou au niveau du dépôt Git directement.

Il manque cependant la variable d’environnement GITLAB_ACCESS_TOKEN_MEGALINTER qui va permettre à MegaLinter d’ajouter automatiquement un commentaire si une Merge Request est ouverte sur la branche Git en cours d’utilisation. Cette variable étant sensible, elle est à définir au niveau du dépôt Git pour ne pas faire apparaître le token d’accès. Le token nécessaire doit être créé avec le périmètre “api” (voir la section Liens pour plus de détails).

.gitlab-ci.yml

# MegaLinter GitLab CI job configuration file
# More info at https://megalinter.io

mega-linter:
 stage: test

  # You can override MegaLinter flavor used to have faster performances
 # More info at https://megalinter.io/latest/flavors/
 image: oxsecurity/megalinter-terraform:v7

 
# if script: ["true"] does not work, you may try this instead:
 #   script: [ "/bin/bash /entrypoint.sh" ]
 script: ["true"]

  variables:
   # All available variables are described in documentation
   # https://megalinter.io/latest/config-file/
   DEFAULT_WORKSPACE: $CI_PROJECT_DIR

    # ADD YOUR CUSTOM ENV VARIABLES HERE TO OVERRIDE VALUES OF .mega-linter.yml
   # AT THE ROOT OF YOUR REPOSITORY

 
artifacts:
   when: always
   paths:
     - report
   expire_in: 1 week

Les trois fichiers suivants sont une liste de fichiers à exclure lors des commits vers le dépôt Git mais aussi lors des vérifications effectuées par MegaLinter. Le dossier megalinter-reports étant le répertoire de travail de MegaLinter, celui-ci peut devenir rapidement volumineux. Il est préférable d’utiliser un artefact ayant une durée de vie définie plutôt que de voir la taille du dépôt augmenter et son utilisation se dégrader.

.gitignore

.gitignore

megalinter-reports/

.cspell.json

.cspell.json

{

 "ignorePaths": [
   "**/node_modules/**",
   "**/vscode-extension/**",
   "**/.git/**",
   "**/.pnpm-lock.json",
   ".vscode",
   "megalinter",
   "package-lock.json",
   "report"
 ],
 "language": "en",
 "noConfigSearch": true,
 "words": ["megalinter", "oxsecurity"],
 "version": "0.2"
}

.jscpd.json : 

.jscpd.json

{
 "threshold": 0,
 "reporters": ["html", "markdown"],
 "ignore": [
   "**/node_modules/**",
   "**/.git/**",
   "**/.rbenv/**",
   "**/.venv/**",
   "**/*cache*/**",
   "**/.github/**",
   "**/.idea/**",
   "**/report/**",
   "**/*.svg"
 ]
}

Il est possible de passer des paramètres aux différents linters exécutés par MegaLinter en ajoutant leur fichier de configuration habituel au même niveau que le fichier .mega-linter.yml.

Utilisation

Nous avons maintenant un MegaLinter prêt à l’emploi intégré à notre dépôt Git qui n’attend plus qu’à être exécuté. Mais avant de pousser nos fichiers vers le dépôt Git et d’observer le pipeline déclenché, nous allons exécuter MegaLinter localement et analyser le rapport généré.

Commençons par ajouter les deux fichiers suivants contenant volontairement des erreurs :

main.tf

teraform {
 required_providers {
     aws = {
       source   = "hashicorp/aws"
       version    = "~> 5.0"
     }
 }
}
README.md

### README.md ##

Hello, World!
credentials

[default]
aws_access_key_id = AKIAIOSFODNN7EXAMPLE
aws_secret_access_key = wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY

Localement

Lançons l'exécution de MegaLinter en lui indiquant que nous voulons vérifier uniquement les fichiers Markdown, Terraform mais aussi la présence d’informations sensibles :

mega-linter-runner --flavor terraform -e "'ENABLE=MARKDOWN,REPOSITORY,TERRAFORM'"

Comme vous pouvez le constater ci-dessous, 4 erreurs sont détectées par MegaLinter :

  • Des informations d'identification AWS dans le fichier credentials
  • Le fichier README.md devrait commencer par “# README.md”
  • Le fichier main.tf devrait contenir un attribut “required_version” pour définir la version de Terraform à utiliser
  • Le bloc de configuration “terraform” n’est pas correctement orthographié
Rapport d'exécution de MegaLinter
...

❌ Linted [REPOSITORY] files with [gitleaks]: Found 1 error(s) - (0.52s)
- Using [gitleaks v8.18.2] https://megalinter.io/7.11.1/descriptors/repository_gitleaks
- MegaLinter key: [REPOSITORY_GITLEAKS]
- Rules config: [.gitleaks.toml]
--Error detail:

   ○
   │╲
   │ ○
   ○ ░
   ░    gitleaks

Finding:     aws_access_key_id = REDACTED
Secret:      REDACTED
RuleID:      aws-access-token
Entropy:     3.684184
File:        credentials
Line:        2
Commit:      a96922914d91d3a465585aba84713cc2c559be76
Author:      anonymous
Email:       anonymous@incognito.org
Date:        2024-05-20T21:35:19Z
Fingerprint: a96922914d91d3a465585aba84713cc2c559be76:credentials:aws-access-token:2

9:35PM INF 8 commits scanned.
9:35PM INF scan completed in 389ms
9:35PM WRN leaks found: 1

...

⚠️ Linted [MARKDOWN] files with [markdownlint]: Found 1 non blocking error(s) - (0.59s)
- Using [markdownlint v0.39.0] https://megalinter.io/7.11.1/descriptors/markdown_markdownlint
- MegaLinter key: [MARKDOWN_MARKDOWNLINT]
- Rules config: [.markdownlint.json]
- Number of files analyzed: [1]
--Error detail:
README.md:1 MD041/first-line-heading/first-line-h1 First line in a file should be a top-level heading [Context: "### README.md ##"]

...

❌ Linted [TERRAFORM] files with [tflint]: Found 1 error(s) - (3.17s)
- Using [tflint v0.50.3] https://megalinter.io/7.11.1/descriptors/terraform_tflint
- MegaLinter key: [TERRAFORM_TFLINT]
- Rules config: [.tflint.hcl]
[Pre][TERRAFORM_TFLINT] run: [tflint --init --config /action/lib/.automation/.tflint.hcl] in cwd [/]
[Pre][TERRAFORM_TFLINT] result:
Installing "aws" plugin...
Installed "aws" (source: github.com/terraform-linters/tflint-ruleset-aws, version: 0.23.1)

--Error detail:

1 issue(s) found:

Warning: terraform "required_version" attribute is required (terraform_required_version)

  on  line 0:
  (source code not available)

Reference: https://github.com/terraform-linters/tflint-ruleset-terraform/blob/v0.5.0/docs/rules/terraform_required_version.md

...


❌ Linted [TERRAFORM] files with [terrascan]: Found 1 error(s) - (6.07s)
- Using [terrascan v1.18.11] https://megalinter.io/7.11.1/descriptors/terraform_terrascan
- MegaLinter key: [TERRAFORM_TERRASCAN]
- Rules config: identified by [terrascan]
[Pre][TERRAFORM_TERRASCAN] run: [terrascan] in cwd [/]
[Pre][TERRAFORM_TERRASCAN] result:

--Error detail:

Scan Errors -

IaC Type            : terraform
Directory           : /tmp/lint
Error Message       : diagnostic errors while loading terraform config dir '/tmp/lint'. error from terraform:
/tmp/lint/main.tf:1,1-9: Unsupported block type; Blocks of type "teraform" are not expected here. Did you mean "terraform"?

...


+----SUMMARY----+--------------------------+---------------+-------+-------+--------+--------------+

| Descriptor    | Linter                   | Mode          | Files | Fixed | Errors | Elapsed time |

+---------------+--------------------------+---------------+-------+-------+--------+--------------+

| ⚠️ MARKDOWN   | markdownlint             | list_of_files |     1 |     0 |      1 |        1.01s |

| ✅ MARKDOWN   | markdown-link-check      | list_of_files |     1 |       |      0 |        1.43s |

| ✅ MARKDOWN   | markdown-table-formatter | list_of_files |     1 |     0 |      0 |        0.86s |

| ✅ REPOSITORY | checkov                  | project       |   n/a |       |      0 |       11.93s |

| ❌ REPOSITORY | gitleaks                 | project       |   n/a |       |      1 |        0.52s |

| ✅ REPOSITORY | git_diff                 | project       |   n/a |       |      0 |        0.07s |

| ✅ REPOSITORY | grype                    | project       |   n/a |       |      0 |       12.03s |

| ✅ REPOSITORY | kics                     | project       |   n/a |       |      0 |       22.32s |

| ✅ REPOSITORY | secretlint               | project       |   n/a |       |      0 |        1.49s |

| ✅ REPOSITORY | trivy                    | project       |   n/a |       |      0 |        9.26s |

| ✅ REPOSITORY | trivy-sbom               | project       |   n/a |       |      0 |        8.31s |

| ✅ REPOSITORY | trufflehog               | project       |   n/a |       |      0 |        6.55s |

| ✅ TERRAFORM  | terraform-fmt            | file          |     1 |     1 |      0 |        0.52s |

| ❌ TERRAFORM  | terrascan                | project       |   n/a |       |      1 |        8.87s |

| ❌ TERRAFORM  | tflint                   | project       |   n/a |       |      1 |        5.77s |

+---------------+--------------------------+---------------+-------+-------+--------+--------------+

[Updated Sources Reporter] copied 1 fixed source files in folder /tmp/lint/megalinter-reports/updated_sources.
Download it from artifacts then copy-paste it in your local repo to apply linters updates
❌ Error(s) have been found during linting

Le paramètre “APPLY_FIXES” ayant été précédemment défini à “all”, MegaLinter a également exécuté la commande “fmt” de Terraform afin de corriger l’indentation du fichier main.tf.

Fichier main.tf corrigé par MegaLinter

teraform {
 required_providers {
   aws = {
     source  = "hashicorp/aws"
     version = "~> 5.0"
   }
 }
}

Pipeline GitLab

Une fois la variable d’environnement GITLAB_ACCESS_TOKEN_MEGALINTER définie, il ne vous reste plus qu’à pousser vos fichiers vers votre dépôt Git. Si une Merge Request est ouverte sur la branche Git source, vous verrez alors apparaître un commentaire dans cette dernière récapitulant les erreurs détectées.

Commentaire ajouté par MegaLinter dans une Merge Request GitLab

Les fichiers corrigés par MegaLinter seront quant à eux disponibles en tant qu’artefact que vous pourrez télécharger pour remplacer vos fichiers comme indiqué en fin de pipeline :

[Updated Sources Reporter] copied 1 fixed source files in folder 
/builds/.../megalinter/megalinter-reports/updated_sources.
Download it from artifacts then copy-paste it in your local repo
to apply linters updates

Conclusion

Comme vous l’aurez constaté, MegaLinter est très simple à mettre en place et permet d’obtenir rapidement un premier aperçu de la qualité du code d’un projet.

Je ne peux que vous conseiller de l’essayer sur vos propres projets, que vous soyez en train d’en développer un nouveau ou de refactoriser du code existant, MegaLinter vous aidera à écrire un code plus propre, plus robuste et plus maintenable.

Liens