fix(ui): use div for fragment, check lastfm url for artist page (#4980)

* fix(ui): use div for fragment, check lastfm url for artist page

* use span instead of div for better compat

* fix: implement isLastFmURL utility and add tests for URL validation

---------

Co-authored-by: Deluan <deluan@navidrome.org>
This commit is contained in:
Kendall Garner
2026-02-04 22:34:26 +00:00
committed by GitHub
parent 4f3845bbe3
commit 2731e25fd2
4 changed files with 43 additions and 5 deletions
+13
View File
@@ -44,3 +44,16 @@ export const shareCoverUrl = (id, square) => {
}
export const docsUrl = (path) => `https://www.navidrome.org${path}`
export const isLastFmURL = (url) => {
try {
const parsed = new URL(url)
return (
(parsed.protocol === 'http:' || parsed.protocol === 'https:') &&
(parsed.hostname === 'last.fm' || parsed.hostname.endsWith('.last.fm')) &&
parsed.pathname.startsWith('/music/')
)
} catch (e) {
return false
}
}
+25
View File
@@ -0,0 +1,25 @@
import { isLastFmURL } from './urls'
describe('isLastFmURL', () => {
it('returns true for valid Last.fm music URLs', () => {
expect(isLastFmURL('https://last.fm/music/The+Beatles')).toBe(true)
expect(isLastFmURL('http://last.fm/music/Radiohead')).toBe(true)
expect(isLastFmURL('https://www.last.fm/music/Daft+Punk')).toBe(true)
})
it('returns false for non-http(s) protocols (XSS prevention)', () => {
expect(isLastFmURL('javascript:alert(1)//last.fm/music/')).toBe(false)
expect(isLastFmURL('data:text/html,<script>//last.fm/music/')).toBe(false)
})
it('returns false for non-last.fm domains', () => {
expect(isLastFmURL('https://example.com/?q=last.fm/music/')).toBe(false)
expect(isLastFmURL('https://fake-last.fm/music/Artist')).toBe(false)
})
it('returns false for invalid paths or inputs', () => {
expect(isLastFmURL('https://last.fm/user/someone')).toBe(false)
expect(isLastFmURL(null)).toBe(false)
expect(isLastFmURL('not-a-url')).toBe(false)
})
})