Skip to content

Latest commit

 

History

History
588 lines (511 loc) · 34.5 KB

developer.fr.adoc

File metadata and controls

588 lines (511 loc) · 34.5 KB

check-script — Guide développeur

1. Vue d’ensemble

check-script est une bibliothèque Dart (lib/) exposée par un CLI (bin/check_script.dart) et une interface Flutter (gui/). Elle orchestre des analyseurs de scripts shell et Python 3 (outils externes et règles intégrées), normalise leurs résultats en problèmes (Finding) classés par catégorie et sévérité, les dédoublonne, applique directives, contextes et configuration, calcule les notes et produit les rapports. Elle sait aussi corriger les défauts sûrs et comparer une analyse à une référence.

1.1. Principes

  • Exploiter les outils existants (ShellCheck, shfmt, bashate, checkbashisms, gitleaks, trufflehog, bash -n) plutôt que les réécrire ; les règles intégrées couvrent ce qu’ils ne traitent pas et assurent une analyse minimale sans eux.

  • Dégradation gracieuse : un outil absent est signalé, jamais bloquant ; sans shfmt, les règles structurelles retombent sur un lexer heuristique.

  • Bibliothèque d’abord : lib/ n’écrit jamais sur la console ; le CLI et l’interface ne sont que des façades.

  • Testabilité : l’exécution des commandes passe par CommandRunner (remplacé par un faux dans les tests) ; les parseurs sont des fonctions pures testées sur des sorties réelles enregistrées ; un corpus fige le comportement sur des scripts réels.

  • Pas de fuite de secrets : jamais recopiés dans les rapports, trufflehog lancé sans vérification réseau.

2. Arborescence

