VAITP Dataset

← Back to the dataset

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.

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 ::