{"record":{"id":"afc0f8ccbd1afc91","repo":"modelcontextprotocol/servers","slug":"invalid-contains-value-contains-cannot-star","errorCode":null,"errorMessage":"Invalid contains value: '{contains}' - cannot start with '-'","messagePattern":"Invalid contains value: '(.+?)' - cannot start with '-'","errorType":"validation","errorClass":"BadName","httpStatus":null,"severity":"error","filePath":"src/git/src/mcp_server_git/server.py","lineNumber":276,"sourceCode":"    try:\n        resolved_repo = repo_path.resolve()\n        resolved_allowed = allowed_repository.resolve()\n    except (OSError, RuntimeError):\n        raise ValueError(f\"Invalid path: {repo_path}\")\n\n    # Check if repo_path is the same as or a subdirectory of allowed_repository\n    try:\n        resolved_repo.relative_to(resolved_allowed)\n    except ValueError:\n        raise ValueError(\n            f\"Repository path '{repo_path}' is outside the allowed repository '{allowed_repository}'\"\n        )\n\n\ndef git_branch(repo: git.Repo, branch_type: str, contains: str | None = None, not_contains: str | None = None) -> str:\n    # Defense in depth: reject values starting with '-' to prevent flag injection\n    if contains and contains.startswith(\"-\"):\n        raise BadName(f\"Invalid contains value: '{contains}' - cannot start with '-'\")\n    if not_contains and not_contains.startswith(\"-\"):\n        raise BadName(f\"Invalid not_contains value: '{not_contains}' - cannot start with '-'\")\n\n    match contains:\n        case None:\n            contains_sha = (None,)\n        case _:\n            contains_sha = (\"--contains\", contains)\n\n    match not_contains:\n        case None:\n            not_contains_sha = (None,)\n        case _:\n            not_contains_sha = (\"--no-contains\", not_contains)\n\n    match branch_type:\n        case 'local':\n            b_type = None","sourceCodeStart":258,"sourceCodeEnd":294,"githubUrl":"https://github.com/modelcontextprotocol/servers/blob/76d64c822f5125032f89eb71dbdb94e42b434821/src/git/src/mcp_server_git/server.py#L258-L294","documentation":"git_branch (server.py:273-305) raises git.exc.BadName when the 'contains' argument starts with '-'. This is defense-in-depth against flag injection: the value is later passed verbatim to repo.git.branch('--contains', contains), and a leading dash would make GitPython parse it as a git flag.","triggerScenarios":"Calling the branch tool with a 'contains' value like '--all', '-v', '--contains=main', or any string beginning with '-'. Most often an LLM or user pasting the literal '--contains X' from a shell command instead of just the ref.","commonSituations":"Copy-pasting 'git branch --contains main' and passing '--contains main' as the value. Hallucinated flag prefixes from a model. Cross-contamination from the not_contains/--no-contains sibling argument.","solutions":["Pass the bare ref or commit (e.g. 'main', 'abc123') as 'contains', never the '--contains' prefix.","Strip a leading '--contains=' from user input before forwarding it.","Validate client-side that the value does not start with '-' and looks like a ref.","Reuse the same ref-sanitization helper you use for branch_name/revision/not_contains."],"exampleFix":"// before\n//   arguments.get(\"contains\") -> \"--contains main\"   -> BadName\n// after\n//   arguments.get(\"contains\") -> \"main\"","handlingStrategy":"validation","validationCode":"def clean_ref_arg(v: str | None) -> str | None:\n    if v is None:\n        return None\n    v = v.strip()\n    if v.startswith(\"--contains=\"):\n        v = v[len(\"--contains=\"):]\n    if v.startswith(\"-\"):\n        raise ValueError(f\"ref argument must not start with '-': {v!r}\")\n    if not v:\n        return None\n    return v\n\ncontains = clean_ref_arg(arguments.get(\"contains\"))","typeGuard":"def is_safe_ref_arg(v: object) -> bool:\n    return v is None or (isinstance(v, str) and not v.startswith(\"-\") and v.strip() != \"\")","tryCatchPattern":"from git.exc import BadName\n\ntry:\n    result = git_branch(repo, branch_type, contains, not_contains)\nexcept BadName as e:\n    # reject the offending contains/not_contains value back to the user\n    log.warning(\"rejected ref arg: %s\", e)","preventionTips":["Never prefix a ref argument with '--'; pass the bare ref/commit only.","Strip a leading '--contains=' if you accept pasted shell input.","Reuse one ref-sanitizer across contains, not_contains, branch_name, revision, target."],"tags":["git","flag-injection","security","python","validation"],"backgroundTag":null,"analyzedSha":"76d64c822f5125032f89eb71dbdb94e42b434821","analyzedAt":"2026-08-12T10:02:41.718Z","schemaVersion":2},"datasetVersion":"2026-08-12T13:17:24.610Z"}