Skip to content

Argument/option injection in git ref parameters leads to arbitrary file write (git_log / git_diff / git_show) #53

Description

@geo-chen

reported on 5 June 2026 https://github.com/cyanheads/git-mcp-server/security/advisories/GHSA-265x-r5m8-w5v2 - no response, so trying here:

Summary

The ref/object parameters of several read tools (git_log branch, git_diff source/target, git_show object) are typed as a plain string with no leading-dash or format validation, and are placed into the git command line as positional arguments. git therefore interprets a value beginning with "-" as an option. Using git's --output= option, an attacker who can influence these parameters can write git output to an arbitrary file outside the repository (and other git option-injection effects). git_log is even annotated readOnlyHint: true but writes a file. Confirmed on 2.15.1: git_log with branch="--output=/tmp/...txt" wrote a file outside the repository.

Details

The ref schema is z.string().min(1) with no validation, src/mcp-server/tools/schemas/common.ts (around line 69). The values are pushed raw into the git argv before the "--" separator:

  • git_log: branch pushed at src/services/git/providers/cli/operations/commits/log.ts (around line 85), before the "--" at line 90.
  • git_diff: source/target -> commit1/commit2 at .../commits/diff.ts (around lines 48, 52).
  • git_show: object at .../commits/show.ts (around line 113).
    The argument validator does not stop this: validateGitArgs allows any --flag=value and its unknown-flag throw is commented out, src/services/git/providers/cli/utils/command-builder.ts (around lines 223-233). In stdio mode the scope/auth check defaults to allowed (transports/auth/lib/authUtils.ts around lines 30-36). So a "-"-prefixed ref reaches git as an option.

PoC

validated on @cyanheads/git-mcp-server 2.15.1 over MCP stdio against a local repo:

git_log present: true
git_log result: { "success": true, "commits": [], ... }
OOB file written outside repo: true | ARG-INJECTION: CONFIRMED

The tool call git_log({ path: "", branch: "--output=/tmp/GITMCP_LOG_OOB.txt" }) caused git to write its log output to /tmp/GITMCP_LOG_OOB.txt, outside the repository. git_diff({ source: "--output=/tmp/...", target: "HEAD" }) and git_show with a "--output=" object behave the same. The clone tool's url field also has no protocol validation (ext::sh ... is forwarded to git), so on git builds where protocol.ext.allow is permissive, clone becomes command execution; on default git it is blocked, but the --output file-write injection above is not config-dependent.

Impact

An attacker who can influence the ref/object arguments (LLM-produced, prompt-injection steerable) can write git output to an arbitrary file the process can write, overwriting files outside the repository (configuration, data, or, combined with the writable content, more serious targets). git's many options (for example --output, and via other tools -O/--upload-pack/-c) provide additional primitives. The readOnlyHint on git_log is misleading.

Remediation

Validate ref/object/branch/remote parameters: reject values beginning with "-" (or pass them only after a "--" separator and never as options), and apply an allowlist/format check (a valid ref/commit/branch pattern). Re-enable the unknown-flag rejection in validateGitArgs. Treat all string parameters that reach the git command line as potentially option-injecting.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions