diff --git a/client/src/components/Journey/JourneyMap.test.tsx b/client/src/components/Journey/JourneyMap.test.tsx index 61c27e5b7..2678fc038 100644 --- a/client/src/components/Journey/JourneyMap.test.tsx +++ b/client/src/components/Journey/JourneyMap.test.tsx @@ -550,4 +550,30 @@ describe('JourneyMap', () => { expect(colors).not.toContain('#ff0000'); expect(colors).not.toContain('#ffffff'); }); + + // A string handed to bindTooltip becomes innerHTML. A track name is a place + // name off a shared trip and an entry label is a journey title, so both are + // written by someone who is not necessarily the person reading the map — and + // this component is what the public journey page renders. + it('FE-COMP-JOURNEYMAP-044: escapes a track name before it becomes tooltip markup', () => { + vi.mocked(L.polyline).mockClear(); + render(' })]} />); + + const line = vi.mocked(L.polyline).mock.results + .map(r => r.value as any) + .find(v => v.bindTooltip.mock.calls.length > 0); + const [label] = line.bindTooltip.mock.calls[0]; + expect(label).not.toContain('', mood: null, entry_date: '2025-06-04' }]; + render(); + + const labels = vi.mocked(mockedMarker().bindTooltip).mock.calls.map(c => c[0]); + expect(labels.some(l => typeof l === 'string' && l.includes(' allCoords.push(c)) } @@ -243,7 +244,9 @@ function JourneyMap( }) const marker = L.marker(pos, { icon }).addTo(map) - marker.bindTooltip(item.label, { + // Escaped for the same reason as the track tooltip above: the label is an + // entry title, and this map is what the public journey page renders. + marker.bindTooltip(escapeHtml(item.label), { direction: 'top', offset: [0, -MARKER_H], className: 'map-tooltip', diff --git a/client/src/components/Map/MapViewGL.test.tsx b/client/src/components/Map/MapViewGL.test.tsx index 550e14a9b..eceae5a2f 100644 --- a/client/src/components/Map/MapViewGL.test.tsx +++ b/client/src/components/Map/MapViewGL.test.tsx @@ -851,6 +851,23 @@ describe('MapViewGL', () => { expect(glMarkers.created[1].lngLat).toEqual([2.12, 48.12]) }) + // The Leaflet twin of this is FE-COMP-MAPVIEW-074. Both renderers build their + // marker as an HTML string, so both need the escape pinned — otherwise it can + // be dropped from one of them without a single test going red. + it('FE-COMP-MAPVIEWGL-023b: an image_url that passes the prefix check is still escaped', async () => { + loadOnAttach() + render(${b.name}${b.notes ? `
${b.notes}
` : ''}`, + `
${escapeHtml(b.name)}
${b.notes ? `
${escapeHtml(b.notes)}
` : ''}`, { className: 'atlas-tooltip', direction: 'top', offset: [0, -14] } ) })