From 47e04b506fc087433eb0837275d72dbdfc0ba586 Mon Sep 17 00:00:00 2001 From: Maurice Date: Fri, 23 Oct 2026 18:26:18 +0200 Subject: [PATCH] fix(place-import): narrow the list id before building Google's list URL, and reach the data URL check through the service The strict ratchet refused the list id that may be null in the URL, and the places controller reached into place-import's provider directly. --- server/src/nest/place-import/place-import.service.ts | 1 + .../place-import/providers/google-list.provider.ts | 10 +++++++--- server/src/nest/places/places.controller.ts | 3 +-- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/server/src/nest/place-import/place-import.service.ts b/server/src/nest/place-import/place-import.service.ts index 01bd18afe..525928a2f 100644 --- a/server/src/nest/place-import/place-import.service.ts +++ b/server/src/nest/place-import/place-import.service.ts @@ -25,6 +25,7 @@ import type { CollectionGpxExport, CollectionGpxReadResult } from '@trek/shared' // shared route with, what the import routes dispatch on, and what the city // walk import reads a walking route and its travel mode with (CW-07). export { directionsTravelMode, isDirectionsUrl, parseDirectionsUrl, MAX_DIR_WAYPOINTS } from './directions-url.helpers'; +export { isGoogleListDataUrl } from './providers/google-list.provider'; export type { DirWaypoint } from './directions-url.helpers'; export type { GpxExportDay, diff --git a/server/src/nest/place-import/providers/google-list.provider.ts b/server/src/nest/place-import/providers/google-list.provider.ts index 60550e104..f904f7407 100644 --- a/server/src/nest/place-import/providers/google-list.provider.ts +++ b/server/src/nest/place-import/providers/google-list.provider.ts @@ -34,6 +34,11 @@ export function isGoogleListDataUrl(url: string): boolean { } } +/** Google's internal endpoint for the places of one shared list. */ +function googleListApiUrl(listId: string): string { + return `https://www.google.com/maps/preview/entitylist/getlist?authuser=0&hl=en&gl=us&pb=!1m1!1s${encodeURIComponent(listId)}!2e2!3e2!4i500!16b1`; +} + export function googleMapsHexId(value: unknown): string | null { if (typeof value !== 'string' && typeof value !== 'number') return null; const raw = String(value).trim(); @@ -124,9 +129,8 @@ export class GoogleListProvider { } // Fetch list data from the configured source or Google's internal API. - const apiUrl = - listDataUrl ?? - `https://www.google.com/maps/preview/entitylist/getlist?authuser=0&hl=en&gl=us&pb=!1m1!1s${encodeURIComponent(listId)}!2e2!3e2!4i500!16b1`; + // Without a data URL the list id was found above, or the read already ended there. + const apiUrl = listDataUrl ?? googleListApiUrl(listId ?? ''); const apiRes = await fetch(apiUrl, { headers: { 'User-Agent': LIST_USER_AGENT }, ...(listDataUrl ? { redirect: 'error' as const } : {}), diff --git a/server/src/nest/places/places.controller.ts b/server/src/nest/places/places.controller.ts index 7872314a2..e87e606a8 100644 --- a/server/src/nest/places/places.controller.ts +++ b/server/src/nest/places/places.controller.ts @@ -7,8 +7,7 @@ import { contentDisposition } from '../common/content-disposition'; import { isDemoWriteBlocked, DEMO_WRITE_ERROR } from '../common/demo-write'; import { PLACE_IMAGE_FILE_FILTER } from '../common/place-image-upload'; import { RequirePermission, TripAccessGuard } from '../permissions/trip-access.guard'; -import { isDirectionsUrl } from '../place-import/place-import.service'; -import { isGoogleListDataUrl } from '../place-import/providers/google-list.provider'; +import { isDirectionsUrl, isGoogleListDataUrl } from '../place-import/place-import.service'; import { StorageService } from '../storage/storage.service'; import { placeImageUrl } from './place-image'; import {