Issue Priority
Medium
Issue Description
The message event fires for messages of type revoked, which should never happen. Revoked messages are supposed to be handled by message_revoke_everyone only.
This is a race condition triggered when:
- A message arrives as
ciphertext (Signal session decryption is still pending)
- Before the ciphertext resolves, the sender deletes the message for everyone
The ciphertext deferral logic in Client.js registers a once('change:type') listener on the model and emits message when the type changes. The problem is that WhatsApp's internal revoke handler also triggers change:type on the same model (it calls model.set({ type: 'revoked', ... }) in place, on the existing ciphertext model in the collection). The listener fires, doesn't check the resolved type, and emits message with a revoked message.
Steps To Reproduce
- Listen on the
message event
- Have another account send a message to your client's number
- Quickly (within ~5 seconds, before ciphertext resolves) have the sender delete the message for everyone
- The
message event fires with msg.type === 'revoked'
This is easier to reproduce on the first message between two accounts, or right after a Signal session reset, since decryption takes longer in those cases.
Code Sample
client.on('message', (msg) => {
// unexpectedly fires with msg.type === 'revoked'
console.log(msg.type);
});
Root Cause
In Client.js (around line 1052), the ciphertext deferral callback passes the resolved message directly to onAddMessageEvent without checking whether the type became revoked:
msg.once('change:type', (_msg) =>
window.onAddMessageEvent(
window.WWebJS.getMessageModel(_msg),
),
);
Proposed Fix
Add a check in the callback:
msg.once('change:type', (_msg) => {
if (_msg.type === 'revoked') return;
window.onAddMessageEvent(
window.WWebJS.getMessageModel(_msg),
);
});
Checklist
WhatsApp Account Type: Standard
Authentication Strategy: LocalAuth
whatsapp-web.js Version: 1.34.6
WhatsApp Web Version: 2.3000.1021202220
Browser Type: Chromium (Puppeteer)
Browser Version: 132.0.6834.83
Computer OS Version: Ubuntu 22.04
Phone OS Version: Android 14
Node.js Version: 22.14.0
Issue Priority
Medium
Issue Description
The
messageevent fires for messages of typerevoked, which should never happen. Revoked messages are supposed to be handled bymessage_revoke_everyoneonly.This is a race condition triggered when:
ciphertext(Signal session decryption is still pending)The ciphertext deferral logic in
Client.jsregisters aonce('change:type')listener on the model and emitsmessagewhen the type changes. The problem is that WhatsApp's internal revoke handler also triggerschange:typeon the same model (it callsmodel.set({ type: 'revoked', ... })in place, on the existing ciphertext model in the collection). The listener fires, doesn't check the resolved type, and emitsmessagewith a revoked message.Steps To Reproduce
messageeventmessageevent fires withmsg.type === 'revoked'This is easier to reproduce on the first message between two accounts, or right after a Signal session reset, since decryption takes longer in those cases.
Code Sample
Root Cause
In
Client.js(around line 1052), the ciphertext deferral callback passes the resolved message directly toonAddMessageEventwithout checking whether the type becamerevoked:Proposed Fix
Add a check in the callback:
Checklist
WhatsApp Account Type: Standard
Authentication Strategy: LocalAuth
whatsapp-web.js Version: 1.34.6
WhatsApp Web Version: 2.3000.1021202220
Browser Type: Chromium (Puppeteer)
Browser Version: 132.0.6834.83
Computer OS Version: Ubuntu 22.04
Phone OS Version: Android 14
Node.js Version: 22.14.0