fix: allow parentheses in valid branch names (#1710)

Accept parentheses while preserving existing branch-name security checks.

Add regression coverage for scoped branch names.

Refs anthropics/claude-code-action#1709
This commit is contained in:
Yauheni Papovich
2026-08-25 08:45:50 -07:00
committed by GitHub
parent 2d7a787fbd
commit 791545dab1
2 changed files with 15 additions and 4 deletions
+6 -4
View File
@@ -29,7 +29,7 @@ function extractFirstLabel(githubData: FetchDataResult): string | undefined {
*
* Valid branch names:
* - Start with alphanumeric character, underscore, or @ (not dash, to prevent option injection)
* - Contain only alphanumeric, forward slash, hyphen, underscore, period, hash (#), plus (+), comma (,), or at sign (@)
* - Contain only alphanumeric, forward slash, hyphen, underscore, period, hash (#), plus (+), comma (,), at sign (@), or parentheses
* - Do not start or end with a period
* - Do not end with a slash
* - Do not contain '..' (path traversal)
@@ -60,7 +60,7 @@ export function validateBranchName(branchName: string): void {
);
}
// Strict whitelist pattern: alphanumeric or @ start, then alphanumeric/slash/hyphen/underscore/period/hash/plus/comma/at-sign.
// Strict whitelist pattern: alphanumeric or @ start, then alphanumeric/slash/hyphen/underscore/period/hash/plus/comma/at-sign/parentheses.
// # 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").
@@ -72,12 +72,14 @@ export function validateBranchName(branchName: string): void {
// _ is valid per git-check-ref-format anywhere in a ref name, including the first character;
// leading underscores are a common convention for release/internal branches (e.g.
// "_release/v1.2.3"), which previously failed validation as a PR's base branch.
// Parentheses are valid per git-check-ref-format and commonly appear in branch names that
// use Conventional Commit-style scopes (e.g. "feat(parser)-handle-empty-input").
// 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/_.#+,@-]*$/;
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, underscore, or '@' and contain only alphanumeric characters, forward slashes, hyphens, underscores, periods, hashes (#), plus signs (+), commas (,), or at signs (@).`,
`Invalid branch name: "${branchName}". Branch names must start with an alphanumeric character, underscore, or '@' and contain only alphanumeric characters, forward slashes, hyphens, underscores, periods, hashes (#), plus signs (+), commas (,), at signs (@), or parentheses.`,
);
}
+9
View File
@@ -29,6 +29,15 @@ describe("validateBranchName", () => {
expect(() => validateBranchName("release.1.2.3")).not.toThrow();
});
it("should accept branch names containing parentheses", () => {
expect(() =>
validateBranchName("feat(example)-valid-branch"),
).not.toThrow();
expect(() =>
validateBranchName("fix(parser)-handle-empty-input"),
).not.toThrow();
});
it("should accept typical branch name formats", () => {
expect(() =>
validateBranchName("claude/issue-123-20250101-1234"),