diff --git a/lib/tdeck_ui/Telemetry/LocationMessagePolicy.cpp b/lib/tdeck_ui/Telemetry/LocationMessagePolicy.cpp index 595aa181..919ab4ab 100644 --- a/lib/tdeck_ui/Telemetry/LocationMessagePolicy.cpp +++ b/lib/tdeck_ui/Telemetry/LocationMessagePolicy.cpp @@ -56,6 +56,16 @@ LocationMessageDecision classifyInboundLocationMessage( return decision; } + if (!isValidPeerLocationInput(decision.location, + decision.meta, + message.received_at_millis)) { + decision.kind = LocationMessageKind::MALFORMED_LOCATION; + decision.log_malformed = true; + setChatActions(decision, human_text); + decision.drop = !human_text; + return decision; + } + decision.kind = decision.meta.has_cease && decision.meta.cease ? LocationMessageKind::VALID_CEASE : LocationMessageKind::VALID_LOCATION; diff --git a/lib/tdeck_ui/Telemetry/LocationShareState.cpp b/lib/tdeck_ui/Telemetry/LocationShareState.cpp index 24fa8dd0..21a10905 100644 --- a/lib/tdeck_ui/Telemetry/LocationShareState.cpp +++ b/lib/tdeck_ui/Telemetry/LocationShareState.cpp @@ -36,6 +36,26 @@ bool locationInRange(const LocationTelemetry& location) { } // namespace +bool isValidPeerLocationInput( + const LocationTelemetry& location, + const CustomLocationMeta& meta, + uint64_t received_at_millis) { + if ((meta.has_expires && meta.expires_millis < 0) || + (meta.has_approx_radius && meta.approx_radius_meters < 0)) { + return false; + } + uint64_t source_timestamp_millis = 0; + if (!effectiveTimestampMillis(location, meta, source_timestamp_millis)) { + return false; + } + if (meta.has_cease && meta.cease) return true; + if (meta.has_expires && + received_at_millis >= static_cast(meta.expires_millis)) { + return true; + } + return locationInRange(location); +} + bool PeerLocationStore::peerEquals(const PeerId& left, const PeerId& right) { return std::memcmp(left.bytes, right.bytes, PEER_ID_SIZE) == 0; } @@ -103,8 +123,7 @@ PeerLocationResult PeerLocationStore::apply( const LocationTelemetry& location, const CustomLocationMeta& meta, uint64_t received_at_millis) { - if ((meta.has_expires && meta.expires_millis < 0) || - (meta.has_approx_radius && meta.approx_radius_meters < 0)) { + if (!isValidPeerLocationInput(location, meta, received_at_millis)) { return PeerLocationResult::INVALID_ARGUMENT; } diff --git a/lib/tdeck_ui/Telemetry/LocationShareState.h b/lib/tdeck_ui/Telemetry/LocationShareState.h index a0be8999..4b9cfbf5 100644 --- a/lib/tdeck_ui/Telemetry/LocationShareState.h +++ b/lib/tdeck_ui/Telemetry/LocationShareState.h @@ -36,6 +36,13 @@ enum class PeerLocationResult : uint8_t { INVALID_ARGUMENT, }; +// Mirrors the argument-domain checks performed by PeerLocationStore::apply(). +// Staleness and capacity remain store-state decisions, not wire validity. +bool isValidPeerLocationInput( + const LocationTelemetry& location, + const CustomLocationMeta& meta, + uint64_t received_at_millis); + class PeerLocationStore { public: PeerLocationStore() = default; diff --git a/tests/native/test_location_message_policy.cpp b/tests/native/test_location_message_policy.cpp index 6274b148..7f93c1a3 100644 --- a/tests/native/test_location_message_policy.cpp +++ b/tests/native/test_location_message_policy.cpp @@ -3,6 +3,7 @@ #include #include #include +#include #include "Telemetry/LocationMessagePolicy.h" @@ -184,6 +185,13 @@ void malformedLocationFailsClosedButTextSurvives() { decision = Telemetry::classifyInboundLocationMessage(message); CHECK(decision.kind == Telemetry::LocationMessageKind::MALFORMED_LOCATION); CHECK(!decision.apply_location); + + Fields unusable_timestamp = + validFields(std::numeric_limits::max()); + message = input(peer(3), unusable_timestamp); + decision = Telemetry::classifyInboundLocationMessage(message); + CHECK(decision.kind == Telemetry::LocationMessageKind::MALFORMED_LOCATION); + CHECK(!decision.apply_location && decision.drop && decision.log_malformed); } void ceaseAndAuthenticatedSenderIsolation() {