diff --git a/src/github/operations/branch.ts b/src/github/operations/branch.ts index 86197da96..7bcf9f212 100644 --- a/src/github/operations/branch.ts +++ b/src/github/operations/branch.ts @@ -58,12 +58,15 @@ export function validateBranchName(branchName: string): void { ); } - // Strict whitelist pattern: alphanumeric start, then alphanumeric/slash/hyphen/underscore/period - const validPattern = /^[a-zA-Z0-9][a-zA-Z0-9/_.-]*$/; + // Strict whitelist pattern: alphanumeric or Unicode letter start, then those plus + // slash/hyphen/underscore/period/@. The `@` is valid in git branch names; only `@{` + // is reserved (checked separately below). Unicode letters (\p{L}) support non-ASCII + // branch names (e.g. Japanese, Chinese characters). + const validPattern = /^[\p{L}\p{N}][\p{L}\p{N}/_.\-@]*$/u; 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, or periods.`, + `Invalid branch name: "${branchName}". Branch names must start with an alphanumeric or Unicode letter character and contain only alphanumeric characters, Unicode letters, forward slashes, hyphens, underscores, periods, or @.`, ); } diff --git a/test/validate-branch-name.test.ts b/test/validate-branch-name.test.ts index 539932dd0..9f26e1d02 100644 --- a/test/validate-branch-name.test.ts +++ b/test/validate-branch-name.test.ts @@ -36,6 +36,17 @@ describe("validateBranchName", () => { expect(() => validateBranchName("refs/heads/main")).not.toThrow(); expect(() => validateBranchName("bugfix/JIRA-1234")).not.toThrow(); }); + + it("should accept branch names with @ character", () => { + expect(() => validateBranchName("TICKET-123@add-feature")).not.toThrow(); + expect(() => validateBranchName("user@branch")).not.toThrow(); + }); + + it("should accept branch names with non-ASCII Unicode characters", () => { + expect(() => validateBranchName("feat/add-機能追加")).not.toThrow(); + expect(() => validateBranchName("修复/bug-123")).not.toThrow(); + expect(() => validateBranchName("фича/новая")).not.toThrow(); + }); }); describe("command injection attempts", () => {