{"record":{"id":"6d1140f80bca7296","repo":"cli/cli","slug":"invalid-branch-name-q","errorCode":null,"errorMessage":"invalid branch name: %q","messagePattern":"invalid branch name: %q","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/cmd/pr/checkout/checkout.go","lineNumber":155,"sourceCode":"\tremotes, err := opts.Remotes()\n\tif err != nil {\n\t\treturn err\n\t}\n\tbaseRemote, _ := remotes.FindByRepo(baseRepo.RepoOwner(), baseRepo.RepoName())\n\tbaseURLOrName := ghrepo.FormatRemoteURL(baseRepo, protocol)\n\tif baseRemote != nil {\n\t\tbaseURLOrName = baseRemote.Name\n\t}\n\n\theadRemote := baseRemote\n\tif pr.HeadRepository == nil {\n\t\theadRemote = nil\n\t} else if pr.IsCrossRepository {\n\t\theadRemote, _ = remotes.FindByRepo(pr.HeadRepositoryOwner.Login, pr.HeadRepository.Name)\n\t}\n\n\tif strings.HasPrefix(pr.HeadRefName, \"-\") {\n\t\treturn fmt.Errorf(\"invalid branch name: %q\", pr.HeadRefName)\n\t}\n\n\tvar cmdQueue [][]string\n\n\tif headRemote != nil {\n\t\tcmdQueue = append(cmdQueue, cmdsForExistingRemote(headRemote, pr, opts, reuseWorktree)...)\n\t} else {\n\t\thttpClient, err := opts.HttpClient()\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tapiClient := api.NewClientFromHTTP(httpClient)\n\n\t\tdefaultBranch, err := api.RepoDefaultBranch(apiClient, baseRepo)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tcmdQueue = append(cmdQueue, cmdsForMissingRemote(pr, baseURLOrName, baseRepo.RepoHost(), defaultBranch, protocol, opts, reuseWorktree)...)","sourceCodeStart":137,"sourceCodeEnd":173,"githubUrl":"https://github.com/cli/cli/blob/0eeec0b92edbe70199f9768522f831d3534f41ad/pkg/cmd/pr/checkout/checkout.go#L137-L173","documentation":"Guard in `gh pr checkout` that rejects a PR head ref name beginning with '-'. The head ref comes from the remote PR data and is passed to git commands (fetch/checkout/worktree add) as an argument; a leading dash would be parsed as an option, so the command refuses before shelling out. This is an option-injection defense, not a general validity check.","triggerScenarios":"Checking out a PR whose HeadRefName starts with '-' (e.g. a branch named '-rf' or '--force' pushed from another git client). GitHub itself rarely permits such branch names via the web UI, but some git servers/versions accept them, so gh guards locally.","commonSituations":"PRs opened from forks with adversarial or malformed branch names; ghsec-style hardening after option-injection findings; testing gh against permissive GHE instances.","solutions":["Do not check out that PR by branch name; fetch and check it out by ref instead: `git fetch origin pull/<N>/head && git checkout FETCH_HEAD`.","Ask the PR author to rename the branch to a name not starting with '-' and update the PR.","If you administer the source repo, reject branch names starting with '-' at the server/push level."],"exampleFix":"# before\ngh pr checkout 57  # head branch '-rf' -> invalid branch name\n\n# after\ngit fetch origin pull/57/head\ngit checkout FETCH_HEAD","handlingStrategy":"validation","validationCode":"# inspect the PR head branch before checkout\nhead=$(gh pr view \"$N\" --json headRefName --jq .headRefName)\ncase \"$head\" in -*) echo \"refusing dash-prefixed branch: $head\" >&2; exit 2;; esac\ngh pr checkout \"$N\"","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Fetch by PR ref instead of branch name for untrusted PRs: `git fetch origin pull/N/head && git checkout FETCH_HEAD`.","Reject branch names starting with '-' in server-side push rules/hooks.","Treat externally-supplied branch names as untrusted input when building git commands."],"tags":["git","security","option-injection","pr-checkout"],"backgroundTag":null,"analyzedSha":"0eeec0b92edbe70199f9768522f831d3534f41ad","analyzedAt":"2026-08-15T12:31:05.478Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}