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: 5 additions & 5 deletions src/github/operations/branch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,14 +28,14 @@ function extractFirstLabel(githubData: FetchDataResult): string | undefined {
*
* Valid branch names:
* - Start with alphanumeric character (not dash, to prevent option injection)
* - Contain only alphanumeric, forward slash, hyphen, underscore, or period
* - Contain only alphanumeric, forward slash, hyphen, underscore, hash, or period
* - Do not start or end with a period
* - Do not end with a slash
* - Do not contain '..' (path traversal)
* - Do not contain '//' (consecutive slashes)
* - Do not end with '.lock'
* - Do not contain '@{'
* - Do not contain control characters or special git characters (~^:?*[\])
* - Do not contain control characters or special git characters (~^:?*[\\])
*/
export function validateBranchName(branchName: string): void {
// Check for empty or whitespace-only names
Expand All @@ -58,12 +58,12 @@ 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 start, then alphanumeric/slash/hyphen/underscore/hash/period
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, or periods.`,
`Invalid branch name: "${branchName}". Branch names must start with an alphanumeric character and contain only alphanumeric characters, forward slashes, hyphens, underscores, hashes, or periods.`,
);
}

Expand Down
8 changes: 8 additions & 0 deletions test/validate-branch-name.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,14 @@ describe("validateBranchName", () => {
expect(() => validateBranchName("release.1.2.3")).not.toThrow();
});

it("should accept names with hash characters (Azure DevOps work items)", () => {
expect(() =>
validateBranchName("feature/AB#1992-sentry-enhancements"),
).not.toThrow();
expect(() => validateBranchName("fix/PROJ#123-bug")).not.toThrow();
expect(() => validateBranchName("user/task#42")).not.toThrow();
});

it("should accept typical branch name formats", () => {
expect(() =>
validateBranchName("claude/issue-123-20250101-1234"),
Expand Down