-
Notifications
You must be signed in to change notification settings - Fork 3
ensure seeker is valid #382
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: dev
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -431,6 +431,19 @@ export class MessageService { | |
| seeker: Uint8Array, | ||
| ownerUserId: string | ||
| ): Promise<Message | undefined> { | ||
| // 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', | ||
| }); | ||
| return undefined; | ||
| } | ||
|
Comment on lines
+436
to
+445
|
||
|
|
||
| return await this.db.messages | ||
| .where('[ownerUserId+seeker]') | ||
| .equals([ownerUserId, seeker]) | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -17,6 +17,19 @@ export const MESSAGE_TYPE_KEEP_ALIVE = 0x03; | |
| // Seeker size: 1 byte length prefix + 32 bytes hash + 1 byte key index | ||
| const SEEKER_SIZE = 34; | ||
|
|
||
| function assertAvailable( | ||
| buffer: Uint8Array, | ||
| offset: number, | ||
| neededBytes: number, | ||
| context: string | ||
| ): void { | ||
| if (buffer.length < offset + neededBytes) { | ||
| throw new Error( | ||
| `Invalid ${context} message: expected ${neededBytes} bytes at offset ${offset}, got ${Math.max(buffer.length - offset, 0)}` | ||
| ); | ||
| } | ||
| } | ||
|
|
||
| export interface DeserializedMessage { | ||
| content: string; | ||
| replyTo?: { | ||
|
|
@@ -190,18 +203,21 @@ export function deserializeMessage(buffer: Uint8Array): DeserializedMessage { | |
| let offset = 1; | ||
|
|
||
| // Read original content length (4 bytes) | ||
| 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'); | ||
|
Comment on lines
+206
to
+213
|
||
| const originalContent = bytesToStr( | ||
| buffer.slice(offset, offset + originalContentLen) | ||
| ); | ||
| offset += originalContentLen; | ||
|
|
||
| // Read seeker (34 bytes) | ||
| assertAvailable(buffer, offset, SEEKER_SIZE, 'reply'); | ||
| const originalSeeker = buffer.slice(offset, offset + SEEKER_SIZE); | ||
| offset += SEEKER_SIZE; | ||
|
|
||
|
|
@@ -223,18 +239,21 @@ export function deserializeMessage(buffer: Uint8Array): DeserializedMessage { | |
| let offset = 1; | ||
|
|
||
| // Read forward content length (4 bytes) | ||
| 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'); | ||
|
Comment on lines
+242
to
+249
|
||
| const originalContent = bytesToStr( | ||
| buffer.slice(offset, offset + forwardContentLen) | ||
| ); | ||
| offset += forwardContentLen; | ||
|
|
||
| // Read seeker (34 bytes) | ||
| assertAvailable(buffer, offset, SEEKER_SIZE, 'forward'); | ||
| const originalSeeker = buffer.slice(offset, offset + SEEKER_SIZE); | ||
| offset += SEEKER_SIZE; | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -117,4 +117,24 @@ describe('Deserialization Failure Handling', () => { | |
| expect(true).toBe(true); | ||
| } | ||
| }); | ||
|
|
||
| it('throws on reply payloads with truncated seeker', () => { | ||
| const valid = serializeReplyMessage('new', 'old', serializationSeeker); | ||
| const truncated = valid.slice(0, valid.length - 10); | ||
| expect(() => deserializeMessage(truncated)).toThrow( | ||
| 'Invalid reply message' | ||
| ); | ||
|
Comment on lines
+124
to
+126
|
||
| }); | ||
|
|
||
| it('throws on forward payloads with truncated seeker', () => { | ||
| const valid = serializeForwardMessage( | ||
| 'forward', | ||
| 'note', | ||
| serializationSeeker | ||
| ); | ||
| const truncated = valid.slice(0, valid.length - 10); | ||
| expect(() => deserializeMessage(truncated)).toThrow( | ||
| 'Invalid forward message' | ||
| ); | ||
|
Comment on lines
+136
to
+138
|
||
| }); | ||
| }); | ||
There was a problem hiding this comment.
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 usinglog.warn(...). This is a minor stylistic preference based on the patterns seen elsewhere in this file.