From afe05454d9e8c4f03294f7e87e66d1cd1c0068ba Mon Sep 17 00:00:00 2001 From: Sameh Khamis Date: Wed, 21 Oct 2026 15:33:14 -0700 Subject: [PATCH] Require a directory prefix for camera URL pins The URL parser already turns backslashes into separators, so only encoded separators need refusing; a pin prefix without a trailing slash is treated as a directory. --- server/providers/cctv/normalize.js | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/server/providers/cctv/normalize.js b/server/providers/cctv/normalize.js index dd1a84516..74f693dd8 100644 --- a/server/providers/cctv/normalize.js +++ b/server/providers/cctv/normalize.js @@ -664,8 +664,8 @@ export function isLikelyTorontoCoordinate(lat, lon) { * A camera URL under a pinned directory, by parsed URL rather than string * prefix: the same scheme and exact host as `prefix`, no credentials, the * default port, and a normalized path inside the prefix's directory. Dot - * segments (including encoded ones) are resolved before the path is compared, - * and an encoded slash or backslash is refused. + * segments (including encoded ones) and backslashes are resolved before the + * path is compared, and an encoded slash or backslash is refused. * * @param {string} raw - Camera URL from a catalog or redirect. * @param {string} prefix - Pinned origin and directory, ending in `/`. @@ -686,8 +686,10 @@ export function pinnedCameraUrl(raw, prefix) { url.username || url.password || url.port || - !url.pathname.startsWith(pin.pathname) || - /%2f|%5c|\\/i.test(url.pathname) + !url.pathname.startsWith( + pin.pathname.endsWith('/') ? pin.pathname : `${pin.pathname}/`, + ) || + /%2f|%5c/i.test(url.pathname) ) return null; return url.href;