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
9 changes: 6 additions & 3 deletions src/github/operations/branch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 @.`,
);
}

Expand Down
11 changes: 11 additions & 0 deletions test/validate-branch-name.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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", () => {
Expand Down