bin/check_script.dart              CLI : options, correction, sorties, seuils, codes retour
lib/check_script.dart              Exports de la bibliothèque
lib/src/
  model/finding.dart               Category, Severity, Finding (hint, url, fingerprint), ToolRun
  model/report.dart                ScriptReport, Comparison (+ toJson)
  script_info.dart                 ScriptInfo : shebang, Dialect, métriques, CRLF
  config.dart                      CheckConfig, Profile, ToolConfig, ScoringConfig, Thresholds (YAML)
  i18n.dart                        Lang, Tr (texte bilingue), Messages (libellés)
  engine.dart                      Engine : orchestration, progression, annulation, dédoublonnage,
                                   enrichissement (liens), contextes, configuration, tri
  scoring.dart                     scoreCategory, globalScore, sizeFactor, gradeFor
  suppressions.dart                Directives # check-script disable…
  baseline.dart                    Empreintes (FNV-1a), Baseline, comparaison
  fixer.dart                       --fix : corrections ShellCheck, intégrées, shfmt ; contrôle syntaxe
  diff.dart                        Diff unifié (Myers)
  discovery.dart                   collectScripts, isScriptFile, isExcluded (fichiers, dossiers,
                                   fichiers hôtes)
  watch.dart                       watchTargets : lots de fichiers modifiés (--watch, vue Dossier)
  embedded.dart                    Scripts intégrés (GitHub Actions, GitLab CI, Dockerfile,
                                   Makefile, Ansible) : script virtuel ligne à ligne, SEC023
  version.dart                     appVersion (synchronisée avec pubspec.yaml)
  rules/catalog.dart               Catalogue des règles intégrées (titre, conseil, contexte),
                                   ExecContext, escalades de sévérité par contexte
  rules/examples.dart              Exemples « à éviter / à écrire » par règle (shell et Python)
  rules/custom_rules.dart          CustomRule (rules.custom), CustomRulesAnalyzer
  rules/references.dart            Références CWE / OWASP / ANSSI des règles (ruleReferences),
                                   intitulés, liens, findingReferences, matchesReference (--ref)
  lsp/server.dart                  Serveur LSP (check-script lsp) : trames JSON-RPC,
                                   diagnostics (complets / pendant la frappe), survol,
                                   corrections rapides, formatage, checkScript/score
  highlight.dart                   Coloration : jetons par ligne (shell, Python, YAML,
                                   Dockerfile, Makefile), états multilignes (heredoc…)
  explain.dart                     ScoreExplanation, explainScore (coût et gain par règle, plan
                                   vers le niveau supérieur), sortByQuickWin
  result_cache.dart                ResultCache (rapports JSON par clé), fastHash (FNV-1a 64 bits)
  git.dart                         changedSince (--changed-since), normalizedPath
  history.dart                     FolderHistory, HistoryEntry (historique des dossiers : GUI,
                                   --history-dir, --dashboard)
  feedback.dart                    FalsePositive, FalsePositiveLog, anonymizeCode
  json_num.dart                    jsonInt : lecture tolérante des nombres du JSON des outils
                                   (nombre ou chaîne selon la version)
  reporters/dashboard.dart         renderDashboard, DashboardRepo, sparkline (tableau de bord)
  config_discovery.dart            findProjectConfig (.checkscript.yaml jusqu'à la racine git)
  ignore.dart                      IgnoreRules (motifs au format .gitignore : --exclude, exclude:,
                                   .checkscriptignore), utilisé par collectScripts et watchTargets
  init.dart                        check-script init : suggestProfile, renderInitConfig
  rule_doc.dart                    check-script explain : findRules, similarRuleIds, renderRuleDoc
  report_diff.dart                 check-script diff : diffReports, ReportsDiff, renderDiffText / Markdown / Json
  baseline_update.dart             --baseline-update : updateBaseline (resserrage de la référence)
  rules/same_rules.dart            sameRuleIds : la même règle dans plusieurs outils (Ruff S ↔
                                   Bandit B, Ruff PL ↔ Pylint, ShellCheck ↔ intégrées)
  rules/registry.dart              RuleEntry, knownRules : règles intégrées et codes externes
                                   classés, titres bilingues (onglet Règles de l'interface)
  analyzers/
    analyzer.dart                  Analyzer, AnalysisContext, CommandRunner, CancelToken
    shellcheck.dart                ShellCheck (JSON) et table de classement SCxxxx
    external_tools.dart            Contrôle syntaxique (bash -n, compilation Python), shfmt,
                                   bashate, checkbashisms et leurs parseurs
    python_tools.dart              Ruff, Bandit, Semgrep, mypy, Pyright, Pylint, Radon, Vermin :
                                   invocation, parseurs, classement, équivalents
    python_rules.dart              Règles intégrées Python ; PythonFacts (module ast via python3,
                                   repli ligne à ligne)
    python_deps.dart               Imports (pydeps : PYROB005, PYPOR002), dépendances déclarées
                                   (requirements, pyproject, PEP 723…), pip-audit
    commands.dart                  Inventaire des commandes externes (CommandUse), paquets
                                   dnf / apt, présence (command -v), ROB017
    ci_tools.dart                  hadolint (Dockerfile), actionlint et zizmor (workflows) :
                                   fichier hôte sur l'entrée standard, appliesTo, parseurs
    secrets.dart                   gitleaks, trufflehog
    ast.dart                       AstFacts : faits extraits de `shfmt --to-json`
    shell_lexer.dart               Lexer heuristique (repli sans shfmt)
    builtin_rules.dart             Détection des règles intégrées (lignes, structure, fichier)
  reporters/
    reporters.dart                 Terminal, Markdown, AsciiDoc, JSON ; OutputFormat, render()
    html.dart                      HTML autonome (filtres, source annotée)
    sarif.dart                     SARIF 2.1.0
    codeclimate.dart               GitLab Code Quality
    junit.dart                     JUnit XML, annotations GitHub Actions (--format github)
    pdf.dart                       Rapport d'audit PDF (paquet pdf) : polices du système,
                                   repli latin-1 ; renderBytes (reporters.dart) pour l'écrire
gui/                               Interface Flutter (voir <<gui>>)
editors/vscode/                    Extension VS Code (TypeScript, vscode-languageclient) :
                                   npm run package → .vsix ; npm test → test de bout en bout
                                   dans un vrai VS Code (CHECK_SCRIPT, VSCODE_EXECUTABLE)
editors/eclipse/                   Plugin Eclipse (LSP4E, éditeur générique) : Maven Tycho,
                                   mvn -f editors/eclipse/pom.xml verify → site de mise à jour
                                   (copier d'abord LICENSE dans bundles/fr.chla28.checkscript/)
editors/geany/lsp.conf             Configuration du greffon LSP Client de Geany
action.yml                         Action GitHub composite (compile check-script, annotations,
                                   résumé, SARIF) ; testée par .github/workflows/self-check.yml
tool/calibrate.dart                Calibrage sur le corpus
tool/false_positives.dart          Synthèse des faux positifs signalés (par règle)
test/                              Tests ; fixtures/ (dont sorties réelles des outils), corpus/
completions/                       Complétions bash et zsh
doc/                               Guides, page de manuel (check-script.1.fr.adoc), exemples CI
packaging/rpm/                     Specs RPM (CLI, interface)
scripts/                           build-dist.sh, build-rpm.sh, build-editors.sh, install.sh, uninstall.sh,
                                   pre-commit-hook.sh
.pre-commit-hooks.yaml             Hooks pour le framework pre-commit

3. Flux d’analyse

fichier ──► ScriptInfo.fromContent (shebang ou extension .py → Dialect, CRLF, lignes)
              │
              ▼
Engine.analyze ── pour chaque Analyzer du langage du script (analyzersFor),
              │   activé, dans l'ordre de priorité :
              │     shell  : syntax → shellcheck → gitleaks → trufflehog → checkbashisms
              │              → builtin (arbre shfmt si disponible) → shfmt → bashate
              │     python : syntax → bandit → ruff → semgrep → mypy → pyright → pylint
              │              → vermin → radon → gitleaks → trufflehog → builtin (ast)
              │     outils lancés en parallèle (Future.wait), résultats rangés dans
              │     l'ordre de priorité ; Engine(parallel: false) pour l'ordre strict
              │     onProgress(AnalysisProgress) ; CancelToken vérifié à chaque outil
              │   analyzeFiles(paths, jobs) : plusieurs scripts à la fois (défaut
              │     Engine.defaultJobs = processeurs / 4, entre 1 et 4), ordre conservé
              ▼
deduplicate   ── garde le premier de chaque groupe de doublons
Suppressions  ── directives # check-script disable… (comptées dans `suppressed`)
enrich        ── liens de documentation, conseils génériques des outils externes
escalate      ── sévérités relevées selon les contextes (root, cron, systemd)
applyConfig   ── rules.disabled, rules.overrides
fingerprintAll── empreintes stables (baseline, SARIF, Code Quality)
sortFindings  ── catégorie, sévérité, ligne
scoreCategory × 5, globalScore
              ▼
ScriptReport ──► Baseline.compare (si référence) ──► render(format)

4. Modèle

Finding

tool, ruleId, category, severity, line (0 = fichier), column, message, snippet, equivalents (codes d’autres outils désignant le même défaut), hint (conseil), url (documentation), fingerprint, endLine (fin de l’instruction multi-ligne signalée, pour le dédoublonnage : recouvrement de lignes, étendue ≤ maxDuplicateSpan), edits (correction concrète : TextEdit 1-based, fin exclusive ; issue du fix ShellCheck ou de attachFixes pour les corrections intégrées). Finding.fromJson relit toJson. fixPreview en tire les lignes avant / après ; exampleFor (rules/examples.dart) donne l’exemple générique d’une règle.

ToolRun

ok, missing, skipped, failed, disabled ; version ; nombre de problèmes ; detail (ast / lexer pour les règles intégrées).

ScriptReport

script, outils, problèmes, 5 CategoryScore, note globale, niveau, date, suppressed, profile, contexts, comparison.

5. Analyseurs

abstract class Analyzer {
  String get name;                                   // clé de configuration
  ToolLanguage get language;                         // shell (défaut), python ou any
  Future<AnalyzerResult> analyze(AnalysisContext ctx);
  Future<String?> version(CommandRunner runner, CheckConfig config); // null = absent, '?' = illisible
}

CommandRunner.run(exe, args, {stdin, cancel}) renvoie null si l’exécutable est introuvable. ProcessCommandRunner force LC_ALL=C, écrit stdin (utilisé par shfmt --to-json, bash -n, le formatage de --fix) et tue le processus quand le CancelToken est déclenché. Les analyseurs passent par ctx.run(…​), qui transmet le jeton.

Outil Invocation Parseur / classement

syntax

bash|sh|ksh|zsh -n FICHIER

parseSyntaxErrors → SYNTAX Robustesse/Critical

shellcheck

--format=json1 --enable=… --exclude=… --shell=… [--external-sources --source-path=SCRIPTDIR]

parseShellcheckJson + classifyShellcheck

gitleaks

detect --no-git --redact --exit-code=0 --report-format=json --report-path=/dev/stdout

parseGitleaks → GL:<règle> Critical

trufflehog

filesystem FICHIER --json --no-update --no-verification

parseTrufflehog → TH:<détecteur> High (Critical si vérifié)

checkbashisms

--extra [--force], scripts sh/dash/sans shebang

parseCheckbashisms → CB

builtin

shfmt --to-json -ln=… sur l’entrée standard (relance en bash pour un sh non POSIX)

AstFacts.fromJson puis runBuiltinRules

shfmt

-d -ln=… -i=<unité> ; relance en -ln=bash si l’analyse POSIX échoue

parseShfmtDiff (FORMAT), parseShfmtErrors (PARSE / DIALECT)

bashate

--ignore=… --max-line-length=N

parseBashate (2.x et 0.x)

syntax (Python)

python3 -c <pythonCompileScript> FICHIER : compile() sans pycache

parsePythonSyntaxError (ligne:colonne:message)

ruff

check --isolated --no-cache --target-version=pyNN --line-length=N --select=… --ignore=… --output-format=json puis format --diff

parseRuff + classifyRuff, ruffEquivalents ; corrections safe → edits

bandit

-f json -q -s …

parseBandit (sévérité × confiance, banditMap, secrets caviardés)

semgrep

scan --config=p/python --json --metrics=off --quiet --disable-version-check

parseSemgrep (catégorie des métadonnées ; fix → edits)

mypy

--output=json --no-incremental --cache-dir=/dev/null --ignore-missing-imports --check-untyped-defs [--python-version]

parseMypy (une ligne JSON par erreur)

pyright

--outputjson --pythonversion V

parsePyright (positions 0-based)

pylint

--output-format=json2 --score=n --persistent=n --py-version=V --disable=…

parsePylint + pylintMap

radon

cc -j -s, mi -j -s

parseRadonCc (CC, ≥ 11), parseRadonMi (MI)

vermin

--no-tips --violations -t=V- --format parsable

parseVermin (VERMIN)

builtin (Python)

python3 -c <pythonFactsScript> FICHIER

PythonFacts.fromJson (sinon lexPythonFacts) puis runPythonRules

5.1. Classement des codes ShellCheck

shellcheckMap classe explicitement les codes significatifs ; sinon classifyShellcheck applique : SC3xxx → Portabilité (Medium/Low), SC1xxx error → Robustesse/High, style → Maintenabilité/Low, sinon Robustesse selon le niveau. Les bashismes qui cassent sous dash (SC2112/2113 function, SC3010 [[, SC3011 <<<, SC3020 &>, SC3030/3054 tableaux, SC3005/3006 ((, SC3018, SC3001) sont High — décision issue du calibrage.

5.2. Dédoublonnage

Deux problèmes sont doublons si l’un des equivalents de l’un correspond au ruleId de l’autre (exact ou préfixe *) et qu’ils portent sur la même ligne — ou que l’un est « fichier » (ligne 0) ou une erreur SYNTAX. Même outil : même ligne et (même règle ou équivalent). Le premier rencontré est conservé ; l’ordre de defaultAnalyzers() fixe la priorité.

5.3. Équivalences Python

Ruff Snnn = Bandit Bnnn (mêmes numéros) ; Ruff PLxnnnn = Pylint xnnnn ; table _ruffToPylint pour les autres (E722 = W0702, B006 = W0102…) ; Semgrep équivaut aux codes Bandit et Ruff de sécurité de la même ligne ; mypy aux diagnostics Pyright (report*) ; les erreurs de version (match en 3.9) de Ruff et Pyright sont classées Portabilité et équivalent à VERMIN. Les messages Bandit B105–B107 et Semgrep « secret » sont caviardés, et leurs lignes masquées (_revealsSecret).

6. Règles intégrées

6.1. Catalogue

rules/catalog.dart décrit chaque règle : identifiant, catégorie, sévérité, titre et conseil de correction bilingues (Tr), contextes d’activation. La détection est séparée (builtin_rules.dart) ; add(id, line, …) y crée le Finding à partir du catalogue et ignore les règles dont le contexte n’est pas déclaré.

6.2. Arbre syntaxique (shfmt) et repli lexer

AstFacts (analyzers/ast.dart) parcourt le JSON de shfmt --to-json :

  • functions : nom, lignes de début et de fin ;

  • blocks : structures de contrôle et profondeur (else/elif au même niveau) ;

  • commands : nom littéral, arguments littéraux (null si expansion), hasExpansion, checked (condition de if/while, opérande gauche de && / || — opérateurs 10 et 11 —, commande niée), inLoop, inSubstitution.

Avec l’arbre, les règles de commande (SEC003, SEC012, ROB005, ROB007, ROB011, POR004, POR005, POR007, PERF003, PERF004) et structurelles (MNT002, MNT003) sont exactes ; les LineRule marquées astCovered sont alors ignorées. Sans shfmt, lexScript (commentaires, chaînes masquées dans bare, heredocs) et les expressions régulières prennent le relais.

6.3. Ajouter une règle

  1. Ajouter une RuleInfo dans ruleCatalog (titre et conseil fr/en, contextes éventuels).

  2. Ajouter sa détection : une LineRule dans lineRules (avec equivalents, et astCovered + un cas dans _astStructure si elle porte sur une commande), ou une règle fichier dans runBuiltinRules.

  3. Si elle doit être relevée dans un contexte, compléter contextEscalations.

  4. Tests : un cas déclencheur et un cas sain (builtin_rules_test.dart ou new_rules_test.dart), puis UPDATE_GOLDEN=1 dart test test/corpus_test.dart si le corpus change — en vérifiant chaque écart.

  5. Mettre à jour le tableau des règles de doc/user.fr.adoc.

7. Directives, contextes, profils

  • Suppressions.parse lit # check-script disable= (ligne, ou ligne de code suivante pour un commentaire seul), disable-next-line=, disable-file= ; jokers * et all.

  • ExecContext (root, cron, systemd) : règles conditionnelles (RuleInfo.contexts) et contextEscalations (sévérité seulement relevée).

  • Profile : CheckConfig.forProfile fournit la base ; CheckConfig.parse applique le YAML par-dessus (le profil de la ligne de commande prime sur la clé profile:).

8. Notation et calibrage

facteur(n)  = √(référence / max(référence, lignes de code))       référence = 100
pénalité    = Σ_règles poids(sévérité) × (1 + log2 occurrences) × (facteur si Medium/Low)
note        = clamp(10 − pénalité, 0, 10), arrondie au dixième
globale     = min(moyenne pondérée, pire catégorie + 1,5)            maxGapToWorst
niveau      = A ≥ 9, B ≥ 7,5, C ≥ 6, D ≥ 4, E

Le corpus test/corpus/ réunit des copies figées de scripts de la suite AuditTools et des scripts typiques (installeur curl | bash, sauvegarde cron ancienne, script init POSIX avec bashismes, bibliothèque propre), et 12 scripts Python py_* écrits pour le calibrage (outil propre, client d’API négligent, installeur dangereux, style Python 2, fonctionnalités 3.10+…). Semgrep est écarté du contrôle des niveaux (règles téléchargées). labels.yaml donne pour chacun, établi a priori en lisant le script, la plage de niveaux attendue (avec et sans contexte), les règles obligatoires et interdites.

dart run tool/calibrate.dart --details     # notes réelles vs plages attendues

Deux ajustements en sont issus : bashismes cassant dash classés High, et plafond de la note globale ramené de « pire + 2,5 » à « pire + 1,5 ». Tout changement de poids doit laisser le corpus conforme.

Le calibrage Python (0.7.0) a aligné les sévérités sur l’échelle shell sans toucher au modèle : secrets en dur Critical si la valeur est réaliste (comme SEC002), exécution de code ou de données High (comme SEC003), PYSEC001 Medium (comme SEC022), shebang python High ; il a ajouté PYROB003 (cron sans verrou, comme ROB014) et PYROB004 (code de retour de main() perdu), et compte les docstrings comme de la documentation. Trois plages a priori jugées trop sévères au regard de l’échelle shell ont été révisées, avec leur justification dans labels.yaml.

9. Correction automatique

fixScript enchaîne : remplacements ShellCheck (parseShellcheckFixes, applyEdits : positions 1-based, fin exclusive, tabulation = 1 colonne, éditions en conflit ignorées), corrections intégrées (applyBuiltinFixes, jamais dans un heredoc, une chaîne multi-lignes ou un commentaire ; replaceBackticks ignore les apostrophes et les backticks échappés), puis shfmt -ln -i sur l’entrée standard. Si <shell> -n accepte l’original mais refuse le résultat, FixResult.aborted est renseigné et rien n’est écrit. Pour un script Python (_fixPython) : ruff check --fix puis ruff format sur l’entrée standard (--stdin-filename=script.py), les corrections étant comptées d’après la sortie JSON ; contrôle par la compilation Python (syntaxRegression utilise SyntaxAnalyzer.commandFor). unifiedDiff (Myers) produit le diff de --dry-run et de l’interface.

10. Référence (baseline)

fingerprintAll calcule fnv1a64(outil|règle|ligne normalisée|rang) : le numéro de ligne n’entre pas dans l’empreinte. Baseline.parse relit un rapport JSON ; compare apparie par empreinte (multiensemble) et renvoie Comparison (nouveaux, corrigés, inchangés, notes précédentes). Les rapports ne détaillent alors que les nouveaux problèmes (detailFindings).

11. Rapports

render(reports, format, RenderOptions) ; OutputFormat : terminal, markdown, asciidoc, json, sarif, html, codeclimate (déduit de l’extension par OutputFormat.fromPath).

  • SARIF : une règle par couple outil/règle (help, helpUri, security-severity pour la sécurité), résultats avec partialFingerprints, startLine ≥ 1.

  • Code Quality : check_name, severity (blocker/critical/major/minor), fingerprint, location.lines.begin.

  • HTML : page unique sans ressource externe, thème clair/sombre, filtres, source annotée (ancres #s<i>-L<n>).

Format JSON (extrait) :

{
  "tool": "check-script", "version": "0.24.1",
  "reports": [{
    "file": "deploy.sh", "dialect": "bash",
    "lines": {"total": 16, "code": 15, "comments": 0},
    "profile": "standard", "contexts": ["root"],
    "global": {"score": 1.5, "grade": "E"},
    "categories": [{"category": "security", "score": 0.0, "total": 10,
                    "critical": 3, "high": 3, "medium": 3, "low": 1}],
    "suppressed": 0,
    "comparison": {"new": 1, "fixed": 2, "unchanged": 30, "previousGlobal": 2.5,
                   "previousScores": {"security": 0.0}},
    "tools": [{"tool": "shellcheck", "status": "ok", "version": "0.11.0", "findings": 14}],
    "findings": [{"tool": "builtin", "rule": "SEC001", "category": "security",
                  "severity": "critical", "line": 5, "column": 0, "message": "…",
                  "snippet": "…", "hint": "…", "fingerprint": "9f2c…",
                  "edits": [{"line": 5, "column": 12, "endLine": 5,
                             "endColumn": 12, "replacement": " || exit"}]}]
  }]
}

12. Internationalisation

Lang.fromEnvironment (LC_ALL > LC_MESSAGES > LANG) ; libellés dans Messages (bibliothèque) et S (interface) ; règles : Tr(fr, en) dans le catalogue ; messages des outils externes non traduits ; décimales à virgule en français.

13. Interface Flutter

gui/ dépend de la bibliothèque par path: ../.

gui/lib/main.dart                  Application, navigation, glisser-déposer, progression
gui/lib/app_state.dart             AppState (ChangeNotifier) et GuiSettings (shared_preferences)
gui/lib/strings.dart               Libellés propres à l'interface (fr/en)
gui/lib/screens/analysis_screen.dart   Source + onglets Synthèse / Problèmes, correction, export
gui/lib/screens/folder_screen.dart     Tableau triable, tendance (sortReports)
gui/lib/screens/settings_screen.dart   Langue, thème, profil, contextes, outils, YAML
gui/lib/editor.dart                detectEditor, editorCommandLine, openInEditor
gui/lib/code_style.dart            Police du code (JetBrains Mono embarquée, gui/fonts/, OFL),
                                   CodeFont (InheritedWidget), couleurs des jetons,
                                   polices mono installées (fc-list)
gui/lib/screens/rules_screen.dart      Règles : cases à cocher (GuiSettings.disabledRules, fusionné
                                       à rules.disabled par buildConfig ; celles de baseConfig
                                       sont verrouillées), recherche, filtres, règles rencontrées
                                       (AppState.seenRules, mémorisées)
gui/lib/widgets/                   radar_chart (RadarPainter), score_panel, source_view,
                                   findings_list, fix_panel (code de correction), split_view
                                   (séparateur déplaçable), common (couleurs, badges, DeltaChip)

AppState construit la configuration (buildConfig : YAML éventuel, profil, contextes, outils désactivés), lance Engine avec onProgress et un CancelToken, gère la référence, proposeFix / applyFix (copie .orig) et l’export. Attention : Flutter possède un widget Baseline ; importer package:flutter/material.dart avec hide Baseline là où la classe de la bibliothèque est utilisée.

Tests : flutter test (état, tri, radar, écrans). Captures hors écran avec les vraies polices système :

cd gui && SCREENSHOT_DIR=/tmp/shots flutter test test/screenshot_test.dart

Les captures françaises sont écrites dans SCREENSHOT_DIR, les anglaises dans SCREENSHOT_DIR/en.

L’interface est déclarée dans le lanceur MainGUI (kToolSpecs, id check_script, commande check-script-gui, bundle check_script_gui).

14. Tests

dart test                                  # bibliothèque et CLI
UPDATE_GOLDEN=1 dart test test/corpus_test.dart   # régénérer les instantanés (après vérification)
dart analyze --fatal-infos
(cd gui && flutter analyze && flutter test)
Fichier Contenu

builtin_rules_test.dart, new_rules_test.dart

Chaque règle (déclencheur / cas sain), dialectes, contextes, profils, entropie, catalogue

ast_test.dart

Faits extraits de l’arbre shfmt réel ; règles sur l’arbre ; loadAst

parsers_test.dart

Parseurs sur les sorties réelles des outils

suppressions_test.dart

Directives

baseline_test.dart

Empreintes, comparaison

fixer_test.dart

Remplacements ShellCheck, corrections intégrées, abandon, diff

formats_test.dart

SARIF, HTML, conseils, secrets externes, progression, annulation

engine_test.dart, scoring_test.dart, config_test.dart

Moteur, notation, configuration

reporters_test.dart, cli_test.dart

Rapports, options et codes de sortie

corpus_test.dart

Instantanés, règles obligatoires / interdites, plages de niveaux

v080_test.dart

Directives obsolètes (MNT011), explication de la note et gain rapide, SARIF fixes, cache des résultats, --changed-since (dépôt git réel), configuration de projet

improvements_test.dart

Même règle entre outils, exclusions transmises, --fix et règles désactivées, doublons multi-lignes, # noqa / # nosec, analyses parallèles, cache Semgrep, export YAML (aller-retour), rapport HTML de dossier, calibrage Python

python_test.dart

Parseurs des outils Python (sorties réelles de fixtures/outputs/python/, capturées sur fixtures/bad.py), dédoublonnage, secrets, règles intégrées Python (lexer et ast réel), --fix, découverte, pythonTarget

fix_suggestions_test.dart

Corrections par problème, exemples, rapport HTML

ci_examples_test.dart

Exemples de CI (doc/ci/, ci.fr.adoc) : chaque commande check-script est acceptée par l’analyseur d’options réel, les entrées de l’action et les hooks pre-commit existent, les versions citées sont à jour

scoring_golden_test.dart

Non-régression du barème : instantané des notes du corpus pour les trois profils (test/corpus/expected_scores.txt, UPDATE_GOLDEN=1 pour régénérer), constantes du barème = guides utilisateur

tools_test.dart

check-script diff, --baseline-update, --fix --interactive, --format gitlab

subcommands_test.dart

Motifs .gitignore, --exclude / .checkscriptignore / exclude:, check-script explain et check-script init

integration_test.dart

Vrais outils (ignorés s’ils sont absents), dont Ruff, Bandit, mypy, Radon, Vermin sur bad.py / good.py

Toute correction de règle s’accompagne d’un test qui échoue avant la correction ; tout faux positif trouvé sur un script réel rejoint le corpus.

15. Commits et publication

Les messages de commit suivent les Conventional Commits : types, portées et description en anglais (l’historique jusqu’à 0.18.5 est en français), sans point final, première ligne de 100 caractères au plus.

feat(gui): ajoute l'onglet Règles
fix(flatpak): lance les outils par leur chemin absolu
docs(user): précise --changed-since
feat(engine)!: change le format JSON        ← « ! » ou « BREAKING CHANGE: » : incompatible

Types : feat, fix, perf, refactor, docs, test, build, ci, style, chore, revert. Portées usuelles : engine, cli, gui, python, rules, reporters, flatpak, appimage, rpm, ci, doc.

  • Contrôle local : scripts/install-git-hooks.sh active .githooks/ (core.hooksPath) ; le hook commit-msg refuse un message non conforme (git commit --no-verify pour passer outre).

  • Contrôle CI : .github/workflows/commits.yml vérifie les nouveaux commits de chaque push et pull request (tool/commit_lint.dart --range).

  • Publication : dart run tool/release.dart --dry-run affiche la version suivante et la section du CHANGELOG ; sans --dry-run, synchronise les fichiers de version, écrit le CHANGELOG, commite chore(release): X.Y.Z et tague vX.Y.Z (--push pour pousser, ce qui déclenche la publication GitHub). Version : feat → mineure, autre changement → correctif, incompatible → majeure (mineure tant que la version est 0.x). Les notes rédigées sous « ## Unreleased » sont gardées en tête de la section.

16. Build et packaging

scripts/build-dist.sh [VERSION] [--rpm] [--editors] [--install] [--skip-tests] [--no-gui] (appelé aussi par l’orchestrateur AuditTools/scripts/build-dist.sh) : analyse et tests (Dart et Flutter), binaire CLI, bundle Flutter (version injectée par --build-name), page de manuel (asciidoctor), complétions, documentation et exemples CI, .desktop et icône, SBOM et rapport CVE si sbom-generator est présent, archive, RPM (scripts/build-rpm.sh, depuis git archive HEAD : seul le contenu commité est empaqueté).

scripts/build-editors.sh [VERSION] [--skip-tests] [--no-vscode] [--no-eclipse] : extension VS Code (npm ci, tests de bout en bout sous xvfb-run ou DISPLAY, vsce package) et site de mise à jour Eclipse (Tycho) → dist/check-script-VERSION.vsix et dist/check-script-eclipse-VERSION.zip.

scripts/build-appimage.sh [--skip-build] : bundle Flutter et CLI compilée dans un AppDir (AppRun : interface par défaut, CLI avec --cli ou via un lien nommé check-script), .desktop, icône et métadonnées AppStream communs (packaging/linux/), puis appimagetool (PATH, APPIMAGETOOL, ou téléchargé dans build/appimage/) → dist/CheckScript-VERSION-ARCH.AppImage.

scripts/build-flatpak.sh [--install] [--skip-build] : mêmes binaires, manifeste packaging/flatpak/fr.chla28.check_script_gui.yml (runtime GNOME 50, qui fournit GTK 3), flatpak-builder (ou org.flatpak.Builder), runtime et SDK installés depuis Flathub pour l’utilisateur → dist/CheckScript-VERSION-ARCH.flatpak. Dans le Flatpak, ProcessCommandRunner lance les outils sur l’hôte (hostInvocation : flatpak-spawn --host --env=LC_ALL=C …, présence vérifiée par command -v sur l’hôte) ; les lanceurs placent TMPDIR dans le cache de l’application, dont le chemin est identique dans le bac à sable et sur l’hôte.

Dans le Flatpak, le PATH de l’utilisateur sur l’hôte est lu une fois par un shell de connexion (bash -lc, pour ~/.local/bin, ~/bin…) et chaque outil est lancé par son chemin absolu résolu sur l’hôte : le portail cherche l’exécutable avec son propre PATH minimal, pas celui de --env.

Publication (.github/workflows/release.yml) : à chaque tag v*, tests (sans outils externes, pour des résultats indépendants des versions d’outils des distributions), puis archive, AppImage et Flatpak (Ubuntu 24.04), RPM (conteneur Fedora 44), et release GitHub avec les fichiers et l’extrait du CHANGELOG. Flutter est figé (FLUTTER_VERSION).

Les deux scripts partagent scripts/package-common.sh et affichent la glibc minimale requise par les binaires (2.34 actuellement : Rocky 9, runtimes Flatpak) ; pour une AppImage largement compatible, construire sur la distribution la plus ancienne visée.

Les specs neutralisent le strip et le debuginfo (ils corrompent les binaires AOT Dart) ; le spec de l’interface désactive le scan automatique des dépendances (moteur Flutter embarqué). Licence : LGPL-3.0-or-later (LICENSE), reprise par les specs et les métadonnées AppStream.

La version figure dans pubspec.yaml, gui/pubspec.yaml, lib/src/version.dart et CHANGELOG.md ; build-dist.sh signale un écart entre les deux premiers fichiers Dart.