FlawOpen

flawopen.com/Teardowns/cve-2026-102437-deepseek-reasonix-git-filter-command-injection

● CVE-2026-102437 · CVSS 7.8[2] · Haute
Recherche · FlawOpen

CVE-2026-102437 : injection Git dans les diffs Reasonix

Un duo .gitattributes/.git/config piégé peut lancer un filtre clean à l’affichage d’un diff Reasonix. Mettez npm à 1.39.3 ou Studio à 2.21.0.[1], [2], [3], [6], [7]

💡 Explication en Langage Simple (ELI5)

Imaginez une table de tri qui promet de comparer uniquement deux étiquettes. Un colis peut contenir une consigne d’acheminement demandant à la table de lancer un auxiliaire enregistré avant de comparer le contenu. Reasonix désactivait certains auxiliaires Git, mais le filtre clean appliqué par fichier constituait une autre voie. Lorsque l’écran de diff demandait à Git de préparer le fichier, ce filtre pouvait s’exécuter avec les droits du développeur.[1], [3]

Concepts Clés et Termes

Filtre clean Git
Transformation configurée que Git peut exécuter pour convertir le contenu d’un fichier de l’arbre de travail vers sa forme stockée dans le dépôt.[1], [3]
.gitattributes
Fichier suivi par le dépôt qui peut associer un filtre nommé à certains chemins.[1]
.git/config
Configuration locale du dépôt pouvant définir la commande appelée par un filtre. Un clone classique ne copie pas ce fichier local.[1], [3]
Visionneuse de diff
Fonction qui demande à Git de préparer et comparer le contenu de travail d’un fichier avec son contenu validé.[1]

Correspondances de référentiels

  • Référence CWE pour la faiblesse décrite dans ce guide : CWE-78

Analyse de Cause Racine

Reasonix renforçait les appels git diff avec --no-ext-diff et --no-textconv , mais ces options contrôlent les mécanismes de diff externe et de conversion de texte ; elles ne désactivent pas les filtres de contenu clean de Git. La vue des changements de l’espace de travail appelait git diff pour un chemin sélectionné. Si le contenu du dépôt associait un filtre au chemin via .gitattributes et que la configuration Git locale définissait la commande de ce pilote, Git pouvait l’exécuter lors de la création du blob de comparaison. Le CVE décrit un attaquant local et exige que l’utilisateur ouvre le diff. La publication de GitLab précise qu’un clone normal ne transfère pas .git/config ; une archive, un dossier synchronisé, un cache ou un devcontainer préparé peut le contenir. Elle décrit aussi un agent local disposant des mêmes droits, capable d’écrire cette configuration dans le dépôt. L’impact est l’exécution de commandes avec les droits de l’utilisateur de Reasonix, et non une exécution distante provoquée par la simple ouverture d’un dépôt hébergé sur GitLab.[1], [3]

Déroulement de l'Attaque Étape par Étape

Étape 1

L’espace de travail reçoit un état de dépôt non fiable

Le développeur ouvre un projet dont les attributs suivis associent un fichier à un filtre et dont la configuration Git locale définit ce filtre. L’avis du mainteneur exige ces deux éléments ; un clone normal ne fournit pas, à lui seul, la configuration locale.[1], [3]

Étape 2

Le développeur demande un diff

La vue des changements de Reasonix lance le chemin Git diff pour le fichier sélectionné. Cette action dans l’interface constitue l’interaction requise par le CVE.[1], [3]

Étape 3

Git applique le filtre clean

Git prépare le contenu à comparer et peut appeler le filtre clean configuré. --no-ext-diff et --no-textconv désactivent d’autres mécanismes et ne suppriment pas ce filtre.[1], [3]

Étape 4

Le filtre s’exécute avec les droits de l’utilisateur

La commande associée au filtre s’exécute dans le contexte du processus Reasonix. La conséquence documentée est l’exécution locale de commandes avec les droits de cet utilisateur.[1], [2], [3]

Code Source : Vulnérable vs Sécurisé

Ce constructeur d’arguments est illustratif et ne constitue pas un correctif prêt à l’emploi. L’appelant doit énumérer tous les drivers sélectionnés par .gitattributes, .git/info/attributes et les fichiers d’attributs globaux/système, puis ne définir discoveryComplete qu’après une analyse complète réussie. Cette fonction valide les noms fournis et génère des substitutions de configuration en ligne de commande ; elle ne découvre ni ne vérifie l’ensemble des drivers. L’appel vulnérable est cité dans la divulgation de GitLab ; le modèle défensif synthétise les recommandations des mainteneurs et leurs PR de durcissement fusionnées.[1], [3], [4], [5]

Extrait source: desktop/workspace_changes.go

IMPLÉMENTATION VULNÉRABLE
// Appel source cité par GitLab Threat Research ; il s’agit du site d’appel, pas d’une fonction complète.
args := []string{"-C", base, "diff", "--no-ext-diff", "--no-textconv", "--relative", "HEAD", "--", filepath.FromSlash(rel)}
PATCH SÉCURISÉ ET ROBUSTE
// Constructeur Go d’arguments donné à titre d’illustration, pas un correctif fournisseur reproduit à l’identique.
package gitguard

import (
    "errors"
    "fmt"
    "path/filepath"
    "regexp"
)

// L’appelant doit découvrir tous les filtres sélectionnés par les attributs effectifs et transmettre l’ensemble complet.
// Cet utilitaire ne fait que neutraliser ces clés de driver dans toutes les portées de configuration Git.
var filterDriverName = regexp.MustCompile(`^[A-Za-z0-9_-]{1,64}$`)

func hardenedDiffArgs(base, rel string, drivers []string, discoveryComplete bool) ([]string, error) {
    if !discoveryComplete {
        return nil, errors.New("cannot safely diff: filter-driver discovery failed")
    }
    args := []string{"-C", base}
    seen := map[string]struct{}{}
    for _, driver := range drivers {
        if !filterDriverName.MatchString(driver) {
            return nil, fmt.Errorf("cannot safely diff: unsupported Git filter driver name %q", driver)
        }
        // Les noms de sous-section de Git config sont sensibles à la casse : ne normalisez pas les noms de driver.
        key := driver
        if _, ok := seen[key]; ok {
            continue
        }
        seen[key] = struct{}{}
        for _, setting := range []string{"clean", "smudge", "process", "required"} {
            args = append(args, "-c", "filter."+driver+"."+setting+"=")
        }
    }
    return append(args, "diff", "--no-ext-diff", "--no-textconv", "--relative", "HEAD", "--", filepath.FromSlash(rel)), nil
}

Liste de Contrôle de Sécurité pour l'Ingénierie

Sources