Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 6 additions & 4 deletions src/github/operations/branch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,16 +58,18 @@ export function validateBranchName(branchName: string): void {
);
}

// Strict whitelist pattern: alphanumeric start, then alphanumeric/slash/hyphen/underscore/period/hash/plus.
// Strict whitelist pattern: alphanumeric start, then alphanumeric/slash/hyphen/underscore/period/hash/plus/comma.
// # is valid per git-check-ref-format and commonly used in branch names like "fix/#123-description".
// + is valid per git-check-ref-format and generated by Claude Code's EnterWorktree tool when
// converting worktree names containing "/" (e.g. "feat/foo" becomes "worktree-feat+foo").
// All git calls use execFileSync (not shell interpolation), so neither # nor + carries injection risk.
const validPattern = /^[a-zA-Z0-9][a-zA-Z0-9/_.#+-]*$/;
// , is valid per git-check-ref-format and commonly appears in branch names derived from titles
// or external identifiers (e.g. place names like "feature/paris,france").
// All git calls use execFileSync (not shell interpolation), so none of these characters carry injection risk.
const validPattern = /^[a-zA-Z0-9][a-zA-Z0-9/_.#+,-]*$/;

if (!validPattern.test(branchName)) {
throw new Error(
`Invalid branch name: "${branchName}". Branch names must start with an alphanumeric character and contain only alphanumeric characters, forward slashes, hyphens, underscores, periods, hashes (#), or plus signs (+).`,
`Invalid branch name: "${branchName}". Branch names must start with an alphanumeric character and contain only alphanumeric characters, forward slashes, hyphens, underscores, periods, hashes (#), plus signs (+), or commas (,).`,
);
}

Expand Down
9 changes: 9 additions & 0 deletions test/validate-branch-name.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,15 @@ describe("validateBranchName", () => {
expect(() => validateBranchName("fix+issue-123")).not.toThrow();
expect(() => validateBranchName("feature+new-thing")).not.toThrow();
});

it("should accept branch names containing , (git-valid, common in title-derived branches)", () => {
// Reported in #1300: branches like "feature/a,b" were rejected, even though
// git check-ref-format and GitHub both accept commas. Common when branch names
// are derived from titles, place names, or external identifiers.
expect(() => validateBranchName("feature/a,b")).not.toThrow();
expect(() => validateBranchName("feature/paris,france")).not.toThrow();
expect(() => validateBranchName("fix/issue-1,2,3")).not.toThrow();
});
});

describe("command injection attempts", () => {
Expand Down