diff --git a/src/Signal/libsignal.ts b/src/Signal/libsignal.ts index 90455240875..52f13b2daab 100644 --- a/src/Signal/libsignal.ts +++ b/src/Signal/libsignal.ts @@ -305,7 +305,17 @@ function readVarint(buffer: Uint8Array, offset: number): { value: number; nextOf } shift += 7 - if (shift > 35) { + // Reject once shift would reach 35 — NOT just exceed it. The 5 + // valid byte shifts are 0, 7, 14, 21, 28. After processing byte 4 + // (`shift` was 28), this increment makes `shift = 35`. With a + // `shift > 35` guard, byte 5 would still be read on the next loop + // iteration and `(byte & 0x7f) << 35` would silently fold into + // `<< 3` because JS `<<` is modulo-32 on the right operand — + // producing a corrupt value that the outer `>>> 0` would happily + // accept as a valid uint32, neutralising the + // `extractIdentityFromPkmsg` field-3 (identityKey) parse on + // crafted pkmsg payloads. + if (shift >= 35) { // Varint too long (max 5 bytes for 32-bit) // This could indicate a malformed or malicious message // Caller should log this condition at debug level diff --git a/src/Utils/multi-db-sqlite/schemas/msgstore.ts b/src/Utils/multi-db-sqlite/schemas/msgstore.ts index 62262210319..a0b1c5186a1 100644 --- a/src/Utils/multi-db-sqlite/schemas/msgstore.ts +++ b/src/Utils/multi-db-sqlite/schemas/msgstore.ts @@ -27,7 +27,19 @@ CREATE TABLE IF NOT EXISTS jid ( agent INTEGER, device INTEGER, type INTEGER, - raw_string TEXT + /* NOT NULL prevents two distinct failure modes — + (1) SQLite treats NULL as DISTINCT inside a UNIQUE index, so any + malformed insert path that produced NULL would silently create + duplicate rows that the jid_raw_string_idx was supposed to + prevent; + (2) selectJidIdByRaw filters by WHERE raw_string = ?, and SQL + NULL = NULL evaluates to UNKNOWN, so the row is never returned + — rowIdFor would then throw "failed to materialize jid row" + for every subsequent access. + CREATE TABLE IF NOT EXISTS is a no-op when the table exists, so + legacy databases keep the nullable column; new databases get the + constraint enforced. */ + raw_string TEXT NOT NULL ); CREATE UNIQUE INDEX IF NOT EXISTS jid_raw_string_idx ON jid (raw_string); @@ -36,7 +48,14 @@ CREATE INDEX IF NOT EXISTS jid_user_server_idx ON jid (user, server); CREATE TABLE IF NOT EXISTS jid_map ( lid_row_id INTEGER PRIMARY KEY NOT NULL, jid_row_id INTEGER NOT NULL, - sort_id INTEGER + /* NOT NULL DEFAULT 0 so a row inserted without an explicit sort_id + can never end up as NULL. ORDER BY sort_id DESC ranks NULLs LAST + in SQLite which would silently demote a freshly-inserted-but-NULL + row below older ones — the opposite of the "last write wins" + intent. upsertMap always provides a real epoch-ms tick so the + default is just defensive against any future code path that + bypasses upsertMap. */ + sort_id INTEGER NOT NULL DEFAULT 0 ); CREATE INDEX IF NOT EXISTS jid_map_jid_row_id_idx ON jid_map (jid_row_id); diff --git a/src/Voip/signaling/bridge.ts b/src/Voip/signaling/bridge.ts index ec09dc43dbf..758ea8be2fc 100644 --- a/src/Voip/signaling/bridge.ts +++ b/src/Voip/signaling/bridge.ts @@ -33,7 +33,7 @@ const ACK_TIMEOUT_MS = 15_000 // version lazy-loaded `@whiskeysockets/baileys` as a peer dep. Inside the fork // we ship as part of the same package, so static imports are cleaner and avoid // the runtime `import()` ceremony. -import { proto } from '../../../WAProto/index' +import { proto } from '../../../WAProto/index.js' import { encodeWAMessage, unpadRandomMax16 } from '../../Utils/generics' import { parseAndInjectE2ESessions } from '../../Utils/signal' import { encodeSignedDeviceIdentity } from '../../Utils/validate-connection'