From e196266a25890ca22c9fba8f52b44f553e66c0b3 Mon Sep 17 00:00:00 2001 From: David Nguyen Date: Tue, 10 Nov 2026 12:09:13 +0700 Subject: [PATCH 1/2] fix(library): reject encoded control characters in station routes --- utils/listening-library.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/utils/listening-library.mjs b/utils/listening-library.mjs index a0cc0ca9..ef4a76bd 100644 --- a/utils/listening-library.mjs +++ b/utils/listening-library.mjs @@ -8,7 +8,7 @@ export function stationIdentity(value) { try { if (value.url.split('/').some(part => { const decoded = decodeURIComponent(part); - return decoded === '.' || decoded === '..' || /[\\/?#]/.test(decoded); + return decoded === '.' || decoded === '..' || /[\\/?#\u0000-\u001f\u007f]/.test(decoded); })) return null; } catch { return null; } if (typeof value.title !== 'string' || !value.title.trim()) return null; From 9758fa8c951812ceba2fc924e253cd4b339fd867 Mon Sep 17 00:00:00 2001 From: David Nguyen Date: Tue, 10 Nov 2026 12:09:28 +0700 Subject: [PATCH 2/2] test(library): cover encoded control characters and valid station slug --- tests/listening-library.test.mjs | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/tests/listening-library.test.mjs b/tests/listening-library.test.mjs index b802a31a..4863edc0 100644 --- a/tests/listening-library.test.mjs +++ b/tests/listening-library.test.mjs @@ -68,3 +68,16 @@ test('encoded path validation preserves valid percent-encoded Unicode station sl const valid = station('%E1%BB%95n-%C4%91%E1%BB%8Bnh'); assert.equal(rememberStation(emptyListeningLibrary(), valid, 'pause').lastStation?.url, valid.url); }); + + +test('rejects percent-encoded control characters in station slugs', () => { + for (const url of [ + '/asia/viet-nam/radio%00test', + '/asia/viet-nam/radio%0Atest', + '/asia/viet-nam/radio%09test', + '/asia/viet-nam/radio%7Ftest', + ]) { + assert.equal(rememberStation(emptyListeningLibrary(), { ...station('test'), url }, 'play').lastStation, null, url); + } + assert.equal(rememberStation(emptyListeningLibrary(), station('radio-1'), 'pause').lastStation?.url, '/asia/viet-nam/radio-1'); +});