fix(waypoint): reject non-owner modification of a stored locked waypoint#6348
Merged
Conversation
Contributor
📝 WalkthroughWalkthroughChangesWaypoint and packet handling
Estimated code review effort: 3 (Moderate) | ~25 minutes Possibly related PRs
Suggested reviewers: 🚥 Pre-merge checks | ✅ 4✅ Passed checks (4 passed)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Inbound waypoint handling only validated the incoming packet's locked_to against its sender, never the already-stored waypoint's owner. A non-owner could hijack a locked waypoint by replaying its id with locked_to = 0 (unlock) or their own num (takeover) — both slip past the inbound-only check. handleWaypoint now reads the currently-stored waypoint (getWaypoints() collapsed via activeWaypointPackets, the same normalisation the map UI and geofence engine use) and drops updates to a locked waypoint from anyone but the node it is locked to — including inbound locked_to = 0 unlock attempts. The stored-owner read and the persist share one session lease (persistDataPacket extracted from rememberDataPacket) so the check sees a committed snapshot instead of nesting a second lease across two coroutines. Preserves new-waypoint creation, owner unlock/edit, updates to unlocked waypoints, and the pre-existing "can't lock to a third party" rule. Adds 7 regression tests in core/data MeshDataHandlerTest. Follow-up to #6344 (deferred CodeRabbit review item); enforcement is client-side and self-heals across re-broadcasts. Un-draft gate is on-mesh / replay validation. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
jamesarich
force-pushed
the
claude/wonderful-tharp-95f087
branch
from
July 21, 2026 21:56
947c18b to
7785c1c
Compare
Collaborator
Author
|
@coderabbitai review |
Contributor
✅ Action performedReview finished.
|
jamesarich
marked this pull request as ready for review
July 21, 2026 22:05
jamesarich
enabled auto-merge
July 21, 2026 22:21
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
A locked waypoint (
locked_to != 0) is meant to be editable only by the node it islocked to. Inbound waypoint handling only validated the incoming packet's
locked_toagainst its sender (
locked_to == 0 || locked_to == from) — it never consulted thealready-stored waypoint's owner. So a non-owner could hijack someone else's locked
waypoint by re-sending an update for the same waypoint id with:
locked_to = 0— silently unlocks and overwrites the stored waypoint, orlocked_to = <their own num>— takes over ownership.Both slip past the inbound-only check. This is a pre-existing protocol-facing gap (called
out during CodeRabbit review of #6344 and deferred there as out-of-scope, since #6344 was a
straight refactor of the existing check). This PR closes it.
What
MeshDataHandlerImpl.handleWaypointnow, before persisting an update, reads thecurrently-stored waypoint for that id and rejects the update when the stored copy is locked
to a different node than the sender — including inbound
locked_to = 0unlock attempts.PacketRepository.getWaypoints()collapsed throughactiveWaypointPackets(now)(newest-per-id, expired dropped) — the same normalisation themap UI and geofence engine already use, so the three can't drift.
Waypoint.isModifiableBy(the sharedlocked_tohelper added in fix(map): allow editing/deleting your own locked waypoint #6344) for thestored-owner check as well, so the inbound and persisted checks share one source of truth.
launchSessionWorklease(
persistDataPacketextracted fromrememberDataPacket), so the check sees a committedsnapshot rather than nesting a second lease across two coroutines.
rememberDataPacket's body split intopersistDataPacket(behaviour-preserving; theduplicate-skip
returnsemantics are unchanged).Preserved (not dropped): new-waypoint creation, owner unlock/edit, updates to unlocked
waypoints, and the inbound
isModifiableByrule (a node can't lock a waypoint to a third party).Behaviour matrix
fromlocked_toTesting performed
core/dataMeshDataHandlerTestcovering every row above (7 cases).spotlessApply spotlessCheck detekt assembleDebug test allTests kmpSmokeCompile.Testing note
Unit-tested and baseline-green (above), but not yet validated on a real mesh / replay
(burningmesh-replay sandbox) to confirm no legitimate cross-client waypoint updates are
dropped — worth a maintainer's on-device check before merge. Notes:
locked_tois advisory on the wire); this protects thelocal DB/view, matching how the map UI already gates edits.
fromnolonger matches the old
locked_to, so their edits would be dropped — but the map UIalready blocks the owner from editing in that state (
locked_to == myNodeNum), so no newinconsistency is introduced. Waypoints also expire.
getWaypoints()reflects committed rows, so areplay landing before the owner's create commits isn't a hard transactional lock — but it
self-heals on the owner's next re-broadcast (their locked copy becomes newest again).
Follow-up to #6344 / thread
#6344 (comment)
Rebased onto
mainafter #6344 merged, so the inbound check and this PR's stored-ownercheck both go through the same
Waypoint.isModifiableByhelper.