diff --git a/app/decoder.py b/app/decoder.py index d2a83fbc..e0352ae9 100644 --- a/app/decoder.py +++ b/app/decoder.py @@ -85,6 +85,10 @@ class PacketInfo: path_hash_size: int = 1 # Bytes per hop: 1, 2, or 3 +def _is_valid_advert_location(lat: float, lon: float) -> bool: + return -90 <= lat <= 90 and -180 <= lon <= 180 + + def extract_payload(raw_packet: bytes) -> bytes | None: """ Extract just the payload from a raw packet, skipping header and path. @@ -243,7 +247,9 @@ def get_packet_payload_type(raw_packet: bytes) -> PayloadType | None: return None -def parse_advertisement(payload: bytes) -> ParsedAdvertisement | None: +def parse_advertisement( + payload: bytes, raw_packet: bytes | None = None +) -> ParsedAdvertisement | None: """ Parse an advertisement payload. @@ -299,6 +305,16 @@ def parse_advertisement(payload: bytes) -> ParsedAdvertisement | None: lon_raw = int.from_bytes(payload[offset + 4 : offset + 8], byteorder="little", signed=True) lat = lat_raw / 1_000_000 lon = lon_raw / 1_000_000 + if not _is_valid_advert_location(lat, lon): + packet_hex = (raw_packet if raw_packet is not None else payload).hex().upper() + logger.warning( + "Dropping location data for nonsensical packet -- packet %s implies lat/lon %s/%s. Outta this world!", + packet_hex, + lat, + lon, + ) + lat = None + lon = None offset += 8 # Skip feature fields if present diff --git a/app/packet_processor.py b/app/packet_processor.py index afeb41b4..2a398503 100644 --- a/app/packet_processor.py +++ b/app/packet_processor.py @@ -425,7 +425,7 @@ async def _process_advertisement( logger.debug("Failed to parse advertisement packet") return - advert = parse_advertisement(packet_info.payload) + advert = parse_advertisement(packet_info.payload, raw_packet=raw_bytes) if not advert: logger.debug("Failed to parse advertisement payload") return diff --git a/frontend/prebuilt/assets/BotCodeEditor-CwRSdUcr.js b/frontend/prebuilt/assets/BotCodeEditor-D9DJubdS.js similarity index 99% rename from frontend/prebuilt/assets/BotCodeEditor-CwRSdUcr.js rename to frontend/prebuilt/assets/BotCodeEditor-D9DJubdS.js index 08cfd18c..1ed337f2 100644 Binary files a/frontend/prebuilt/assets/BotCodeEditor-CwRSdUcr.js and b/frontend/prebuilt/assets/BotCodeEditor-D9DJubdS.js differ diff --git a/frontend/prebuilt/assets/BotCodeEditor-CwRSdUcr.js.map b/frontend/prebuilt/assets/BotCodeEditor-D9DJubdS.js.map similarity index 99% rename from frontend/prebuilt/assets/BotCodeEditor-CwRSdUcr.js.map rename to frontend/prebuilt/assets/BotCodeEditor-D9DJubdS.js.map index 8fd84fc6..67544f59 100644 Binary files a/frontend/prebuilt/assets/BotCodeEditor-CwRSdUcr.js.map and b/frontend/prebuilt/assets/BotCodeEditor-D9DJubdS.js.map differ diff --git a/frontend/prebuilt/assets/CrackerPanel-F1IZPbqZ.js b/frontend/prebuilt/assets/CrackerPanel-XySvf2Tf.js similarity index 99% rename from frontend/prebuilt/assets/CrackerPanel-F1IZPbqZ.js rename to frontend/prebuilt/assets/CrackerPanel-XySvf2Tf.js index cd35b6d9..5d9d929c 100644 Binary files a/frontend/prebuilt/assets/CrackerPanel-F1IZPbqZ.js and b/frontend/prebuilt/assets/CrackerPanel-XySvf2Tf.js differ diff --git a/frontend/prebuilt/assets/CrackerPanel-F1IZPbqZ.js.map b/frontend/prebuilt/assets/CrackerPanel-XySvf2Tf.js.map similarity index 99% rename from frontend/prebuilt/assets/CrackerPanel-F1IZPbqZ.js.map rename to frontend/prebuilt/assets/CrackerPanel-XySvf2Tf.js.map index 01f0ffc2..babae990 100644 Binary files a/frontend/prebuilt/assets/CrackerPanel-F1IZPbqZ.js.map and b/frontend/prebuilt/assets/CrackerPanel-XySvf2Tf.js.map differ diff --git a/frontend/prebuilt/assets/MapView-CpdCCT8F.js b/frontend/prebuilt/assets/MapView-CIrx3Q-Q.js similarity index 92% rename from frontend/prebuilt/assets/MapView-CpdCCT8F.js rename to frontend/prebuilt/assets/MapView-CIrx3Q-Q.js index fb36cd69..a92faa56 100644 Binary files a/frontend/prebuilt/assets/MapView-CpdCCT8F.js and b/frontend/prebuilt/assets/MapView-CIrx3Q-Q.js differ diff --git a/frontend/prebuilt/assets/MapView-CpdCCT8F.js.map b/frontend/prebuilt/assets/MapView-CIrx3Q-Q.js.map similarity index 99% rename from frontend/prebuilt/assets/MapView-CpdCCT8F.js.map rename to frontend/prebuilt/assets/MapView-CIrx3Q-Q.js.map index f29de66a..5d7dea6e 100644 Binary files a/frontend/prebuilt/assets/MapView-CpdCCT8F.js.map and b/frontend/prebuilt/assets/MapView-CIrx3Q-Q.js.map differ diff --git a/frontend/prebuilt/assets/NeighborsMiniMap-BrlNdNzL.js b/frontend/prebuilt/assets/NeighborsMiniMap-BKFex7LO.js similarity index 85% rename from frontend/prebuilt/assets/NeighborsMiniMap-BrlNdNzL.js rename to frontend/prebuilt/assets/NeighborsMiniMap-BKFex7LO.js index 8a0c72c0..5f3a4227 100644 Binary files a/frontend/prebuilt/assets/NeighborsMiniMap-BrlNdNzL.js and b/frontend/prebuilt/assets/NeighborsMiniMap-BKFex7LO.js differ diff --git a/frontend/prebuilt/assets/NeighborsMiniMap-BrlNdNzL.js.map b/frontend/prebuilt/assets/NeighborsMiniMap-BKFex7LO.js.map similarity index 97% rename from frontend/prebuilt/assets/NeighborsMiniMap-BrlNdNzL.js.map rename to frontend/prebuilt/assets/NeighborsMiniMap-BKFex7LO.js.map index 9e5b8ba9..49ebb549 100644 Binary files a/frontend/prebuilt/assets/NeighborsMiniMap-BrlNdNzL.js.map and b/frontend/prebuilt/assets/NeighborsMiniMap-BKFex7LO.js.map differ diff --git a/frontend/prebuilt/assets/PathRouteMap-CI0Ccl1k.js b/frontend/prebuilt/assets/PathRouteMap-DngkQYtL.js similarity index 95% rename from frontend/prebuilt/assets/PathRouteMap-CI0Ccl1k.js rename to frontend/prebuilt/assets/PathRouteMap-DngkQYtL.js index e92e11d0..3b3eedde 100644 Binary files a/frontend/prebuilt/assets/PathRouteMap-CI0Ccl1k.js and b/frontend/prebuilt/assets/PathRouteMap-DngkQYtL.js differ diff --git a/frontend/prebuilt/assets/PathRouteMap-CI0Ccl1k.js.map b/frontend/prebuilt/assets/PathRouteMap-DngkQYtL.js.map similarity index 99% rename from frontend/prebuilt/assets/PathRouteMap-CI0Ccl1k.js.map rename to frontend/prebuilt/assets/PathRouteMap-DngkQYtL.js.map index 6aa3c111..ac6bc6e6 100644 Binary files a/frontend/prebuilt/assets/PathRouteMap-CI0Ccl1k.js.map and b/frontend/prebuilt/assets/PathRouteMap-DngkQYtL.js.map differ diff --git a/frontend/prebuilt/assets/Popup-afIfyuqS.js b/frontend/prebuilt/assets/Popup-CPQBLPRZ.js similarity index 81% rename from frontend/prebuilt/assets/Popup-afIfyuqS.js rename to frontend/prebuilt/assets/Popup-CPQBLPRZ.js index f1d00b12..f6bb377b 100644 Binary files a/frontend/prebuilt/assets/Popup-afIfyuqS.js and b/frontend/prebuilt/assets/Popup-CPQBLPRZ.js differ diff --git a/frontend/prebuilt/assets/Popup-afIfyuqS.js.map b/frontend/prebuilt/assets/Popup-CPQBLPRZ.js.map similarity index 97% rename from frontend/prebuilt/assets/Popup-afIfyuqS.js.map rename to frontend/prebuilt/assets/Popup-CPQBLPRZ.js.map index 38e6b763..5af762cb 100644 Binary files a/frontend/prebuilt/assets/Popup-afIfyuqS.js.map and b/frontend/prebuilt/assets/Popup-CPQBLPRZ.js.map differ diff --git a/frontend/prebuilt/assets/RepeaterDashboard-CNYTKg7R.js b/frontend/prebuilt/assets/RepeaterDashboard-CNYTKg7R.js deleted file mode 100644 index e6a0baba..00000000 Binary files a/frontend/prebuilt/assets/RepeaterDashboard-CNYTKg7R.js and /dev/null differ diff --git a/frontend/prebuilt/assets/RepeaterDashboard-CQiVVwkX.js b/frontend/prebuilt/assets/RepeaterDashboard-CQiVVwkX.js new file mode 100644 index 00000000..93583cbc Binary files /dev/null and b/frontend/prebuilt/assets/RepeaterDashboard-CQiVVwkX.js differ diff --git a/frontend/prebuilt/assets/RepeaterDashboard-CNYTKg7R.js.map b/frontend/prebuilt/assets/RepeaterDashboard-CQiVVwkX.js.map similarity index 66% rename from frontend/prebuilt/assets/RepeaterDashboard-CNYTKg7R.js.map rename to frontend/prebuilt/assets/RepeaterDashboard-CQiVVwkX.js.map index 1ab943b8..f8fd507f 100644 Binary files a/frontend/prebuilt/assets/RepeaterDashboard-CNYTKg7R.js.map and b/frontend/prebuilt/assets/RepeaterDashboard-CQiVVwkX.js.map differ diff --git a/frontend/prebuilt/assets/SearchView-CITGP0uG.js b/frontend/prebuilt/assets/SearchView-WhBxZXuQ.js similarity index 97% rename from frontend/prebuilt/assets/SearchView-CITGP0uG.js rename to frontend/prebuilt/assets/SearchView-WhBxZXuQ.js index f9959a3a..ed5dd986 100644 Binary files a/frontend/prebuilt/assets/SearchView-CITGP0uG.js and b/frontend/prebuilt/assets/SearchView-WhBxZXuQ.js differ diff --git a/frontend/prebuilt/assets/SearchView-CITGP0uG.js.map b/frontend/prebuilt/assets/SearchView-WhBxZXuQ.js.map similarity index 99% rename from frontend/prebuilt/assets/SearchView-CITGP0uG.js.map rename to frontend/prebuilt/assets/SearchView-WhBxZXuQ.js.map index cf5afac5..0e6d772d 100644 Binary files a/frontend/prebuilt/assets/SearchView-CITGP0uG.js.map and b/frontend/prebuilt/assets/SearchView-WhBxZXuQ.js.map differ diff --git a/frontend/prebuilt/assets/SettingsModal-B-RRzLqB.js b/frontend/prebuilt/assets/SettingsModal-Ds7A10ji.js similarity index 99% rename from frontend/prebuilt/assets/SettingsModal-B-RRzLqB.js rename to frontend/prebuilt/assets/SettingsModal-Ds7A10ji.js index 65a3f62f..6924ef15 100644 Binary files a/frontend/prebuilt/assets/SettingsModal-B-RRzLqB.js and b/frontend/prebuilt/assets/SettingsModal-Ds7A10ji.js differ diff --git a/frontend/prebuilt/assets/SettingsModal-B-RRzLqB.js.map b/frontend/prebuilt/assets/SettingsModal-Ds7A10ji.js.map similarity index 99% rename from frontend/prebuilt/assets/SettingsModal-B-RRzLqB.js.map rename to frontend/prebuilt/assets/SettingsModal-Ds7A10ji.js.map index 80702d71..626a5c48 100644 Binary files a/frontend/prebuilt/assets/SettingsModal-B-RRzLqB.js.map and b/frontend/prebuilt/assets/SettingsModal-Ds7A10ji.js.map differ diff --git a/frontend/prebuilt/assets/VisualizerView-6NygNgga.js b/frontend/prebuilt/assets/VisualizerView-DS6ZM6Ne.js similarity index 99% rename from frontend/prebuilt/assets/VisualizerView-6NygNgga.js rename to frontend/prebuilt/assets/VisualizerView-DS6ZM6Ne.js index a1e50ac4..228a80e7 100644 Binary files a/frontend/prebuilt/assets/VisualizerView-6NygNgga.js and b/frontend/prebuilt/assets/VisualizerView-DS6ZM6Ne.js differ diff --git a/frontend/prebuilt/assets/VisualizerView-6NygNgga.js.map b/frontend/prebuilt/assets/VisualizerView-DS6ZM6Ne.js.map similarity index 99% rename from frontend/prebuilt/assets/VisualizerView-6NygNgga.js.map rename to frontend/prebuilt/assets/VisualizerView-DS6ZM6Ne.js.map index 3e7a45a8..275783d8 100644 Binary files a/frontend/prebuilt/assets/VisualizerView-6NygNgga.js.map and b/frontend/prebuilt/assets/VisualizerView-DS6ZM6Ne.js.map differ diff --git a/frontend/prebuilt/assets/hooks-29eXpyfK.js b/frontend/prebuilt/assets/hooks-29eXpyfK.js new file mode 100644 index 00000000..8bd8fff4 Binary files /dev/null and b/frontend/prebuilt/assets/hooks-29eXpyfK.js differ diff --git a/frontend/prebuilt/assets/hooks-B3DSOzgN.js.map b/frontend/prebuilt/assets/hooks-29eXpyfK.js.map similarity index 90% rename from frontend/prebuilt/assets/hooks-B3DSOzgN.js.map rename to frontend/prebuilt/assets/hooks-29eXpyfK.js.map index e0f91662..b448288f 100644 Binary files a/frontend/prebuilt/assets/hooks-B3DSOzgN.js.map and b/frontend/prebuilt/assets/hooks-29eXpyfK.js.map differ diff --git a/frontend/prebuilt/assets/hooks-B3DSOzgN.js b/frontend/prebuilt/assets/hooks-B3DSOzgN.js deleted file mode 100644 index df72b454..00000000 Binary files a/frontend/prebuilt/assets/hooks-B3DSOzgN.js and /dev/null differ diff --git a/frontend/prebuilt/assets/index-Kkb-Bio9.css b/frontend/prebuilt/assets/index-Cx5ENk0V.css similarity index 60% rename from frontend/prebuilt/assets/index-Kkb-Bio9.css rename to frontend/prebuilt/assets/index-Cx5ENk0V.css index 8421d558..92492d9a 100644 Binary files a/frontend/prebuilt/assets/index-Kkb-Bio9.css and b/frontend/prebuilt/assets/index-Cx5ENk0V.css differ diff --git a/frontend/prebuilt/assets/index-GLdV3E1N.js b/frontend/prebuilt/assets/index-DxvL_BZd.js similarity index 96% rename from frontend/prebuilt/assets/index-GLdV3E1N.js rename to frontend/prebuilt/assets/index-DxvL_BZd.js index ed05510c..ddf65656 100644 Binary files a/frontend/prebuilt/assets/index-GLdV3E1N.js and b/frontend/prebuilt/assets/index-DxvL_BZd.js differ diff --git a/frontend/prebuilt/assets/index-GLdV3E1N.js.map b/frontend/prebuilt/assets/index-DxvL_BZd.js.map similarity index 60% rename from frontend/prebuilt/assets/index-GLdV3E1N.js.map rename to frontend/prebuilt/assets/index-DxvL_BZd.js.map index 1a8e5bc5..0b143aab 100644 Binary files a/frontend/prebuilt/assets/index-GLdV3E1N.js.map and b/frontend/prebuilt/assets/index-DxvL_BZd.js.map differ diff --git a/frontend/prebuilt/assets/leaflet-Df2K48et.js b/frontend/prebuilt/assets/leaflet-DtWtaoJ1.js similarity index 99% rename from frontend/prebuilt/assets/leaflet-Df2K48et.js rename to frontend/prebuilt/assets/leaflet-DtWtaoJ1.js index f571bcb8..2cfac8ca 100644 Binary files a/frontend/prebuilt/assets/leaflet-Df2K48et.js and b/frontend/prebuilt/assets/leaflet-DtWtaoJ1.js differ diff --git a/frontend/prebuilt/assets/leaflet-Df2K48et.js.map b/frontend/prebuilt/assets/leaflet-DtWtaoJ1.js.map similarity index 99% rename from frontend/prebuilt/assets/leaflet-Df2K48et.js.map rename to frontend/prebuilt/assets/leaflet-DtWtaoJ1.js.map index 54e7b98c..a09b1b4d 100644 Binary files a/frontend/prebuilt/assets/leaflet-Df2K48et.js.map and b/frontend/prebuilt/assets/leaflet-DtWtaoJ1.js.map differ diff --git a/frontend/prebuilt/assets/separator-iZHk50Ep.js b/frontend/prebuilt/assets/separator-nd9KgFnt.js similarity index 89% rename from frontend/prebuilt/assets/separator-iZHk50Ep.js rename to frontend/prebuilt/assets/separator-nd9KgFnt.js index bdb82d9a..7dccdaab 100644 Binary files a/frontend/prebuilt/assets/separator-iZHk50Ep.js and b/frontend/prebuilt/assets/separator-nd9KgFnt.js differ diff --git a/frontend/prebuilt/assets/separator-iZHk50Ep.js.map b/frontend/prebuilt/assets/separator-nd9KgFnt.js.map similarity index 98% rename from frontend/prebuilt/assets/separator-iZHk50Ep.js.map rename to frontend/prebuilt/assets/separator-nd9KgFnt.js.map index 15fe8140..1d8fe3db 100644 Binary files a/frontend/prebuilt/assets/separator-iZHk50Ep.js.map and b/frontend/prebuilt/assets/separator-nd9KgFnt.js.map differ diff --git a/frontend/prebuilt/index.html b/frontend/prebuilt/index.html index 5145914f..8cf690dc 100644 Binary files a/frontend/prebuilt/index.html and b/frontend/prebuilt/index.html differ diff --git a/frontend/src/components/RepeaterDashboard.tsx b/frontend/src/components/RepeaterDashboard.tsx index ef3b4347..46c0a225 100644 --- a/frontend/src/components/RepeaterDashboard.tsx +++ b/frontend/src/components/RepeaterDashboard.tsx @@ -225,7 +225,7 @@ export function RepeaterDashboard({ ) : (
{/* Top row: Telemetry + Radio Settings | Node Info + Neighbors */} -
+
-
+
{!data ? ( ) : sorted.length === 0 ? (

No neighbors reported

) : ( -
-
+
+
@@ -145,7 +145,7 @@ export function NeighborsPane({ {hasValidRepeaterGps && (neighborsWithCoords.length > 0 || hasValidRepeaterGps) ? ( +
Loading map...
} diff --git a/frontend/src/test/pathUtils.test.ts b/frontend/src/test/pathUtils.test.ts index 0b7f29e8..79dfdc49 100644 --- a/frontend/src/test/pathUtils.test.ts +++ b/frontend/src/test/pathUtils.test.ts @@ -7,6 +7,7 @@ import { formatRouteLabel, formatRoutingOverrideInput, getEffectiveContactRoute, + isValidLocation, resolvePath, formatDistance, formatHopCounts, @@ -665,6 +666,24 @@ describe('resolvePath', () => { }); }); +describe('isValidLocation', () => { + it('rejects null and unset coordinates', () => { + expect(isValidLocation(null, -122.3)).toBe(false); + expect(isValidLocation(47.6, null)).toBe(false); + expect(isValidLocation(0, 0)).toBe(false); + }); + + it('rejects out-of-range coordinates', () => { + expect(isValidLocation(-593.497573, -1659.939204)).toBe(false); + expect(isValidLocation(91, 0)).toBe(false); + expect(isValidLocation(0, 181)).toBe(false); + }); + + it('accepts sane coordinates', () => { + expect(isValidLocation(47.6062, -122.3321)).toBe(true); + }); +}); + describe('formatDistance', () => { it('formats distances under 1km in meters', () => { expect(formatDistance(0.5)).toBe('500m'); diff --git a/frontend/src/utils/pathUtils.ts b/frontend/src/utils/pathUtils.ts index 5d84f266..e0412526 100644 --- a/frontend/src/utils/pathUtils.ts +++ b/frontend/src/utils/pathUtils.ts @@ -273,6 +273,9 @@ export function isValidLocation(lat: number | null, lon: number | null): boolean if (lat === null || lon === null) { return false; } + if (lat < -90 || lat > 90 || lon < -180 || lon > 180) { + return false; + } // (0, 0) is in the Atlantic Ocean - treat as unset if (lat === 0 && lon === 0) { return false; diff --git a/tests/test_decoder.py b/tests/test_decoder.py index a357a0b9..5d67ae34 100644 --- a/tests/test_decoder.py +++ b/tests/test_decoder.py @@ -448,6 +448,38 @@ class TestAdvertisementParsing: assert result.lat is None assert result.lon is None + def test_parse_advertisement_discards_out_of_range_gps(self, caplog): + """Out-of-range advert coordinates are treated as missing.""" + from app.decoder import parse_advertisement + + payload = bytearray() + payload.extend( + bytes.fromhex("f29fdc7c560f9d813d1593a8587fa46a9e7efe2f5506d38c0af41307bf9e517a") + ) + payload.extend((1718749967).to_bytes(4, byteorder="little")) + payload.extend(bytes(64)) + payload.append(0x92) + payload.extend((-593497573).to_bytes(4, byteorder="little", signed=True)) + payload.extend((-1659939204).to_bytes(4, byteorder="little", signed=True)) + payload.extend(b"Tacompton") + raw_packet = bytes.fromhex("11") + bytes(payload) + + with caplog.at_level("WARNING"): + result = parse_advertisement(bytes(payload), raw_packet=raw_packet) + + assert result is not None + assert ( + result.public_key == "f29fdc7c560f9d813d1593a8587fa46a9e7efe2f5506d38c0af41307bf9e517a" + ) + assert result.name == "Tacompton" + assert result.device_role == 2 + assert result.lat is None + assert result.lon is None + assert "Dropping location data for nonsensical packet -- packet" in caplog.text + assert raw_packet.hex().upper() in caplog.text + assert "-593.497573/-1659.939204" in caplog.text + assert "Outta this world!" in caplog.text + def test_parse_advertisement_extracts_public_key(self): """Advertisement parsing extracts the public key correctly.""" from app.decoder import parse_advertisement, parse_packet