Affirmatech / MeshSense

MeshSense directly connects to your Meshtastic node via Bluetooth or WiFi and continuously provides information to assess the health of your mesh network.
https://affirmatech.com/meshsense
GNU General Public License v3.0
46 stars 6 forks source link

Critical Bug: Receiving Waypoints Crash UI [Remote DoS] #19

Open mblauser opened 1 month ago

mblauser commented 1 month ago

Waypoints sent by remote nodes cause MeshSense 1.0.15-beta.2 on MacOS 14.3.1 to become irrecoverably unresponsive and require restarting the app, both in the:

It is difficult to provide a capture of the received waypoint transmission, since the UI becomes unresponsive. I am not able to find a log file within the application directory on the filesystem. The only entries I can find the in the UI System Log are after restarting the app: 1:33:26:268 TRACE [iMeshDevice:HttpConnection] HandleFromRadio 🚧 Received Queue Status: [object Object]

This is easily reproducible and observed from 'waypoint sending' nodes running firmware 2.4.2.5b45303 and 2.5.0. Local connected 'receiving' node running fw v2.3.12.24458a7.

WebUI: image

Soltares commented 1 month ago

Interesting, thanks for the report! Coincidentally, I have recently added support in 1.0.15-beta.2 for persisting the last runtime log to disk in the application's data directory. The data directory differs per platform:

Linux: ~/.local/share/meshsense Mac: ~/Library/Application\ Support/meshsense Windows: %APPDATA%/../Local/meshsense/Data

lastLog.txt is the log file. Thanks!

mblauser commented 1 month ago

found the log file. Is there a way to enable more verbose debugging?

A single line in the log file occurs when the app UI freeze occurs: 19:11:21:779 TRACE [iMeshDevice:HttpConnection] HandleMeshPacket 📦 Received WAYPOINT_APP packet

It seems that only the UI interfaces freeze (both Electron and web UI). The map and node list {often, but not always} disappear in the web UI interface. No new transmissions visually occur in the list. Neither interface able to:

The app process continues to function in the background, receiving and parsing mesh transmissions as evident by continuing transmission entries in the lastLog.txt. It's even able to send out new traceroute requests.

Soltares commented 1 month ago

This sounds like a front-end Javascript error. You can view the front-end console using Ctrl+Shift+I inside of the Electron window and clicking the Console tab.

mblauser commented 1 month ago

Ctrl+Shift+I doesn't seem to work for me within the Electron window on MacOS, but I did capture the errors on the developer console tab within my web browser.

The same issue occurs both from receiving WAYPOINT_APP and RANGE_TEST_APP transmissions, and likely other types not yet tested.

Here are the respective logs seen within the browser for each: localhost-1727471901982 [WAYPOINT_APP].log localhost-1727472173941 [RANGE_TEST_APP].log

Soltares commented 1 month ago

Looks like for Electron, Developer Console is accessible from the View menu:

image

Relevant error from the log is:

Uncaught (in promise) TypeError: Cannot read properties of undefined (reading 'toString')

Although it is not clear what property is being read. This is odd as unexpected packet types have not historically crashed the front-end and merely displayed "undefined". I wonder if I broke something :sweat_smile:

mblauser commented 1 month ago

ahh, Option+Cmd+I

Same console error within Electron Developer Console

index-DAde3bY6.js:10 highlighted=n.toString(16)

function Qt(n) {
    if (n == $o)
        return "all";
    let e = Fa.value.find(t => t.num == n);
    return e ? qn(e) : `!${n.toString(16)}`
}

at Dh (index-DAde3bY6.js:795:13196) highlighted=Qt(n[31].from)

    let e, t, i = (n[31].rxTime ? new Date(n[31].rxTime * 1e3).toLocaleString(void 0, {
        day: "numeric",
        month: "numeric",
        hour: "numeric",
        minute: "numeric"
    }) : "") + "", s, r, o, a, l, c, h, u, f = Qt(n[31].from) + "", d, g, m, p, y = n[31].channel + "", w, _, E, x = (n[31].hopStart == n[31].hopLimit && n[31].rxSnr || "") + "", I, S, R, T = (n[31].hopStart == n[31].hopLimit && n[31].rxRssi || "") + "", W, G, z, X = (n[31].encrypted ? "encrypted" : (Be = n[31].decoded) == null ? void 0 : Be.portnum) + "", J, Y, L, F, B, N, j, V, $, H = n[31].to != 4294967295 && Fh(n), D = n[31].hopStart && Nh(n);

