From 0162ec0cdf0de63ead5789f8f8ae26fcf0c78df8 Mon Sep 17 00:00:00 2001 From: jing Date: Mon, 13 Jul 2026 20:55:42 +1000 Subject: [PATCH] pre-deploy: escape external feed strings in InfoBox HTML (ship-check) Defense-in-depth for the public deploy: USGS/EONET place/title/category are now HTML-escaped before interpolation into Cesium InfoBox descriptions, and the USGS/EONET links are passed through lib.safeUrl (http(s)-only) so a hostile feed can't inject markup or a javascript:/data: href. Entity names/labels stay raw (Cesium renders those as text). Verified: escapeHtml neutralizes , safeUrl drops javascript:/data:, 219 quakes + 500 fires still render normally. ship-check pass: no auth surface (dev-only history/snap not deployed); no secrets in tree (AIS key empty, data/ gitignored); proxy upstreams are a fixed dict (no SSRF); no money paths. Co-Authored-By: Claude Opus 4.8 --- js/layers/fires.js | 8 ++++---- js/layers/quakes.js | 7 ++++--- js/lib.js | 18 ++++++++++++++++++ 3 files changed, 26 insertions(+), 7 deletions(-) diff --git a/js/layers/fires.js b/js/layers/fires.js index 01062b0..ebc1ea2 100644 --- a/js/layers/fires.js +++ b/js/layers/fires.js @@ -59,7 +59,7 @@ export default function create(ctx) { if (!loc) continue; const title = ev.title || 'Wildfire'; const category = (ev.categories && ev.categories[0] && ev.categories[0].title) || 'Wildfires'; - const link = ev.link || (ev.id ? `https://eonet.gsfc.nasa.gov/api/v3/events/${ev.id}` : null); + const link = lib.safeUrl(ev.link || (ev.id ? `https://eonet.gsfc.nasa.gov/api/v3/events/${encodeURIComponent(ev.id)}` : '')); const dateTxt = loc.date ? new Date(loc.date).toISOString().replace('T', ' ').replace('.000Z', ' UTC') : '—'; ds.entities.add({ @@ -88,11 +88,11 @@ export default function create(ctx) { }, description: `` + - `` + - `` + + `` + + `` + `` + `
Event${title}
Category${category}
Event${lib.escapeHtml(title)}
Category${lib.escapeHtml(category)}
Last report${dateTxt}
` + - (link ? `

Open event on EONET ↗

` : '') + + (link ? `

Open event on EONET ↗

` : '') + `

Active wildfire event from NASA EONET (real data).

`, }); count++; diff --git a/js/layers/quakes.js b/js/layers/quakes.js index 02f30d1..d8c252a 100644 --- a/js/layers/quakes.js +++ b/js/layers/quakes.js @@ -64,7 +64,8 @@ export default function create(ctx) { const depth = coords[2]; const mag = p.mag; const timeMs = p.time; - const place = p.place || 'Unknown location'; + const place = p.place || 'Unknown location'; // raw: used in name/label (text contexts) + const url = lib.safeUrl(p.url); const size = baseSize(mag); const color = magColor(mag); const recentAtBuild = typeof timeMs === 'number' && (Date.now() - timeMs) < 3_600_000; @@ -93,11 +94,11 @@ export default function create(ctx) { description: `` + `` + - `` + + `` + `` + `` + `
MagnitudeM${mag.toFixed(1)}
Place${place}
Place${lib.escapeHtml(place)}
Depth${depth != null ? depth.toFixed(1) + ' km' : '—'}
Time (UTC)${timeMs != null ? new Date(timeMs).toISOString().replace('T', ' ').replace('.000Z', ' UTC') : '—'}
` + - (p.url ? `

USGS event page ↗

` : ''), + (url ? `

USGS event page ↗

` : ''), }); // Labels only for the notable quakes, to keep the globe legible. diff --git a/js/lib.js b/js/lib.js index 8d6b939..8179c78 100644 --- a/js/lib.js +++ b/js/lib.js @@ -77,6 +77,24 @@ export function altitudeColor(CONFIG, altMeters) { return c.aircraftHigh; } +// Escape a string for safe interpolation into InfoBox description HTML. +// Feeds like USGS/EONET are trusted, but this is defense-in-depth for the +// public deploy (and the InfoBox is a real HTML sink). +export function escapeHtml(s) { + return String(s == null ? '' : s) + .replace(/&/g, '&') + .replace(//g, '>') + .replace(/"/g, '"') + .replace(/'/g, '''); +} + +// Only allow http(s) URLs into href attributes (blocks javascript:/data: URLs). +export function safeUrl(u) { + const s = String(u == null ? '' : u).trim(); + return /^https?:\/\//i.test(s) ? s : ''; +} + // CSS hex → Cesium.Color, optional alpha. export function cz(hex, alpha) { const col = Cesium.Color.fromCssColorString(hex);