mistralai/mistral-vibe — security scan
Repository: mistralai/mistral-vibe — 4.4k★, Apache-2.0, a minimal CLI coding agent by Mistral. Python core with a git-integration (“teleport”) layer, a session/snapshot system, and a remote-workflow event translator.
Commit scanned: cafb6d414747 (HEAD of main at scan time)
Scan date: 2026-06-12
Disclosure status: Public courtesy issue filed. Corporate-backed (Mistral) but no SECURITY.md or PVR is published; the curated items are routine public-CVE dependency bumps (not exploit-shaped), so a public courtesy issue — scoped tightly with reachability nuance — is the appropriate channel.
Summary
| Severity | Count |
|---|---|
| Critical | 0 |
| High | 17 |
| Medium | 4 |
| Low | 0 |
| Info | 0 (filtered) |
21 findings. After curation: a dependency tail (gitpython, urllib3, starlette, idna — and pyjwt, which is the interesting one) plus two source findings that are both correct-by-design. For a coding agent — a category whose obvious attack surface is shell/git execution and file writes — the notable result is that the git wiring is done right: GitPython’s argv API (no shell=True, no string-built commands), and a content-hash that explicitly declares itself non-cryptographic. The real signal is a routine “bump the pinned deps + wire Dependabot” item, sharpened by a clean reachability split.
Top findings (curated)
1. gitpython==3.1.47 — three advisories, library is used (reachable)
Tool: pip-audit / Trivy Verdict: Real version-match on a directly-used library.
mistral-vibe’s vibe/core/teleport/git.py uses GitPython heavily — Repo(...), repo.git.add, repo.git.diff, repo.git.branch, repo.git.rev_list, repo.git.rev_parse. The pinned 3.1.47 carries CVE-2026-44243, CVE-2026-44244, and GHSA-mv93-w799-cj2w (newline injection in config_writer()).
Applying the reachability discipline from the ouroboros triage: the config_writer newline-injection (GHSA-mv93) is not on a reachable path — mistral-vibe never calls config_writer / set_value / add_section (the only _set_value_at_path in the tree is a JSON-path setter in remote_workflow_event_translator.py, unrelated to git config). The other two CVEs sit in GitPython’s broader surface that is exercised. So: bump gitpython to clear all three (low cost), but the most-severe-sounding one (config injection) isn’t actually exploitable here.
2. pyjwt==2.12.1 — four advisories, declared but not imported in source (low reachability)
Tool: pip-audit Verdict: Version-match, but not reachable through mistral-vibe’s own code.
pyjwt is pinned directly in pyproject.toml and carries four PYSEC advisories (PYSEC-2026-175, -177, -178, -179). But there is no import jwt anywhere in vibe/ — the source never constructs or verifies a JWT. In uv.lock, pyjwt appears as { name = "pyjwt", extra = ["crypto"] }, i.e. it is required by a dependency, not by mistral-vibe’s own code.
This is the same shape as the ouroboros LiteLLM/anthropic case: a real version-match whose vulnerable code paths mistral-vibe doesn’t exercise. The honest framing is “bump it during a dep refresh for hygiene, but it is not an applicable attack surface in this project’s own usage.” Worth bumping anyway since it’s a one-line change, but it shouldn’t be presented as an applicable vulnerability.
3. urllib3==2.6.3 ×3, starlette==1.0.0 ×3, idna==3.13 — standard transitive web tail
Verdict: Real version-matches, mixed reachability, single coordinated bump clears them. urllib3 (info-disclosure via cross-origin redirect, DoS) and starlette (security-restriction bypass; sse-starlette is used for the agent’s streaming) are the standard 2026 web tail. idna CVE-2026-45409. A uv lock --upgrade clears the lot.
No Dependabot is configured (no .github/dependabot.yml), consistent with the pattern across the recent series (MemoryBear, agency-swarm, ouroboros, Clawith). Wiring it is the durable fix.
4-N. Source findings — both correct-by-design
| Finding | File | Verdict |
|---|---|---|
insecure-hash-algorithm-sha1 |
vibe/core/session/image_snapshot.py:51 |
FP — and a model of how to do it right. The call is hashlib.sha1(data, usedforsecurity=False).hexdigest() to build a content-addressed attachment filename. The developer explicitly passed usedforsecurity=False — the exact signal that tells both Python’s FIPS layer and a human reader “this is a dedup hash, not a security primitive.” The Semgrep rule fires on the sha1 token regardless; the code is exemplary. |
non-literal-import |
vibe/core/experiments/__init__.py:54 |
By-design — experiment/plugin discovery via dynamic import. |
The git-execution wiring deserves an explicit callout as a non-finding: a coding agent is precisely the category where you expect to find subprocess(..., shell=True) with LLM-influenced arguments or string-concatenated git commands. mistral-vibe has neither — every git operation goes through GitPython’s argv-based API (repo.git.diff("HEAD", binary=True), repo.git.rev_list("--count", ref_range)), which handles argument boundaries safely. That’s the right way to build it, and worth noting because the recurring agent-shell-tool by-design class (fast-agent, agency-swarm, ReMe, LazyLLM) is the opposite design choice — those ship an intentional LLM-driven shell; mistral-vibe’s git layer is a constrained, safe-by-construction API surface.
Patterns observed
A coding agent that doesn’t ship an LLM-driven shell is a different (and cleaner) shape. Five prior agent-framework scans surfaced an intentional shell tool as a by-design finding. mistral-vibe is the first where the agent’s system-interaction surface (git) is a constrained typed API rather than a raw shell. There’s no shell-tool finding to contextualize because there’s no shell tool — the git operations are a fixed vocabulary of GitPython calls. For a curated scan, “the dangerous thing you’d expect isn’t here, and here’s the safer construction they used instead” is as useful a data point as any finding.
The reachability split is unusually clean on this target. Two dependency clusters, two different reachability verdicts: gitpython is imported and exercised (reachable — bump it), pyjwt is declared but never imported in source and only pulled transitively (low reachability — bump for hygiene, not applicable as an attack surface). This is the SCA-vs-reachability lesson applying within a single scan rather than across maintainer feedback — exactly the distinction the curated report should make up front, so a reader doesn’t see “4 pyjwt highs” and conclude the agent has a JWT-auth vulnerability.
usedforsecurity=False is the credential-key-prefix idea applied to hashes. Just as a phc_… PostHog key or a New Relic OTLP token should downgrade a gitleaks hit, an explicit hashlib.sha1(data, usedforsecurity=False) should downgrade (or suppress) the insecure-hash-algorithm-sha1 Semgrep finding. The flag is an unambiguous machine-readable declaration of intent; a rule that ignores it generates exactly the kind of FP that erodes trust in the scanner. A concrete tool-side refinement: suppress the sha1/md5 hash-algorithm rules when the call site passes usedforsecurity=False.
Notes on the tool
- The 2026-06-11 UTF-8 fix held on this target:
reports/mistralai-mistral-vibe/raw/semgrep.jsonwas 55,884 bytes (healthy), and per the Clawith lesson I verified the byte size before trusting the low semgrep count. The two semgrep findings are genuine (and both benign on inspection) — not a silent crash. - A
usedforsecurity=False-aware suppression for the hash-algorithm rules is the concrete scanner refinement this scan suggests, in the same family as the proposedphc_-prefix gitleaks downgrade and thelogger-credential-leakdowngrade already shipped. - Reachability annotation (declared-vs-imported) would have flagged the
pyjwtcluster automatically: a check for “is this packageimported anywhere in the scanned source?” cleanly separates thegitpython(imported) from thepyjwt(not imported) case.
Disclosure timeline
- 2026-06-12 — Scan run at commit
cafb6d414747;semgrep.jsonbyte-size verified healthy (post-UTF-8-fix). Findings curated with reachability analysis:gitpythonimported/reachable,pyjwtdeclared-but-unused, the two source findings correct-by-design. - 2026-06-12 — Public courtesy issue #775 filed on mistralai/mistral-vibe with the dependency refresh (gitpython + web tail), the
pyjwtdeclared-but-unimported note, and a one-line Dependabot suggestion. The two source findings are noted as non-issues in this write-up.
Reproduce
git clone https://github.com/elfrost/ai-patchlab
cd ai-patchlab
pip install -e ".[dev]"
python scanner/run_scan.py \
--from-git-url "https://github.com/mistralai/mistral-vibe" \
--reports-dir reports/mistralai-mistral-vibe \
--min-severity medium \
--ignore-samples
External tools (Semgrep, Gitleaks, Trivy, pip-audit) need to be installed separately — see the project README.