From dee13b160f523d79a48281897ced3063fcc70440 Mon Sep 17 00:00:00 2001 From: Colin McDonnell Date: Mon, 11 May 2026 18:45:20 +0000 Subject: [PATCH] console: case-insensitive owner/repo slug resolution (#649) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * console: case-insensitive owner/repo slug resolution URL slugs may be any case but GitHub treats logins and repo names as case-insensitive (and 301-redirects to canonical case). Internal find/filter sites compared with `===`, so mixed-case slugs (e.g. `/console/Pullfrog`) hard-403'd in resolveOwnerAccess and silently redirected from the per-repo console when currentRepo lookup missed. Lowercase both sides at every slug comparison: resolveOwnerAccess installation lookup, currentRepo lookup in repo + history pages, ConsoleHeader installation/repo lookups, getInstallations personal split, getOrgMembership user/org checks, getInstallationRepos node filter, getUserRole owner-as-collaborator check, and the action runtime's installation-repo access check. Caches keyed by raw input remain case-split across casings; that's fine since both entries resolve to the same canonical GitHub data and TTLs are short. * api: resolve targetAccountId by gh node id getAuthenticatedAccountContext was looking up Account by `name` using the raw URL slug, but `Account.name` is plain String populated from canonical GitHub login. Mixed-case URLs would render the page (since resolveOwnerAccess is now case-insensitive) but every billing/secrets API call would 403 on the find-by-name miss. Resolve by gh_${access.installation.account.node_id} instead — invariant to case-folding and login renames. Same pattern as the sibling owner page route already uses. --- utils/github.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/utils/github.ts b/utils/github.ts index 7f2d5b4..81a0b6b 100644 --- a/utils/github.ts +++ b/utils/github.ts @@ -221,8 +221,11 @@ const checkRepositoryAccess = async ( headers: { Authorization: `token ${token}` }, }); + const ownerLower = repoOwner.toLowerCase(); + const nameLower = repoName.toLowerCase(); return response.repositories.some( - (repo) => repo.owner.login === repoOwner && repo.name === repoName + (repo) => + repo.owner.login.toLowerCase() === ownerLower && repo.name.toLowerCase() === nameLower ); } catch { return false;