CVE-2026-27735
Path traversal in mcp-server-git git_add stages files outside the repo.
- CVSS 6.4
- CWE-22
- Input Validation and Sanitization
- Remote
Model Context Protocol Servers is a collection of reference implementations for the model context protocol (MCP). In mcp-server-git versions prior to 2026.1.14, the git_add tool did not validate that file paths provided in the files argument were within the repository boundaries. Because the tool used GitPython's repo.index.add() rather than the Git CLI, relative paths containing `../` sequences that resolve outside the repository were accepted and staged into the Git index. Users are advised to upgrade to 2026.1.14 or newer to remediate this issue.
- CWE
- CWE-22
- CVSS base score
- 6.4
- Published
- 2026-02-25
- OWASP
- A08 Software and Data Integrity Failures
- Orthogonal defect classification
- Checking
- Code defect classification
- Missing Check
- Category
- Input Validation and Sanitization
- Subcategory
- Path Traversal
- Accessibility scope
- Remote
- Impact
- Information Disclosure
- Affected component
- mcp-server-g
- Fixed by upgrading
- Yes
Solution
Upgrade mcp-server-git to version 2026.1.14 or newer.
Vulnerable code sample
import git
from typing import List
def git_add(repo: git.Repo, files: List[str]):
"""
A tool to stage files into the Git index.
This version is vulnerable as it does not validate that file paths
are within the repository's boundaries before staging them.
"""
# The 'files' list is passed directly to repo.index.add() without sanitation.
# An attacker could provide a relative path like ['../../../etc/passwd']
# to add a file from outside the intended repository directory.
repo.index.add(files)Patched code sample
import os
import shutil
import tempfile
import git
def fixed_git_add(repo_path: str, files_to_add: list[str]):
"""
Adds files to a Git repository's index, but only after validating
that each file path is securely within the repository's boundaries.
This function represents the fix for a path traversal vulnerability where
relative paths like '../' could be used to add files from outside the
repository.
Args:
repo_path (str): The path to the Git repository.
files_to_add (list[str]): A list of file paths to add, relative to the
repository root.
Raises:
ValueError: If any file path resolves to a location outside the
repository's working directory.
"""
repo = git.Repo(repo_path)
repo_root = os.path.realpath(repo.working_dir)
validated_files = []
for file_path in files_to_add:
# Create the full path by joining the repo root and the user-provided path
full_path = os.path.join(repo_root, file_path)
# Resolve the path to its canonical form, eliminating '..' sequences
resolved_path = os.path.realpath(full_path)
# THE FIX:
# Check if the resolved, absolute path of the file is still within
# the repository's root directory.
if not resolved_path.startswith(repo_root):
raise ValueError(
f"Path traversal attempt blocked. File '{file_path}' "
f"resolves to '{resolved_path}' which is outside the "
f"repository boundary '{repo_root}'."
)
# Check if the file actually exists before adding
if not os.path.exists(resolved_path) or os.path.isdir(resolved_path):
print(f"Warning: File '{file_path}' does not exist or is a directory. Skipping.")
continue
validated_files.append(file_path)
if validated_files:
print(f"Securely adding files: {validated_files}")
repo.index.add(validated_files)
repo.index.commit(f"Add files: {', '.join(validated_files)}")
print("Commit successful.")
else:
print("No valid files to add.")
# --- Demonstration Setup ---
def main():
"""
Sets up a temporary environment to demonstrate the vulnerability fix.
1. Creates a temporary directory.
2. Creates a 'secret.txt' file outside a new Git repository.
3. Creates 'my_repo' and initializes it as a Git repository.
4. Attempts to add both a legitimate file and the secret file using
a path traversal payload.
5. The fixed function should block the malicious attempt.
"""
# Create a temporary directory structure for the demonstration
temp_dir = tempfile.mkdtemp(prefix="cve_fix_demo_")
print(f"Created temporary directory: {temp_dir}")
# We will work inside this temp directory
original_cwd = os.getcwd()
os.chdir(temp_dir)
try:
# 1. Create a sensitive file OUTSIDE the repository
with open("sensitive_system_file.txt", "w") as f:
f.write("this is a secret")
print("Created 'sensitive_system_file.txt' outside the repo.")
# 2. Create and initialize the git repository
repo_dir = "my_repo"
os.makedirs(repo_dir)
repo = git.Repo.init(repo_dir)
print(f"Initialized Git repository in '{repo_dir}'")
# 3. Create a legitimate file INSIDE the repository
legit_file_path = os.path.join(repo_dir, "legit_file.txt")
with open(legit_file_path, "w") as f:
f.write("this is a public file")
print("Created 'legit_file.txt' inside the repo.")
# --- DEMONSTRATION OF THE FIX ---
# 4. Attempt to add the external file using a path traversal attack
malicious_path = "../sensitive_system_file.txt"
print(f"\n--- Attempting to add a malicious path: '{malicious_path}' ---")
try:
fixed_git_add(repo_dir, [malicious_path])
except ValueError as e:
print(f"SUCCESS: The fix worked as expected. Caught error: {e}")
# Verify the sensitive file was NOT added
staged_files = [item.a_path for item in repo.index.diff("HEAD")]
assert "sensitive_system_file.txt" not in staged_files
print("Verified that 'sensitive_system_file.txt' was not staged.")
# 5. Demonstrate normal operation with a legitimate file
print("\n--- Attempting to add a legitimate file: 'legit_file.txt' ---")
try:
fixed_git_add(repo_dir, ["legit_file.txt"])
# Verify the legitimate file WAS added
staged_files = [item.a_path for item in repo.index.diff(None)]
assert "legit_file.txt" in staged_files
print("Verified that 'legit_file.txt' was successfully staged and committed.")
except ValueError as e:
print(f"FAILURE: A legitimate operation was blocked. Error: {e}")
finally:
# Clean up the temporary directory
os.chdir(original_cwd)
shutil.rmtree(temp_dir)
print(f"\nCleaned up temporary directory: {temp_dir}")
if __name__ == "__main__":
main()Payload
{
"tool": "git_add",
"files": [
"../../../etc/passwd"
]
}
Cite this entry
@misc{vaitp:cve202627735,
title = {{Path traversal in mcp-server-git git_add stages files outside the repo.}},
author = {Bogaerts, Fr\'ed\'eric and Ivaki, Naghmeh and Fonseca, Jos\'e},
year = {2026},
note = {VAITP Python Vulnerability Dataset, entry CVE-2026-27735},
howpublished = {\url{https://netpack.pt/vaitp/vulnerability/CVE-2026-27735/}}
}
Introducing the "VAITP dataset": a specialized repository of Python vulnerabilities and patches, meticulously compiled for the use of the security research community. As Python's prominence grows, understanding and addressing potential security vulnerabilities become crucial. Crafted by and for the cybersecurity community, this dataset offers a valuable resource for researchers, analysts, and developers to analyze and mitigate the security risks associated with Python. Through the comprehensive exploration of vulnerabilities and corresponding patches, the VAITP dataset fosters a safer and more resilient Python ecosystem, encouraging collaborative advancements in programming security.
The supreme art of war is to subdue the enemy without fighting.
Sun Tzu – “The Art of War”
:: Shaping the future through research and ingenuity ::
