From 32367b6d47b6cf0f0556da45bfd3e84acdeb525d Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Wed, 21 Oct 2026 18:16:55 +0000 Subject: [PATCH 1/2] fix(feeds): reject scheme-only http(s) story URLs that browsers resolve as app routes Co-Authored-By: Ishita Gupta --- react-app/src/features/feeds/externalHref.test.ts | 2 ++ react-app/src/features/feeds/externalHref.ts | 10 +++++++--- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/react-app/src/features/feeds/externalHref.test.ts b/react-app/src/features/feeds/externalHref.test.ts index 8d1a87bd9..baf1ffa0c 100644 --- a/react-app/src/features/feeds/externalHref.test.ts +++ b/react-app/src/features/feeds/externalHref.test.ts @@ -14,6 +14,8 @@ describe('externalHref', () => { ['data: URL', 'data:text/html,hi'], ['protocol-relative URL', '//evil.example'], ['"http" prefix that is not a URL', 'httpfoo'], + ['scheme-only https URL', 'https:example.com'], + ['scheme-only http URL with one slash', 'http:/example.com'], ['empty string', ''], ['undefined', undefined], ])('returns null for %s', (_, url) => { diff --git a/react-app/src/features/feeds/externalHref.ts b/react-app/src/features/feeds/externalHref.ts index 2c52ac1b5..2df77b355 100644 --- a/react-app/src/features/feeds/externalHref.ts +++ b/react-app/src/features/feeds/externalHref.ts @@ -1,11 +1,15 @@ import { isSafeHref } from '../../shared/utils'; /** - * Angular `hasUrl` (`url.indexOf('http') === 0`), tightened: only a parseable, safe http(s) URL renders as an - * external link. Everything else (HN-relative `item?id=N` for Ask/Show/text posts) links to `/item/:id`. + * Angular `hasUrl` (`url.indexOf('http') === 0`), tightened: only a parseable, safe, absolute `http(s)://` URL renders + * as an external link. Everything else (HN-relative `item?id=N` for Ask/Show/text posts) links to `/item/:id`. + * Scheme-only forms like `https:example.com` are rejected: `new URL` accepts them, but as an href the browser resolves + * them relative to the page. */ +const ABSOLUTE_HTTP = /^https?:\/\//; + export function externalHref(url: string | null | undefined): string | null { - if (typeof url !== 'string' || url.indexOf('http') !== 0 || !isSafeHref(url)) return null; + if (typeof url !== 'string' || !ABSOLUTE_HTTP.test(url) || !isSafeHref(url)) return null; try { const parsed = new URL(url); return parsed.protocol === 'http:' || parsed.protocol === 'https:' ? url : null; From 62fd6a6f54c3631a89acf2c26e508d2854016246 Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Wed, 21 Oct 2026 19:10:10 +0000 Subject: [PATCH 2/2] fix(externalHref): accept httpS:// like Angular's case-sensitive 'http' prefix check Co-Authored-By: Ishita Gupta --- react-app/src/shared/utils/externalHref.test.ts | 1 + react-app/src/shared/utils/externalHref.ts | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/react-app/src/shared/utils/externalHref.test.ts b/react-app/src/shared/utils/externalHref.test.ts index 52fcb3cc1..4ca666efa 100644 --- a/react-app/src/shared/utils/externalHref.test.ts +++ b/react-app/src/shared/utils/externalHref.test.ts @@ -6,6 +6,7 @@ describe('externalHref', () => { it('returns http(s) URLs unchanged (Angular binds item.url as-is)', () => { expect(externalHref('https://example.com/a?b=1')).toBe('https://example.com/a?b=1'); expect(externalHref('http://example.com')).toBe('http://example.com'); + expect(externalHref('httpS://example.com/a')).toBe('httpS://example.com/a'); }); it.each([ diff --git a/react-app/src/shared/utils/externalHref.ts b/react-app/src/shared/utils/externalHref.ts index 183688245..384865e9c 100644 --- a/react-app/src/shared/utils/externalHref.ts +++ b/react-app/src/shared/utils/externalHref.ts @@ -6,7 +6,7 @@ import { isSafeHref } from './sanitizeHtml'; * Scheme-only forms like `https:example.com` are rejected: `new URL` accepts them, but as an href the browser resolves * them relative to the page. */ -const ABSOLUTE_HTTP = /^https?:\/\//; +const ABSOLUTE_HTTP = /^http[sS]?:\/\//; export function externalHref(url: string | null | undefined): string | null { if (typeof url !== 'string' || !ABSOLUTE_HTTP.test(url) || !isSafeHref(url)) return null;