last entry in the chain at ru (index-DAde3bY6.js:4:1275) highlighted=bd(e.$$)

        try {
            for (; wi < Si.length; ) {
                const e = Si[wi];
                wi++,
                ys(e),
                bd(e.$$)
            }
        } catch (e) {
            throw Si.length = 0,
            wi = 0,
            e
        }
Soltares commented 1 month ago

This code in the error appears to be a minified version of line 82 of Log.svelte

<div class="w-28">{packet.rxTime ? new Date(packet.rxTime * 1000).toLocaleString(undefined, { day: 'numeric', month: 'numeric', hour: 'numeric', minute: 'numeric' }) : ''}</div>
Soltares commented 1 month ago

I have enabled the generation of sourcemaps in vite for future production builds to more easily find the offending line of code.

Soltares commented 1 month ago

I believe this is fixed!! Thank you again @mblauser for the stack traces, they were very helpful. Issue was getNodeNameById failing when passed an undefined id as sometimes happened when packet.to is empty.

I have published 1.0.15-beta.3 that includes this fix. If you have a chance to verify in your environment that would be a huge help. Thanks!

mblauser commented 1 month ago

I've remotely upgraded to beta.3 Since I'm currently traveling outside of my home region, I'll have someone back home initiate a waypoint and range_test, and report back

mblauser commented 1 month ago

Seems fixed. Tested waypoints and UI no longer crashes. Only see a minor UI log representation issue when these are received. The UI displays unknown and undefined. We should probably either 1) just not display them, or 2) make the UI entries more meaningful.

image

WAYPOINT_APP response detail:

{
  "from": 1129781044,
  "to": 4294967295,
  "channel": 0,
  "decoded": {
    "portnum": "WAYPOINT_APP",
    "payload": "COOj87sJFU21chUdHB[xxxxxx]MgR0ZXN0RdzzAQA=",
    "wantResponse": false,
    "dest": 0,
    "source": 0,
    "requestId": 0,
    "replyId": 0,
    "emoji": 0
  },
  "id": 2541539814,
  "rxTime": 1728748555,
  "rxSnr": 2.25,
  "hopLimit": 4,
  "wantAck": false,
  "priority": "UNSET",
  "rxRssi": -103,
  "delayed": "NO_DELAY",
  "viaMqtt": false,
  "hopStart": 5,
  "publicKey": "",
  "pkiEncrypted": false
}

unknown/undefined/undefined response detail:

{
  "event": "onWaypointPacket",
  "json": {
    "id": 2541539814,
    "rxTime": "2024-10-12T15:55:55.000Z",
    "type": "broadcast",
    "from": 1129781044,
    "to": 4294967295,
    "channel": 0,
    "data": {
      "id": 2541539811,
      "latitudeI": 359[xxxxxx],
      "longitudeI": -1152[xxxxxx],
      "expire": 1,
      "lockedTo": 0,
      "name": "test",
      "description": "",
      "icon": 127964
    }
  }
}

[xxxxxx masked]

Thought: Instead of only looking for packet.to, might it make sense to look for either packet.to or packet.json.to ? But either way, still include proper handling of an empty or nonexistant value.

Soltares commented 1 month ago

Thank you very much for the packet detail. When we encounter a new packet type that's not parsed, that's when it comes across as unknown/undefined. Perhaps I can still connect it with the initial WAYPOINT_APP line in the log for improved UX. Essentially the WAYPOINT_APP is the raw packet, and the undefined line is the decoded detail.

I have not encountered waypoint traffic here, but apparently it is a feature in the mobile apps to place points of interest on the map. To display these in MeshSense, we would probably want to find a sensible place to store this information (is it associated with a node or keep a separate list of waypoints?) I'll try to replicate locally and see what makes sense.