{"record":{"id":"5f60fc1a1e3890eb","repo":"modelcontextprotocol/servers","slug":"invalid-target-target-cannot-start-with","errorCode":null,"errorMessage":"Invalid target: '{target}' - cannot start with '-'","messagePattern":"Invalid target: '(.+?)' - cannot start with '-'","errorType":"exception","errorClass":"BadName","httpStatus":null,"severity":"error","filePath":"src/git/src/mcp_server_git/server.py","lineNumber":124,"sourceCode":"    CHECKOUT = \"git_checkout\"\n    SHOW = \"git_show\"\n\n    BRANCH = \"git_branch\"\n\ndef git_status(repo: git.Repo) -> str:\n    return repo.git.status()\n\ndef git_diff_unstaged(repo: git.Repo, context_lines: int = DEFAULT_CONTEXT_LINES) -> str:\n    return repo.git.diff(f\"--unified={context_lines}\")\n\ndef git_diff_staged(repo: git.Repo, context_lines: int = DEFAULT_CONTEXT_LINES) -> str:\n    return repo.git.diff(f\"--unified={context_lines}\", \"--cached\")\n\ndef git_diff(repo: git.Repo, target: str, context_lines: int = DEFAULT_CONTEXT_LINES) -> str:\n    # Defense in depth: reject targets starting with '-' to prevent flag injection,\n    # even if a malicious ref with that name exists (e.g. via filesystem manipulation)\n    if target.startswith(\"-\"):\n        raise BadName(f\"Invalid target: '{target}' - cannot start with '-'\")\n    repo.rev_parse(target)  # Validates target is a real git ref, throws BadName if not\n    return repo.git.diff(f\"--unified={context_lines}\", target)\n\ndef git_commit(repo: git.Repo, message: str) -> str:\n    commit = repo.index.commit(message)\n    return f\"Changes committed successfully with hash {commit.hexsha}\"\n\ndef git_add(repo: git.Repo, files: list[str]) -> str:\n    if files == [\".\"]:\n        repo.git.add(\".\")\n    else:\n        # Defense in depth: validate each path resolves within the repository\n        # working tree to prevent path traversal (e.g. '../../etc/passwd' or an\n        # absolute path) from staging files outside repository boundaries.\n        repo_root = Path(repo.working_dir).resolve()\n        for f in files:\n            try:\n                resolved = (repo_root / f).resolve()","sourceCodeStart":106,"sourceCodeEnd":142,"githubUrl":"https://github.com/modelcontextprotocol/servers/blob/76d64c822f5125032f89eb71dbdb94e42b434821/src/git/src/mcp_server_git/server.py#L106-L142","documentation":"git_diff() rejects a target that starts with '-' as defense-in-depth against flag injection into `git diff` (e.g. a target like --upload-pack=...). The check runs before repo.rev_parse(target), so it blocks the value regardless of whether such a ref exists. Raises gitdb BadName, which propagates raw to the client because the git server runs with raise_exceptions=True and call_tool does not wrap it.","triggerScenarios":"Calling git_diff with a target string beginning with '-'; malicious or malformed ref input.","commonSituations":"Adversarial tool input; copy-paste artifacts; a UI that lets users pass arbitrary ref text.","solutions":["Reject or strip a leading '-' from target before calling.","Use a valid ref (branch, tag, or commit SHA) that does not start with '-'.","If the value legitimately could be an option, pass it through the proper option parameter instead of target."],"exampleFix":"# before\ngit_diff(repo, target='-eMalicious')  # -> BadName\n\n# after\ndef safe_target(t: str) -> str:\n    if t.startswith('-'):\n        raise ValueError('target must not start with -')\n    return t\ngit_diff(repo, safe_target(target))","handlingStrategy":"validation","validationCode":"def safe_ref(target: str) -> str:\n    if not target or target.startswith('-'):\n        raise ValueError('target must be a non-empty ref not starting with -')\n    return target","typeGuard":null,"tryCatchPattern":"from gitdb.exc import BadName\ntry:\n    git_diff(repo, target)\nexcept BadName as e:\n    if 'cannot start with' in str(e):\n        # reject/normalize input\n    raise","preventionTips":["Reject any ref/option value beginning with '-' before passing to git helpers.","Prefer SHAs/tags over free-form strings from untrusted callers.","Keep the call_tool layer sanitizing user-supplied refs."],"tags":["git","python","security","flag-injection","validation"],"backgroundTag":null,"analyzedSha":"76d64c822f5125032f89eb71dbdb94e42b434821","analyzedAt":"2026-08-12T10:02:41.718Z","schemaVersion":2},"datasetVersion":"2026-08-12T13:17:24.610Z"}