{"record":{"id":"3344b5971c07e366","repo":"modelcontextprotocol/servers","slug":"invalid-not-contains-value-not-contains-can","errorCode":null,"errorMessage":"Invalid not_contains value: '{not_contains}' - cannot start with '-'","messagePattern":"Invalid not_contains value: '(.+?)' - cannot start with '-'","errorType":"validation","errorClass":"BadName","httpStatus":null,"severity":"error","filePath":"src/git/src/mcp_server_git/server.py","lineNumber":278,"sourceCode":"        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\n        case 'remote':\n            b_type = \"-r\"","sourceCodeStart":260,"sourceCodeEnd":296,"githubUrl":"https://github.com/modelcontextprotocol/servers/blob/76d64c822f5125032f89eb71dbdb94e42b434821/src/git/src/mcp_server_git/server.py#L260-L296","documentation":"Same defense-in-depth guard in git_branch (server.py:277-278) for the 'not_contains' argument, which would otherwise be forwarded to repo.git.branch('--no-contains', not_contains). A leading '-' is rejected with git.exc.BadName to prevent flag injection.","triggerScenarios":"Calling the branch tool with a 'not_contains' value beginning with '-', e.g. '-r', '--merged', '--no-contains=main'. Typically a user/LLM copying the '--no-contains X' literal into the value slot.","commonSituations":"Translating 'git branch --no-contains main' into tool args naively. Reusing a flag string across contains/not_contains. Hallucinated prefixes.","solutions":["Pass the bare ref or commit as 'not_contains' (e.g. 'main'), not '--no-contains main'.","Strip a leading '--no-contains=' prefix from user input if present.","Reject any value starting with '-' before sending the call.","Share one ref-sanitization helper across contains and not_contains."],"exampleFix":"// before\n//   not_contains: \"--no-contains main\"   -> BadName\n// after\n//   not_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(\"--no-contains=\"):\n        v = v[len(\"--no-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\nnot_contains = clean_ref_arg(arguments.get(\"not_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    log.warning(\"rejected ref arg: %s\", e)","preventionTips":["Pass the bare ref as not_contains, never '--no-contains main'.","Strip '--no-contains=' from pasted input.","Share the same sanitizer used for contains and other ref arguments."],"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"}