fix: do not treat pure-numeric property values as bare-domain URLs

Fixes #710

A number property like "Year: 2026" rendered as "0.0.7.234" in the
note list column because the WHATWG URL parser canonicalizes integer-only
hosts to IPv4 form: new URL('https://2026').hostname === '0.0.7.234'.
formatChipLabel called isUrlValue('2026'), which returned true via the
bare-domain branch of normalizeExternalUrl, and then asked the URL parser
for the hostname.

Add a single guard in normalizeExternalUrl: when the input contains no '.',
reject it before the bare-domain candidate is constructed. The
parseHttpUrl(trimmed) branch above already handles inputs with an explicit
scheme (https://...), so the guard only narrows the bare-domain path -
the path that produced the bug. Existing cases ('example.com', 'localhost')
remain unaffected.

Two new vitest cases cover the regression ('2026' and '0') plus a parity
case for https://example.com to make sure the scheme branch is untouched.
This commit is contained in:
Matt Van Horn
2026-05-19 23:30:59 -07:00
parent 6cf77f9b94
commit 57c6a4a2a4
2 changed files with 9 additions and 0 deletions

View File

@@ -3,6 +3,7 @@ import { invoke } from '@tauri-apps/api/core'
import { revealItemInDir } from '@tauri-apps/plugin-opener'
import {
copyLocalPath,
isUrlValue,
normalizeExternalUrl,
openExternalUrl,
openLocalFile,
@@ -24,10 +25,17 @@ function setClipboard(writeText: (value: string) => Promise<void>) {
describe('normalizeExternalUrl', () => {
it('keeps valid http URLs and normalizes bare domains', () => {
expect(normalizeExternalUrl('https://example.com')).toBe('https://example.com')
expect(normalizeExternalUrl('https://example.com/docs')).toBe('https://example.com/docs')
expect(normalizeExternalUrl('example.com/docs')).toBe('https://example.com/docs')
})
it('rejects pure numeric values instead of treating them as bare domains', () => {
expect(normalizeExternalUrl('2026')).toBeNull()
expect(normalizeExternalUrl('0')).toBeNull()
expect(isUrlValue('2026')).toBe(false)
})
it('rejects malformed or unsupported URLs', () => {
expect(normalizeExternalUrl('https://exa mple.com')).toBeNull()
expect(normalizeExternalUrl('javascript:alert(1)')).toBeNull()

View File

@@ -28,6 +28,7 @@ export function normalizeExternalUrl(value: string): string | null {
}
if (parseHttpUrl(trimmed)) return trimmed
if (!trimmed.includes('.')) return null
const bareDomainCandidate = `https://${trimmed}`
const parsedBareDomain = parseHttpUrl(bareDomainCandidate)