flawopen.com/Teardowns/cve-2026-102437-deepseek-reasonix-git-filter-command-injection
CVE-2026-102437: Reasonix diff-view Git injection
A poisoned .gitattributes/.git/config pair can run a clean filter when Reasonix renders a diff. Update npm to 1.39.3 or Studio to 2.21.0.[1], [2], [3], [6], [7]
Think of a package-sorting desk that promises only to compare two labels. A parcel can carry a routing note that tells the desk to run a registered helper before it compares the contents. Reasonix disabled some Git helpers, but Git's per-file clean filter was a separate route. When the diff screen asked Git to prepare a file for comparison, that filter could run under the developer's account.[1], [3]
Core Concepts & Subsystem Terms
Git clean filter- A configured transformation Git may run to convert a file's working-tree contents into the form stored in the repository.[1], [3]
.gitattributes- A repository-tracked file that can assign a named filter to matching paths.[1]
.git/config- Repository-local Git configuration, which can define what command a named filter invokes. A normal clone does not copy this local file.[1], [3]
Diff viewer- A feature that asks Git to prepare and compare a file's working-tree and committed contents.[1]
Framework mappings
- CWE reference for the weakness described in this guide: CWE-78
Root Cause Analysis
Reasonix hardened Git diff calls with --no-ext-diff and --no-textconv , but those flags govern external diff and text-conversion mechanisms; they do not disable Git's clean content filters. The workspace changes view called git diff for a selected path. If repository content assigned that path a filter through .gitattributes and local Git configuration supplied the driver's command, Git could invoke it while constructing the comparison blob. The CVE describes a local attacker and requires user interaction to view the diff. GitLab's disclosure says ordinary cloning does not transfer .git/config ; archives, synced directories, caches, or prepared devcontainers can carry it. It also describes a same-user local-agent route that can write local repository config. The impact is command execution with the Reasonix user's privileges, not remote code execution merely from opening a GitLab-hosted repository.[1], [3]
Step-by-Step Attack Flow
Source Code: Flaw vs. Secure Implementation
This is an illustrative argument builder, not a drop-in patch. The caller must enumerate every driver selected by effective .gitattributes, .git/info/attributes, and global/system attribute files, setting discoveryComplete only after a successful complete scan. This function validates the supplied names and emits command-line overrides; it does not discover or verify the driver set. The vulnerable call is quoted from GitLab’s disclosure; the defensive pattern is an original synthesis of maintainer guidance and the merged hardening PRs.[1], [3], [4], [5]
Source excerpt: desktop/workspace_changes.go
// Source call quoted by GitLab Threat Research; this is the call site, not a complete function.
args := []string{"-C", base, "diff", "--no-ext-diff", "--no-textconv", "--relative", "HEAD", "--", filepath.FromSlash(rel)}
// Illustrative Go argument builder, not a verbatim vendor patch.
package gitguard
import (
"errors"
"fmt"
"path/filepath"
"regexp"
)
// The caller must discover every filter selected by effective attributes and pass the complete set.
// This helper only neutralizes those driver keys across all Git config scopes.
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)
}
// Git config subsection names are case-sensitive: do not fold driver names.
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
}
Engineering & System Hardening Checklist
- ✓✓ Update the Reasonix npm package to 1.39.3 or later, or Reasonix Studio to 2.21.0 or later, following the project's current release guidance.[2], [6], [7]
- ✓✓ When a tool runs Git against a user-controlled workspace, treat repository-local configuration and tracked attributes as untrusted inputs.[1], [3]
- ✓✓ For each host-Git call that can transform content, identify filters selected by effective
.gitattributes,.git/info/attributes, and global/system attribute files; neutralize their driver keys across system, global, local, and worktree config; fail closed if discovery is incomplete.[1], [3], [4], [5] - ✓✓ Keep
--no-ext-diffand--no-textconvas separate controls, but do not treat either flag as a substitute for filter-driver handling.[1] - ✓✓ Add regression tests using real Git that prove a hostile repository-configured filter is not invoked through diff, status, staging, checkout, and other content-processing paths supported by the wrapper.[4], [5]
- ✓✓ Review archive, shared-folder, cache, and devcontainer workflows for locally supplied
.git/configfiles; do not describe a plain clone as carrying this file.[1], [3]
Sources
- GitLab Threat Research — DeepSeek-Reasonix vulnerability details and affected call site
- GitLab Advisory Database — CVE-2026-102437
- DeepSeek-Reasonix maintainer advisory — GHSA-grg2-7gc6-36m6
- DeepSeek-Reasonix Studio host Git hardening PR #11110
- DeepSeek-Reasonix 1.x host Git hardening PR #11114
- DeepSeek-Reasonix 1.39.3 release
- DeepSeek-Reasonix Studio 2.21.0 release
- MITRE CWE-78 — Improper Neutralization of Special Elements used in an OS Command