Files
nullrouten0andClaude Mythos 5.1 57395e2b28 fix(ui): escape observer iata, name and id, and node names, in eight render sinks (#2118)
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>
2026-10-08 22:59:25 +02:00
..