fix: allow @ in branch names (valid per git-check-ref-format) (#1411)

`validateBranchName` rejects branch names containing `@`, even though
`git check-ref-format` permits `@` and GitHub itself accepts such
branches. PRs whose head or base branch contains an `@` fail validation
in-process before any git operation, so the action errors out
immediately.

Branch names with `@` show up in real workflows: ticket conventions
like "TICKET-123@add-feature" (#998), leading-prefix conventions like
"@hotfix/...", and agent tooling that appends "@<sessionid>" (#1305).
There is no workaround other than renaming the branch, which is often
not under the user's control.

Branch names are never passed through a shell (git calls use
execFileSync argv arrays), so `@` carries no injection risk. This is
the same reasoning used to add `#` in #1167, `+` in #1248, and `,` in
#1310. The bare name "@" (HEAD shorthand in git revision syntax) and
the "@{" reflog sequence are still rejected.

- Add `@` to the validateBranchName whitelist regex, including the
  leading position (the leading-character rule blocks option injection
  via `-`, which `@` cannot cause)
- Reject the bare name "@" with a dedicated check
- Update the surrounding comment, JSDoc, and error message to match
- Add test cases for @-containing names and bare "@"

Fixes #998

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Bellal Mohamed
2026-06-22 14:42:36 -07:00
committed by GitHub
co-authored by Claude Fable 5
parent e452eb9dce
commit 360be9c8fc
2 changed files with 34 additions and 5 deletions
+16
View File
@@ -64,6 +64,16 @@ describe("validateBranchName", () => {
expect(() => validateBranchName("feature/paris,france")).not.toThrow();
expect(() => validateBranchName("fix/issue-1,2,3")).not.toThrow();
});
it("should accept branch names containing @ (git-valid, used in team and tooling conventions)", () => {
// Reported in #998: branches like "TICKET-123@add-feature" were rejected, even
// though git check-ref-format and GitHub both accept @ anywhere in a ref name.
// Also common as a leading prefix (e.g. "@hotfix/...") and in agent-generated
// names ("task@sessionid"). Bare "@" and "@{" are still rejected.
expect(() => validateBranchName("TICKET-123@add-feature")).not.toThrow();
expect(() => validateBranchName("@hotfix/login-timeout")).not.toThrow();
expect(() => validateBranchName("agent/task@abc123")).not.toThrow();
});
});
describe("command injection attempts", () => {
@@ -137,6 +147,12 @@ describe("validateBranchName", () => {
expect(() => validateBranchName("HEAD@{yesterday}")).toThrow(/@{/);
});
it("should reject the single character @", () => {
// Per git-check-ref-format, a refname cannot be the single character "@";
// "@" also resolves to HEAD in git revision syntax.
expect(() => validateBranchName("@")).toThrow(/single character '@'/);
});
it("should reject .lock suffix", () => {
expect(() => validateBranchName("branch.lock")).toThrow(/\.lock/);
expect(() => validateBranchName("feature.lock")).toThrow(/\.lock/);