From b15d4751a6478e3d2b6565953cf654a3e7320eeb Mon Sep 17 00:00:00 2001 From: Max Flanagan Date: Sat, 4 Apr 2026 23:17:46 -0400 Subject: [PATCH] fix: allow # in branch names for PR checkout and base restore (#1167) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `validateBranchName` used a strict whitelist that excluded `#`, causing the action to fail on PRs from branches like `put-back-arm64-#2` with "Invalid branch name" — even though the branch already exists in git and `#` is permitted by git-check-ref-format. The validation was designed to prevent command injection. However, every git call in the action uses `execFileSync`, which bypasses the shell entirely and passes arguments directly to the kernel's execve. There is no shell to interpret `#` as a metacharacter, so the strict whitelist was over-blocking valid names with no security benefit. Add `#` to the whitelist pattern, and update the JSDoc and error message to reflect the allowed character set. Fixes #1137. Co-authored-by: Claude Sonnet 4.6 --- src/github/operations/branch.ts | 10 ++++++---- test/validate-branch-name.test.ts | 7 +++++++ 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/src/github/operations/branch.ts b/src/github/operations/branch.ts index bf57f09..b2c145b 100644 --- a/src/github/operations/branch.ts +++ b/src/github/operations/branch.ts @@ -28,7 +28,7 @@ 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, period, or hash (#) * - Do not start or end with a period * - Do not end with a slash * - Do not contain '..' (path traversal) @@ -58,12 +58,14 @@ 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/period/hash. + // # is valid per git-check-ref-format and commonly used in branch names like "fix/#123-description". + // All git calls use execFileSync (not shell interpolation), so # carries no 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, or periods.`, + `Invalid branch name: "${branchName}". Branch names must start with an alphanumeric character and contain only alphanumeric characters, forward slashes, hyphens, underscores, periods, or hashes (#).`, ); } diff --git a/test/validate-branch-name.test.ts b/test/validate-branch-name.test.ts index 539932d..a5ba6a1 100644 --- a/test/validate-branch-name.test.ts +++ b/test/validate-branch-name.test.ts @@ -36,6 +36,13 @@ describe("validateBranchName", () => { expect(() => validateBranchName("refs/heads/main")).not.toThrow(); expect(() => validateBranchName("bugfix/JIRA-1234")).not.toThrow(); }); + + it("should accept branch names containing # (git-valid, common in issue-linked branches)", () => { + // Reported in #1137: branches like "put-back-arm64-#2" were rejected + expect(() => validateBranchName("put-back-arm64-#2")).not.toThrow(); + expect(() => validateBranchName("feature/#123-description")).not.toThrow(); + expect(() => validateBranchName("fix/issue-#42")).not.toThrow(); + }); }); describe("command injection attempts", () => {