Anyone who can publish to an MQTT broker that CoreScope reads controls
three strings: the observer `iata` (a topic segment), the observer
`name` (the JSON `origin` field) and the observer `id` (a topic
segment). Anyone with a radio controls the node `name` in an ADVERT.
Eight places wrote these into the page with `innerHTML` without
escaping, so a crafted value runs as script in every visitor's browser:
| file | what |
|---|---|
| `packets.js` | Region badge in the group, child and single packet rows
|
| `observers.js` | Region badge in the observers table and in the
slide-over |
| `live.js` | "Heard By — Regions" header in the node detail |
| `nodes.js` | observer name in the clock-skew evidence panel |
| `analytics.js` | `observer_id` inside `data-observer=` and `id=`
attributes |
| `app.js` | node name in the favorites dropdown |
| `customize-v2.js` | node name in the geofilter prune preview (admin
page) |
**Fix:** each value now goes through the existing `escapeHtml()` /
`esc()` helper. No behaviour change for normal values.
**Tests:** one test per sink added to
`tests/unit/test-xss-escape-sinks.js`, following the file's existing
pattern (pull the template out of the source, render it with a hostile
value, check the output is escaped). Reverting any single fix makes its
test fail. 41/41 pass.
**Notes for reviewers:**
- The ingestor uppercases `iata` before storing it. That is not a
defence: tag and attribute names are case-insensitive, and script can be
written with numeric character references.
- An operator who has set an `observerIATAWhitelist` is protected from
the `iata` sinks but not from the name/id sinks.
- Follow-ups worth a separate PR: validate `iata` and observer `id` to a
safe character set at ingest, cap the `origin` length, and add a
`Content-Security-Policy` header. This PR is escaping only so it is easy
to review and safe to ship.
Running in production on our instance since 2026-10-07.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-authored-by: Claude Mythos 5.1 <noreply@anthropic.com>