/** Normalize a git remote URL for allowlist comparison. */ export function normalizeGitRemoteUrl(raw: string | null | undefined): string | null { const trimmed = raw?.trim(); if (!trimmed) return null; try { if (/^git@/i.test(trimmed)) { const match = /^git@([^:]+):(.+?)(?:\.git)?$/i.exec(trimmed); if (!match) return null; return `https://${match[1]!.toLowerCase()}/${match[2]!.replace(/^\//, "")}`.replace( /\.git$/i, "", ); } const url = new URL(trimmed); if (url.protocol !== "http:" && url.protocol !== "https:") return null; return `${url.protocol}//${url.host.toLowerCase()}${url.pathname.replace(/\/$/, "").replace(/\.git$/i, "")}`; } catch { return null; } } export function gitRemoteMatchesAllowlist( remoteUrl: string | null | undefined, allowedRemoteUrl: string | null | undefined, ): boolean { const remote = normalizeGitRemoteUrl(remoteUrl); const allowed = normalizeGitRemoteUrl(allowedRemoteUrl); if (!remote || !allowed) return false; return remote === allowed; }