Skip to content

fix(server): repair Pending lastSeen acknowledgement bookkeeping - #1537

Open
GoatTech-42 wants to merge 2 commits into
PrismarineJS:masterfrom
GoatTech-42:fix/chat-pending-lastseen
Open

GoatTech-42 wants to merge 2 commits into
PrismarineJS:masterfrom
GoatTech-42:fix/chat-pending-lastseen

Conversation

@GoatTech-42

Copy link
Copy Markdown

Fixes #1535.

The reported bug: Pending.setPreviouslyAcknowledged() stored the return value of Array.prototype.push() (a length) instead of the array, so the next validation threw TypeError: this.lastSeen.some is not a function.

While writing a regression test for it, three more bugs surfaced in the same Pending acknowledgement path (src/server/chat.js, used for 1.19.1/1.19.2 servers with enforceSecureProfile) - each one alone breaks the flow before the reported line is even reached, so the PR fixes all four:

  1. validateLastMessages called pending.get(messageSender)(messageSignature) - a curried call on a two-argument method. pending.get(...) returns a timestamp or undefined, so any first-time acknowledgement entry threw TypeError: pending.get(...) is not a function. Now pending.get(messageSender, messageSignature).

  2. setPreviouslyAcknowledged (the reported bug) - now builds the array directly and only appends the rejected entry when it is non-empty:

    this.lastSeen = lastSeen.map(e => Object.values(e))
    const rejected = Object.values(lastRejected)
    if (rejected.length) this.lastSeen.push(rejected)
  3. Signatures were compared by identity (===) in previouslyAcknowledged, acknowledge, and acknowledgePrior. Packet signatures are freshly parsed Buffers per message, so a client repeating a previously acknowledged entry could never match - exactly the case setPreviouslyAcknowledged exists to allow. Added a signaturesEqual helper that uses Buffer.equals when both sides are buffers. This also fixes acknowledge's findIndex returning -1 and splice(-1, 1) silently dropping the newest pending entry when a lastRejected signature didn't identity-match.

  4. acknowledgePrior deleted this.m[a] (a sender's whole timestamp map) and called this.splice(0, i), which leaves the matched entry itself in the pending list. It now finds the match first, deletes only the covered per-signature entries, and splices index + 1 entries.

Regression tests in test/chatPendingTest.js drive the real chat_message handler across two packets: a repeated acknowledgement (re-parsed buffer, same bytes) must be accepted, a message the server never sent must still disconnect with multiplayer.disconnect.chat_validation_failed. On current master the first test fails on the very first packet (bug 1); with bug 1 fixed alone it fails on the second packet (the reported bug 2, then bug 3).

Lint (standard) passes; the new tests pass against the fixed code and fail against master.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

setPreviouslyAcknowledged() stores an array length instead of the acknowledgement list

1 participant