{"slug": "a-nested-git-file-let-the-git-mcp-server-out-of-its-repository-until-2026-10-28", "title": "A nested .git file let the Git MCP server out of its --repository until 2026.10.28. Which version does your uvx run?", "summary": "A security advisory published 2026-10-28 (GHSA-w766-5x43-whvw, CVSS 5.3, reported by ShieldPi) disclosed that the reference Git MCP server's --repository confinement could be bypassed by a nested directory containing a plain-text .git file pointing at another repository, letting tool calls read committed content and create branches outside the allowed path. The flaw affects all mcp-server-git versions before 2026.10.28, the patched release, and the reporter noted a realistic trigger is a deployment that also grants a filesystem MCP server write access to the same directory, since its write_file has no .git exclusion. The advisory follows two earlier path-validation fixes (2025.12.18 and 2026.1.14), and the README's unpinned uvx launch command means users may not know which version they run.", "body_md": "`mcp-server-git --repository <path>` is the one setting that keeps the reference Git MCP server inside a single repository. An advisory published on 2026-10-28 showed that a nested directory with a `.git` file pointing elsewhere let tool calls read and change a repository outside that boundary; the fix is version 2026.10.28. The configuration the project's README shows runs the server through `uvx` with no version in it, and a sibling advisory for the filesystem server, a regression, shows what each way of pinning costs.\n\nThe advisory is [GHSA-w766-5x43-whvw, mcp-server-git: nested gitdir file bypasses --repository confinement](https://github.com/modelcontextprotocol/servers/security/advisories/GHSA-w766-5x43-whvw), reported by ShieldPi on 2026-10-27 and rated Moderate (CVSS 5.3). Affected versions are everything before 2026.10.28, which is the patched version. On PyPI, 2026.10.28 was uploaded on 2026-10-28 at 21:06 UTC, and it was still the newest release when we checked again on 2026-11-03 (UTC).\n\nIt is not the first advisory about keeping this server inside a repository. The repository's advisory list also has [GHSA-j22h-9j4x-23w5](https://github.com/modelcontextprotocol/servers/security/advisories/GHSA-j22h-9j4x-23w5), \"Missing path validation when using --repository flag in mcp-server-git\", published on 2025-12-17 with 2025.12.18 as the patched version, and [GHSA-vjqx-cfc4-9h6v](https://github.com/modelcontextprotocol/servers/security/advisories/GHSA-vjqx-cfc4-9h6v), \"Path traversal in git_add allows staging files outside repository boundaries in mcp-server-git\", published on 2026-02-25 with 2026.1.14 as the patched version. Each fix closed one way out of the boundary. That history is the practical reason to know which version your launch command runs, which is where this article ends up.\n\nThe mechanism rests on an ordinary Git feature. Git's [repository layout documentation](https://git-scm.com/docs/gitrepository-layout) explains that instead of a `.git` directory \"you can have a plain text file .git at the root of your working tree, containing gitdir:  to point at the real directory that has the repository.\" Submodules and worktrees use this every day.\n\nThe server checked the path it was given, not the repository Git ended up opening. In the report's words, the path check \"canonicalize[s] only the caller-supplied `repo_path` and check[s] that it is inside `allowed_repository`\", and then the repository is opened, at which point \"GitPython resolves that redirection later. There is no check after opening the repository to ensure that the repository actually selected by Git is the allowed one.\"\n\nThe reporter confirmed two effects through the real MCP interface: reading committed content from the outside repository, and creating a branch in it. \"This bypasses the server's claimed repository boundary and permits unauthorized read and modification with the MCP process's privileges.\" The report is also explicit about what it does not claim: it \"does not claim arbitrary host-file write or code execution\", and it is \"not claimed to be an unauthenticated standalone remote exploit\".\n\nThe interesting part of the report is how the nested `.git` file gets there. The caller has to be able to \"create a nested directory and a `.git` file inside the allowed repository, or cause such content to be placed there\", and to guess the path of another repository the process can reach.\n\nThe report names one realistic setup: \"a deployment that also gives a filesystem MCP server write access to the same allowed directory.\" At the tested commit, the filesystem server's `write_file` \"has no special `.git` exclusion.\"\n\nThat is a plausible configuration. Both servers live in the same reference repository, both are easy to point at the same project in Claude Code or Cursor, and each server's boundary looks fine on its own. The combination is what crossed it. An agent steered by text it read, in an issue or a file, would only need to use both tools in the right order.\n\nThe [mcp-server-git README](https://github.com/modelcontextprotocol/servers/tree/main/src/git) is worth rereading, because it states the boundary more precisely than most people configure it.\n\n\"Without `--repository`, the server reads and writes any Git repository the server process can reach.\" The README adds that this holds \"for the tools that change a repository too\", and that \"`--repository` is the only confinement the server offers\".\n\nIt also widens what the flag admits: \"At startup the server walks up to the enclosing repository root\", so \"A path inside the repository therefore allows tool calls anywhere in that repository, not only under the subdirectory given.\" If you pointed `--repository` at `packages/api` hoping to keep the server out of the rest of a monorepo, it does not do that.\n\nAnd it describes the fix: a tool call's `repo_path` must be inside the repository, \"and so must the directories Git then uses for it: a repository nested inside whose `.git` file, `commondir` or `core.worktree` points outside is refused.\"\n\nHere is the configuration the README gives for Claude Desktop, and the same command is what most people pass to `claude mcp add`:\n\n```\n{\n  \"command\": \"uvx\",\n  \"args\": [\"mcp-server-git\", \"--repository\", \"path/to/git/repo\"]\n}\n```\n\nThere is no version in it. uv's [tools guide](https://docs.astral.sh/uv/guides/tools/) gives two ways to say which version you want: \"To run a tool at a specific version, use command@\", and \"To run a tool at the latest version, use command@latest\". It also notes that \"the @ syntax cannot be used for anything other than an exact version.\" The bare name is a third choice, and the guide we read does not promise it means the newest release, which is reason enough to write one of the other two.\n\nThe filesystem server's launch commands follow the same pattern with `npx -y`. npm's [` npm exec` documentation](https://docs.npmjs.com/cli/v11/commands/npm-exec) says the CLI \"utilizes its internal package cache when using the package name specified\", and describes `--prefer-online` as the flag that \"Forces staleness checks for packages, making the cli look for updates immediately even if the package is already in the cache.\"\n\nTo see what Claude Code launches, `claude mcp list` shows each configured server and its status, and `claude mcp get <name>` shows the details of one, according to the [MCP documentation](https://code.claude.com/docs/en/mcp). The command and arguments live in `.mcp.json` for project scope and in `~/.claude.json` for local and user scope.\n\nThree days before the Git advisory, the same repository published [GHSA-4gxg-m5jx-9j57](https://github.com/modelcontextprotocol/servers/security/advisories/GHSA-4gxg-m5jx-9j57) for `server-filesystem`. On a case-insensitive file system, such as the macOS and Windows defaults, versions 2026.8.31, 1.0.0 and 2.0.0 could create files outside the allowed directories through a symlink spelled with different letter case. The detail that matters for pinning is one line: \"It is a regression: 2026.7.10 refuses these requests.\" The fix is 2.0.1.\n\nThe npm registry's release times make the trade-off concrete:\n\n| How the version is chosen | What it got from July to October 2026 | \n|---|---|\n| Exact pin to 2026.7.10 | Never had this regression, and never got any later change either (whether it is affected by the other advisories published that day is a separate question) | \n| Always the newest release | Picked up the regression on 2026-08-31 and the fix on 2026-10-25 (2.0.1) | \n| Exact pin to 2.0.1 | Fixed, and stays there until someone edits the command | \n\nNeither column is free. An exact pin keeps out a bad release, including a malicious one pushed with a stolen token, but it also keeps out the next security fix until a person moves it. Floating gets fixes as soon as they ship and regressions just as fast. What we would avoid is the bare name, where the version is decided by a cache you did not choose.\n\n`claude mcp get <name>` for your Git server and look at its arguments. If it says `mcp-server-git` with no version, you do not know whether you are on 2026.10.28.`uvx mcp-server-git@2026.10.28 --repository /path/to/repo`. For always-newest, `uvx mcp-server-git@latest ...`. With `claude mcp add`, the command goes after `--`, as in `claude mcp add git -- uvx mcp-server-git@2026.10.28 --repository /path/to/repo`.` server-filesystem`.`--repository`.\nWe read the boundary flags of MCP servers as promises from one server. This advisory is a reminder that they are promises about one server's own checks, and that the agent holding several tools at once can combine them in ways no single server sees.\n\nDo you pin your MCP server versions in `.mcp.json`, float on `@latest`, or leave the bare name? If you have a way to get told when a pinned server has a fix waiting, describe it in the comments below.", "url": "https://wpnews.pro/news/a-nested-git-file-let-the-git-mcp-server-out-of-its-repository-until-2026-10-28", "canonical_source": "https://dev.to/rulestack/a-nested-git-file-let-the-git-mcp-server-out-of-its-repository-until-20261028-which-version-3e1l", "published_at": "2026-11-04 02:17:00+00:00", "updated_at": "2026-11-04 02:22:01.763168+00:00", "lang": "en", "topics": ["ai-agents", "ai-tools", "ai-safety", "agent-protocols", "developer-tools"], "entities": ["mcp-server-git", "Model Context Protocol", "ShieldPi", "GitPython", "PyPI", "GitHub", "Claude Code", "Cursor"], "also_reported_by": [], "alternates": {"html": "https://wpnews.pro/news/a-nested-git-file-let-the-git-mcp-server-out-of-its-repository-until-2026-10-28", "markdown": "https://wpnews.pro/news/a-nested-git-file-let-the-git-mcp-server-out-of-its-repository-until-2026-10-28.md", "text": "https://wpnews.pro/news/a-nested-git-file-let-the-git-mcp-server-out-of-its-repository-until-2026-10-28.txt", "jsonld": "https://wpnews.pro/news/a-nested-git-file-let-the-git-mcp-server-out-of-its-repository-until-2026-10-28.jsonld"}}