* Update CONTRIBUTING.md
* Update CONTRIBUTING.md
* Escape mesh-provided strings before inserting them into the page
Node names, firmware strings, channel ids and message text all come from
the radio network, so anyone with a Meshtastic node (or publish access to
the MQTT broker) chooses their contents. Several pages dropped those
values straight into innerHTML / insertAdjacentHTML / Leaflet popups,
which lets a crafted node name run JavaScript in every visitor's browser.
Add a small shared helper (static/html-escape.js, loaded by base.html and
kiosk.html) and route every mesh-controlled value on the node list, top
nodes, firehose, node, packet and kiosk pages through it. Pages that
already had a local escapeHtml (chat, net, map, reliability) are unchanged.
Co-Authored-By: Claude Mythos 5.1 <noreply@anthropic.com>
---------
Co-authored-by: Pablo Revilla <pablorevilla@gmail.com>
Co-authored-by: Nathan <nathan@stonekitty.net>
Co-authored-by: Claude Mythos 5.1 <noreply@anthropic.com>
* Update CONTRIBUTING.md
* Update CONTRIBUTING.md
* Keep /api/lang and the static page route inside their directories
Two routes built a filename from request input without checking where it
ended up:
- /api/lang?lang=... joined the value onto the lang directory and added
".json". os.path.join discards the directory when the value is an
absolute path, so ?lang=/some/where/file returned /some/where/file.json.
Any .json file the server process can read was served.
- /{page} appended ".html" and read static/<page>. aiohttp passes an
encoded slash (%2F) through a single path segment and unquotes it, so
/..%2F..%2F..%2Fx read ../../../x.html relative to the static directory.
The lang route now only accepts a plain language code (en, es, pt-BR, ...)
and falls back to English otherwise, exactly as it already did for unknown
languages. The page route resolves the final path and requires it to be a
file inside meshview/static, the same check /docs/{doc} already does.
Co-Authored-By: Claude Mythos 5.1 <noreply@anthropic.com>
---------
Co-authored-by: Pablo Revilla <pablorevilla@gmail.com>
Co-authored-by: Nathan <nathan@stonekitty.net>
Co-authored-by: Claude Mythos 5.1 <noreply@anthropic.com>
* Update CONTRIBUTING.md
* Update CONTRIBUTING.md
* Do not let payload search match redacted direct messages
/api/packets shows the text of a direct message (TEXT_MESSAGE_APP to a
specific node) as "<redacted>". The contains= filter, however, is a LIKE
over the stored packet bytes, which include the decrypted text for
messages on a known channel key. With portnum left out of the request,
contains=<guess> returned the packet when the guess appeared in a redacted
message, so the text could be recovered one substring at a time.
Direct text messages are now excluded from the substring match. Broadcast
messages and all other packet types search exactly as before.
Co-Authored-By: Claude Mythos 5.1 <noreply@anthropic.com>
---------
Co-authored-by: Pablo Revilla <pablorevilla@gmail.com>
Co-authored-by: Nathan <nathan@stonekitty.net>
Co-authored-by: Claude Mythos 5.1 <noreply@anthropic.com>
* Update CONTRIBUTING.md
* Update CONTRIBUTING.md
* fix: limit the neighbor SNR chart to the last 7 days
The chart asked for the last 500 neighbor-info packets and put no
limit on how old they could be. On a busy node that is several weeks
of history, and every neighbor heard in that span gets its own line.
On one node it was 119 packets covering 21.8 days and 339 lines.
The chart now asks for the last 7 days, using the `since` parameter
that /api/packets already supports, and draws only the 15 neighbors
with the most data points in that window. A subtitle says how many
neighbors are shown and how many there were, so a missing line is not
a mystery.
Three smaller fixes in the same function:
- Neighbor names are looked up only for the lines that are drawn.
fetchNodeFromApi() does not cache a node it cannot find, so every
unknown neighbor was fetched again on every packet it appeared in.
- Each point is read from a Map instead of calling indexOf() on the
timestamp list, which was quadratic in the number of points.
- Two neighbors sharing a short name are told apart. ECharts
identifies a series by its name, so they were drawn as one line.
The "Export CSV" button for this chart wrote an empty file, because
neighborData was declared but never filled in. It is now filled in
from the neighbors on screen.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Pablo Revilla <pablorevilla@gmail.com>
Co-authored-by: Nathan <nathan@stonekitty.net>
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>