From 0d1fc0c6f19b68ba43ceefc8dacd0185b9c007a9 Mon Sep 17 00:00:00 2001 From: Harlan GitHub Agent Date: Sat, 17 Oct 2026 07:55:28 +1100 Subject: [PATCH] fix(analytics): parse signup links without URL.canParse --- shared/signup-analytics.ts | 9 +++++++-- test/unit/signup-analytics.test.ts | 17 +++++++++++++++++ 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/shared/signup-analytics.ts b/shared/signup-analytics.ts index 19dd747c..21e0be26 100644 --- a/shared/signup-analytics.ts +++ b/shared/signup-analytics.ts @@ -27,9 +27,14 @@ export function signupEntry(action: string, returnTo: string): SignupEntry { /** Classify a clicked sign-in link without recording its path or parameters. */ export function signupClickEvent(href: string, origin: string): Extract | null { - if (!URL.canParse(href, origin)) + let url: URL + try { + url = new URL(href, origin) + } + catch { + // Invalid links are outside the sign-in action. return null - const url = new URL(href, origin) + } if (url.origin !== origin || url.pathname !== '/auth/github') return null return { diff --git a/test/unit/signup-analytics.test.ts b/test/unit/signup-analytics.test.ts index 12df1f59..c4ef2543 100644 --- a/test/unit/signup-analytics.test.ts +++ b/test/unit/signup-analytics.test.ts @@ -19,6 +19,23 @@ it.each(['/login', 'https://other.example/auth/github', 'javascript:alert(1)', ' expect(signupClickEvent(href, 'https://skilld.dev')).toBeNull() }) +it('classifies sign-in links when URL.canParse is unavailable', () => { + const descriptor = Object.getOwnPropertyDescriptor(URL, 'canParse') + Reflect.deleteProperty(URL, 'canParse') + try { + expect(signupClickEvent('/auth/github', 'https://skilld.dev')).toEqual({ + stage: 'oauth', + outcome: 'clicked', + entry: 'direct', + }) + expect(signupClickEvent('http://[', 'https://skilld.dev')).toBeNull() + } + finally { + if (descriptor) + Object.defineProperty(URL, 'canParse', descriptor) + } +}) + it('classifies CLI sign-in without storing its authorization parameters', () => { expect(signupEntry('', '/cli/authorize?user_code=SECRET')).toBe('cli') expect(signupEntry('like-skill', '/gh/owner/repo/skill')).toBe('like-skill')