fix(types): remove non-null assertions in groups.ts, noise-handler.ts, messages-recv.ts (partial)

- groups.ts: Replace ! assertions with ?? fallbacks for group attrs
- noise-handler.ts: Add proper validation for serverHello fields before use,
  remove all ! assertions (9 total), throw Boom errors for missing fields
- messages-recv.ts: Fix contradictory messageKey?.id! pattern (partial, more coming)

https://claude.ai/code/session_01E2cfX1N3sJgCJBTvzGazSG
This commit is contained in:
Claude
2026-02-08 23:41:19 +00:00
parent 92c51fa741
commit 8fd10c8b9b
3 changed files with 40 additions and 23 deletions
+6 -6
View File
@@ -316,19 +316,19 @@ export const extractGroupMetadata = (result: BinaryNode) => {
descId = descChild.attrs.id descId = descChild.attrs.id
} }
const groupId = group.attrs.id!.includes('@') ? group.attrs.id : jidEncode(group.attrs.id!, 'g.us') const groupId = (group.attrs.id ?? '').includes('@') ? group.attrs.id : jidEncode(group.attrs.id ?? '', 'g.us')
const eph = getBinaryNodeChild(group, 'ephemeral')?.attrs.expiration const eph = getBinaryNodeChild(group, 'ephemeral')?.attrs.expiration
const memberAddMode = getBinaryNodeChildString(group, 'member_add_mode') === 'all_member_add' const memberAddMode = getBinaryNodeChildString(group, 'member_add_mode') === 'all_member_add'
const metadata: GroupMetadata = { const metadata: GroupMetadata = {
id: groupId!, id: groupId,
notify: group.attrs.notify, notify: group.attrs.notify,
addressingMode: group.attrs.addressing_mode === 'lid' ? WAMessageAddressingMode.LID : WAMessageAddressingMode.PN, addressingMode: group.attrs.addressing_mode === 'lid' ? WAMessageAddressingMode.LID : WAMessageAddressingMode.PN,
subject: group.attrs.subject!, subject: group.attrs.subject ?? '',
subjectOwner: group.attrs.s_o, subjectOwner: group.attrs.s_o,
subjectOwnerPn: group.attrs.s_o_pn, subjectOwnerPn: group.attrs.s_o_pn,
subjectTime: +group.attrs.s_t!, subjectTime: +group.attrs.s_t ?? '0',
size: group.attrs.size ? +group.attrs.size : getBinaryNodeChildren(group, 'participant').length, size: group.attrs.size ? +group.attrs.size : getBinaryNodeChildren(group, 'participant').length,
creation: +group.attrs.creation!, creation: +group.attrs.creation ?? '0',
owner: group.attrs.creator ? jidNormalizedUser(group.attrs.creator) : undefined, owner: group.attrs.creator ? jidNormalizedUser(group.attrs.creator) : undefined,
ownerPn: group.attrs.creator_pn ? jidNormalizedUser(group.attrs.creator_pn) : undefined, ownerPn: group.attrs.creator_pn ? jidNormalizedUser(group.attrs.creator_pn) : undefined,
owner_country_code: group.attrs.creator_country_code, owner_country_code: group.attrs.creator_country_code,
@@ -347,7 +347,7 @@ export const extractGroupMetadata = (result: BinaryNode) => {
participants: getBinaryNodeChildren(group, 'participant').map(({ attrs }) => { participants: getBinaryNodeChildren(group, 'participant').map(({ attrs }) => {
// TODO: Store LID MAPPINGS // TODO: Store LID MAPPINGS
return { return {
id: attrs.jid!, id: attrs.jid ?? '',
phoneNumber: isLidUser(attrs.jid) && isPnUser(attrs.phone_number) ? attrs.phone_number : undefined, phoneNumber: isLidUser(attrs.jid) && isPnUser(attrs.phone_number) ? attrs.phone_number : undefined,
lid: isPnUser(attrs.jid) && isLidUser(attrs.lid) ? attrs.lid : undefined, lid: isPnUser(attrs.jid) && isLidUser(attrs.lid) ? attrs.lid : undefined,
admin: (attrs.type || null) as GroupParticipant['admin'] admin: (attrs.type || null) as GroupParticipant['admin']
+11 -10
View File
@@ -156,16 +156,16 @@ export const makeMessagesRecvSocket = (config: SocketConfig) => {
throw new Boom('Not authenticated') throw new Boom('Not authenticated')
} }
if (await placeholderResendCache.get(messageKey?.id!)) { if (await placeholderResendCache.get(messageKey?.id)) {
logger.debug({ messageKey }, 'already requested resend') logger.debug({ messageKey }, 'already requested resend')
return return
} else { } else {
await placeholderResendCache.set(messageKey?.id!, true) await placeholderResendCache.set(messageKey?.id, true)
} }
await delay(2000) await delay(2000)
if (!(await placeholderResendCache.get(messageKey?.id!))) { if (!(await placeholderResendCache.get(messageKey?.id))) {
logger.debug({ messageKey }, 'message received while resend requested') logger.debug({ messageKey }, 'message received while resend requested')
return 'RESOLVED' return 'RESOLVED'
} }
@@ -180,9 +180,9 @@ export const makeMessagesRecvSocket = (config: SocketConfig) => {
} }
setTimeout(async () => { setTimeout(async () => {
if (await placeholderResendCache.get(messageKey?.id!)) { if (await placeholderResendCache.get(messageKey?.id)) {
logger.debug({ messageKey }, 'PDO message without response after 8 seconds. Phone possibly offline') logger.debug({ messageKey }, 'PDO message without response after 8 seconds. Phone possibly offline')
await placeholderResendCache.del(messageKey?.id!) await placeholderResendCache.del(messageKey?.id)
} }
}, 8_000) }, 8_000)
@@ -233,7 +233,7 @@ export const makeMessagesRecvSocket = (config: SocketConfig) => {
if (update.jid && update.user) { if (update.jid && update.user) {
ev.emit('newsletter-participants.update', { ev.emit('newsletter-participants.update', {
id: update.jid, id: update.jid,
author: node.attrs.from!, author: node.attrs.from ?? '',
user: update.user, user: update.user,
new_role: 'ADMIN', new_role: 'ADMIN',
action: 'promote' action: 'promote'
@@ -251,9 +251,10 @@ export const makeMessagesRecvSocket = (config: SocketConfig) => {
// Handles newsletter notifications // Handles newsletter notifications
const handleNewsletterNotification = async (node: BinaryNode) => { const handleNewsletterNotification = async (node: BinaryNode) => {
const from = node.attrs.from! const from = node.attrs.from ?? ''
const child = getAllBinaryNodeChildren(node)[0]! const child = getAllBinaryNodeChildren(node)[0]
const author = node.attrs.participant! if (!child) return
const author = node.attrs.participant ?? ''
logger.info({ from, child }, 'got newsletter notification') logger.info({ from, child }, 'got newsletter notification')
@@ -261,7 +262,7 @@ export const makeMessagesRecvSocket = (config: SocketConfig) => {
case 'reaction': case 'reaction':
const reactionUpdate = { const reactionUpdate = {
id: from, id: from,
server_id: child.attrs.message_id!, server_id: child.attrs.message_id ?? '',
reaction: { reaction: {
code: getBinaryNodeChildString(child, 'reaction'), code: getBinaryNodeChildString(child, 'reaction'),
count: 1 count: 1
+23 -7
View File
@@ -150,7 +150,7 @@ export const makeNoiseHandler = ({
while (true) { while (true) {
if (inBytes.length < 3) return if (inBytes.length < 3) return
size = (inBytes[0]! << 16) | (inBytes[1]! << 8) | inBytes[2]! size = (inBytes[0] << 16) | (inBytes[1] << 8) | inBytes[2]
if (inBytes.length < size + 3) return if (inBytes.length < size + 3) return
@@ -180,13 +180,25 @@ export const makeNoiseHandler = ({
mixIntoKey, mixIntoKey,
finishInit, finishInit,
processHandshake: ({ serverHello }: proto.HandshakeMessage, noiseKey: KeyPair) => { processHandshake: ({ serverHello }: proto.HandshakeMessage, noiseKey: KeyPair) => {
authenticate(serverHello!.ephemeral!) if (!serverHello?.ephemeral) {
mixIntoKey(Curve.sharedKey(privateKey, serverHello!.ephemeral!)) throw new Boom('Missing server hello ephemeral', { statusCode: 500 })
}
const decStaticContent = decrypt(serverHello!.static!) if (!serverHello?.static) {
throw new Boom('Missing server hello static', { statusCode: 500 })
}
if (!serverHello?.payload) {
throw new Boom('Missing server hello payload', { statusCode: 500 })
}
authenticate(serverHello.ephemeral)
mixIntoKey(Curve.sharedKey(privateKey, serverHello.ephemeral))
const decStaticContent = decrypt(serverHello.static)
mixIntoKey(Curve.sharedKey(privateKey, decStaticContent)) mixIntoKey(Curve.sharedKey(privateKey, decStaticContent))
const certDecoded = decrypt(serverHello!.payload!) const certDecoded = decrypt(serverHello.payload)
const { intermediate: certIntermediate, leaf } = proto.CertChain.decode(certDecoded) const { intermediate: certIntermediate, leaf } = proto.CertChain.decode(certDecoded)
// leaf // leaf
@@ -202,7 +214,11 @@ export const makeNoiseHandler = ({
const { issuerSerial } = details const { issuerSerial } = details
const verify = Curve.verify(details.key!, leaf.details, leaf.signature) if (!details.key) {
throw new Boom('Missing certificate key', { statusCode: 500 })
}
const verify = Curve.verify(details.key, leaf.details, leaf.signature)
const verifyIntermediate = Curve.verify( const verifyIntermediate = Curve.verify(
WA_CERT_DETAILS.PUBLIC_KEY, WA_CERT_DETAILS.PUBLIC_KEY,
@@ -223,7 +239,7 @@ export const makeNoiseHandler = ({
} }
const keyEnc = encrypt(noiseKey.public) const keyEnc = encrypt(noiseKey.public)
mixIntoKey(Curve.sharedKey(noiseKey.private, serverHello!.ephemeral!)) mixIntoKey(Curve.sharedKey(noiseKey.private, serverHello.ephemeral))
return keyEnc return keyEnc
}, },