Skip to content

ensure seeker is valid#382

Open
peterjah wants to merge 1 commit intodevfrom
sdk_enckey
Open

ensure seeker is valid#382
peterjah wants to merge 1 commit intodevfrom
sdk_enckey

Conversation

@peterjah
Copy link
Contributor

No description provided.

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds validation to ensure seeker fields in reply and forward messages are complete before deserialization, preventing truncated messages from causing errors downstream. It introduces a new assertAvailable helper function to validate buffer boundaries and adds defensive checks in the message lookup logic.

Changes:

  • Added assertAvailable helper function to validate buffer size before reading message components during deserialization
  • Added buffer size validation checks at critical points in reply and forward message deserialization (content length, content, and seeker)
  • Added defensive validation in MessageService.findMessageBySeeker to handle empty or invalid seekers gracefully
  • Added test cases to verify truncated messages throw appropriate errors

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 6 comments.

File Description
gossip-sdk/src/utils/messageSerialization.ts Adds assertAvailable helper function and buffer validation checks in reply/forward message deserialization to ensure seekers are complete
gossip-sdk/src/services/message.ts Adds defensive check to validate seeker is non-empty Uint8Array before database lookup, preventing IndexedDB errors
gossip-sdk/test/utils/serialization.spec.ts Adds test cases for truncated seekers in reply and forward messages to ensure proper error handling

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +242 to +249
assertAvailable(buffer, offset, 4, 'forward');
const forwardContentLen = Number(
U32.fromBytes(buffer.slice(offset, offset + 4))
);
offset += 4;

// Read forward content
assertAvailable(buffer, offset, forwardContentLen, 'forward');
Copy link

Copilot AI Feb 14, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The assertAvailable function validates that enough bytes are available for the content length itself, but it does not validate that the content length value is reasonable before attempting to validate the content bytes. If forwardContentLen is an extremely large number (e.g., due to corruption), this could lead to an unhelpful error message or potential performance issues. Consider adding a sanity check on the length value (e.g., maximum message size) before using it to validate content availability.

Copilot uses AI. Check for mistakes.
Comment on lines +436 to +445
if (!(seeker instanceof Uint8Array) || seeker.byteLength === 0) {
logger
.forMethod('findMessageBySeeker')
.warn('skipping invalid seeker lookup', {
ownerUserId,
seekerLength:
seeker instanceof Uint8Array ? seeker.byteLength : 'not-uint8array',
});
return undefined;
}
Copy link

Copilot AI Feb 14, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The defensive check validates that the seeker is a Uint8Array and has non-zero length, but does not verify that it has the expected length of 34 bytes (SEEKER_SIZE). While the deserialization validation should ensure seekers are always 34 bytes, adding a check for the exact size would make this function more robust and would catch any potential bugs in the deserialization logic or other code paths that might pass seekers to this function. Consider adding a check like: seeker.byteLength !== 34

Copilot uses AI. Check for mistakes.
Comment on lines +434 to +443
// Malformed payloads can carry an empty seeker in reply/forward metadata.
// fake-indexeddb rejects that as an IndexedDB key and throws DataError.
if (!(seeker instanceof Uint8Array) || seeker.byteLength === 0) {
logger
.forMethod('findMessageBySeeker')
.warn('skipping invalid seeker lookup', {
ownerUserId,
seekerLength:
seeker instanceof Uint8Array ? seeker.byteLength : 'not-uint8array',
});
Copy link

Copilot AI Feb 14, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The logging pattern here creates the logger inline rather than storing it in a const variable like other methods in this file (e.g., fetchMessages, storeDecryptedMessages). While this works correctly, for consistency with the rest of the file, consider creating a const log variable at the start of the function: const log = logger.forMethod('findMessageBySeeker'); and then using log.warn(...). This is a minor stylistic preference based on the patterns seen elsewhere in this file.

Suggested change
// Malformed payloads can carry an empty seeker in reply/forward metadata.
// fake-indexeddb rejects that as an IndexedDB key and throws DataError.
if (!(seeker instanceof Uint8Array) || seeker.byteLength === 0) {
logger
.forMethod('findMessageBySeeker')
.warn('skipping invalid seeker lookup', {
ownerUserId,
seekerLength:
seeker instanceof Uint8Array ? seeker.byteLength : 'not-uint8array',
});
const log = logger.forMethod('findMessageBySeeker');
// Malformed payloads can carry an empty seeker in reply/forward metadata.
// fake-indexeddb rejects that as an IndexedDB key and throws DataError.
if (!(seeker instanceof Uint8Array) || seeker.byteLength === 0) {
log.warn('skipping invalid seeker lookup', {
ownerUserId,
seekerLength:
seeker instanceof Uint8Array ? seeker.byteLength : 'not-uint8array',
});

Copilot uses AI. Check for mistakes.
Comment on lines +124 to +126
expect(() => deserializeMessage(truncated)).toThrow(
'Invalid reply message'
);
Copy link

Copilot AI Feb 14, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The error message expectation does not match the actual error message thrown by the assertAvailable function. The assertAvailable function throws a more detailed error message that includes the expected bytes, offset, and actual bytes received (e.g., "Invalid reply message: expected 34 bytes at offset X, got Y"). The test expects only "Invalid reply message" which will cause this test to fail. Consider updating the expectation to match the full error message format or use a partial string match.

Copilot uses AI. Check for mistakes.
Comment on lines +136 to +138
expect(() => deserializeMessage(truncated)).toThrow(
'Invalid forward message'
);
Copy link

Copilot AI Feb 14, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The error message expectation does not match the actual error message thrown by the assertAvailable function. The assertAvailable function throws a more detailed error message that includes the expected bytes, offset, and actual bytes received (e.g., "Invalid forward message: expected 34 bytes at offset X, got Y"). The test expects only "Invalid forward message" which will cause this test to fail. Consider updating the expectation to match the full error message format or use a partial string match.

Copilot uses AI. Check for mistakes.
Comment on lines +206 to +213
assertAvailable(buffer, offset, 4, 'reply');
const originalContentLen = Number(
U32.fromBytes(buffer.slice(offset, offset + 4))
);
offset += 4;

// Read original content
assertAvailable(buffer, offset, originalContentLen, 'reply');
Copy link

Copilot AI Feb 14, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The assertAvailable function validates that enough bytes are available for the content length itself, but it does not validate that the content length value is reasonable before attempting to validate the content bytes. If originalContentLen or forwardContentLen is an extremely large number (e.g., due to corruption), this could lead to an unhelpful error message or potential performance issues. Consider adding a sanity check on the length value (e.g., maximum message size) before using it to validate content availability.

Copilot uses AI. Check for mistakes.
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.

1 participant