Fix XSS in map popups by replacing setHTML with setDOMContent
- POI popup: use DOM API with textContent (auto-escapes) instead of template literal in setHTML - Listing popup: replace renderToString + setHTML with createRoot + setDOMContent for proper React lifecycle
This commit is contained in:
parent
0a9a83507e
commit
727dd537ef
1 changed files with 17 additions and 7 deletions
|
|
@ -3,7 +3,7 @@ import mapboxgl from "mapbox-gl";
|
||||||
import 'mapbox-gl/dist/mapbox-gl.css';
|
import 'mapbox-gl/dist/mapbox-gl.css';
|
||||||
import { useEffect, useRef, useMemo, useCallback } from "react";
|
import { useEffect, useRef, useMemo, useCallback } from "react";
|
||||||
import { Crosshair } from "lucide-react";
|
import { Crosshair } from "lucide-react";
|
||||||
import { renderToString } from 'react-dom/server';
|
import { createRoot } from 'react-dom/client';
|
||||||
import "../assets/Map.css";
|
import "../assets/Map.css";
|
||||||
import { Metric, type ParameterValues } from "./FilterPanel";
|
import { Metric, type ParameterValues } from "./FilterPanel";
|
||||||
import { PropertyCard } from "./PropertyCard";
|
import { PropertyCard } from "./PropertyCard";
|
||||||
|
|
@ -253,11 +253,18 @@ export function Map(props: MapProps) {
|
||||||
el.style.cssText = 'width:24px;height:24px;background:#ef4444;border:2px solid white;border-radius:50%;box-shadow:0 2px 4px rgba(0,0,0,0.3);cursor:pointer;';
|
el.style.cssText = 'width:24px;height:24px;background:#ef4444;border:2px solid white;border-radius:50%;box-shadow:0 2px 4px rgba(0,0,0,0.3);cursor:pointer;';
|
||||||
el.title = poi.name;
|
el.title = poi.name;
|
||||||
|
|
||||||
|
const popupContent = document.createElement('div');
|
||||||
|
popupContent.style.cssText = 'padding:4px 8px';
|
||||||
|
const nameEl = document.createElement('strong');
|
||||||
|
nameEl.textContent = poi.name;
|
||||||
|
const addressEl = document.createElement('span');
|
||||||
|
addressEl.style.cssText = 'color:#666;font-size:12px';
|
||||||
|
addressEl.textContent = poi.address;
|
||||||
|
popupContent.append(nameEl, document.createElement('br'), addressEl);
|
||||||
|
|
||||||
const marker = new mapboxgl.Marker({ element: el })
|
const marker = new mapboxgl.Marker({ element: el })
|
||||||
.setLngLat([poi.longitude, poi.latitude])
|
.setLngLat([poi.longitude, poi.latitude])
|
||||||
.setPopup(new mapboxgl.Popup({ offset: 12 }).setHTML(
|
.setPopup(new mapboxgl.Popup({ offset: 12 }).setDOMContent(popupContent))
|
||||||
`<div style="padding:4px 8px"><strong>${poi.name}</strong><br/><span style="color:#666;font-size:12px">${poi.address}</span></div>`
|
|
||||||
))
|
|
||||||
.addTo(mapRef.current);
|
.addTo(mapRef.current);
|
||||||
|
|
||||||
poiMarkersRef.current.push(marker);
|
poiMarkersRef.current.push(marker);
|
||||||
|
|
@ -352,12 +359,15 @@ export function Map(props: MapProps) {
|
||||||
|
|
||||||
const properties = heatmapRef.current._tree.search(searchBounds);
|
const properties = heatmapRef.current._tree.search(searchBounds);
|
||||||
if (properties.length > 0) {
|
if (properties.length > 0) {
|
||||||
const listingDialogPopup = getListingDialog(properties);
|
const container = document.createElement('div');
|
||||||
new mapboxgl.Popup()
|
const root = createRoot(container);
|
||||||
|
root.render(getListingDialog(properties));
|
||||||
|
const popup = new mapboxgl.Popup()
|
||||||
.setLngLat([longitude, latitude])
|
.setLngLat([longitude, latitude])
|
||||||
.setHTML(renderToString(listingDialogPopup))
|
.setDOMContent(container)
|
||||||
.setMaxWidth("450px")
|
.setMaxWidth("450px")
|
||||||
.addTo(mapRef.current);
|
.addTo(mapRef.current);
|
||||||
|
popup.on('close', () => root.unmount());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue