feat: decrypt secret-encrypted message edits - #128
Conversation
When a phone edits a message in a @lid-addressed chat, WhatsApp delivers the edit as secretEncryptedMessage (MESSAGE_EDIT), encrypted with the original message's "message secret" — which this whatsmeow session already holds. Today the encrypted event is forwarded to the webhook as-is, so consumers learn that an edit happened but not what changed. Decrypt it with whatsmeow's official primitive (Client.DecryptSecretEncryptedMessage, native EncSecretMessageEdit support) and rewrite the event into the canonical cleartext shape (protocolMessage MESSAGE_EDIT + editedMessage) — the same shape BuildEdit produces — so webhook consumers handle phone edits and app edits identically. Fail-open: if the secret is missing (message predates the pairing) the event passes through unchanged. Battle-tested in production since 2026-07-21 against real phone edits.
Reviewer's GuideDecrypts secret-encrypted message edits from phones and rewrites them into the canonical cleartext MESSAGE_EDIT shape before forwarding to webhooks, while failing open when decryption is not possible. Sequence diagram for decrypting secret-encrypted MESSAGE_EDIT before webhook forwardingsequenceDiagram
actor Phone
participant WhatsAppServer
participant MyClient
participant WAClient
participant WebhookConsumer
Phone->>WhatsAppServer: secretEncryptedMessage MESSAGE_EDIT
WhatsAppServer->>MyClient: events.Message (secretEncryptedMessage)
MyClient->>MyClient: myEventHandler(rawEvt)
MyClient->>MyClient: resolveSecretEncryptedEdit(evt)
MyClient->>WAClient: DecryptSecretEncryptedMessage(ctx, evt)
alt [decryption succeeds]
WAClient-->>MyClient: decrypted waE2E.Message
MyClient->>MyClient: rewrite evt.Message to protocolMessage MESSAGE_EDIT + editedMessage
else [decryption fails or unsupported]
WAClient-->>MyClient: error
MyClient->>MyClient: leave evt.Message as secretEncryptedMessage
end
MyClient->>WebhookConsumer: forward Message event (cleartext when available)
File-Level Changes
Possibly linked issues
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
There was a problem hiding this comment.
Hey - I've found 1 issue
Prompt for AI Agents
Please address the comments from this code review:
## Individual Comments
### Comment 1
<location path="pkg/whatsmeow/service/secret_edit.go" line_range="34-35" />
<code_context>
+// failure (missing secret, unsupported type, …) the event passes through
+// untouched — behavior falls back to what it was before this change (webhook
+// receives the encrypted payload).
+func (mycli *MyClient) resolveSecretEncryptedEdit(evt *events.Message) {
+ enc := evt.Message.GetSecretEncryptedMessage()
+ if enc == nil || enc.GetSecretEncType() != waE2E.SecretEncryptedMessage_MESSAGE_EDIT {
+ return
</code_context>
<issue_to_address>
**issue (bug_risk):** Guard against a nil targetMessageKey before logging its ID
Both log lines call enc.GetTargetMessageKey().GetID() without confirming targetMessageKey is non-nil. If a payload is missing targetMessageKey, this will panic while attempting to log. Please add a nil check and either skip logging the ID or use a safe placeholder to avoid crashes on malformed inputs.
</issue_to_address>Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
| func (mycli *MyClient) resolveSecretEncryptedEdit(evt *events.Message) { | ||
| enc := evt.Message.GetSecretEncryptedMessage() |
There was a problem hiding this comment.
issue (bug_risk): Guard against a nil targetMessageKey before logging its ID
Both log lines call enc.GetTargetMessageKey().GetID() without confirming targetMessageKey is non-nil. If a payload is missing targetMessageKey, this will panic while attempting to log. Please add a nil check and either skip logging the ID or use a safe placeholder to avoid crashes on malformed inputs.
|
Bom dia Flávio tudo bem?! Consegue criar as PR's suas apontando para a branch develop ao invés da main? pois o pessoal só analisam solicitações na develop... Vi que suas correções resolvem parte dos meus problemas rsrsrs. |
|
Bom dia, @iagocotta! Tudo certo, e obrigado pelo aviso — refiz tudo apontando para a As novas, todas com
Uma coisa que vale registrar, porque explica por que não deu para simplesmente trocar a base no botão do GitHub: Então cada PR virou um branch novo saído da
A #127 (RWMutex nos mapas compartilhados) eu deixei de fora, e queria a sua orientação. Ela protege acessos a Qualquer ajuste nas cinco, é só falar. |
When a phone edits a message in a
@lid-addressed chat, WhatsApp delivers the edit assecretEncryptedMessage(MESSAGE_EDIT): the new content is encrypted with the message secret of the original message — a secret the whatsmeow session already holds (it arrived inside the original message). Today the encrypted event is forwarded to the webhook as-is, so consumers learn that an edit happened but not what changed.This decrypts it with whatsmeow's official primitive (
Client.DecryptSecretEncryptedMessage, nativeEncSecretMessageEditsupport) and rewrites the event into the canonical cleartext shape (protocolMessageMESSAGE_EDIT +editedMessage) — the same shapeBuildEditproduces — so webhook consumers handle phone edits and app edits identically.Fail-open by design: if the secret is missing (message predates the pairing) the event passes through unchanged. Self-contained file plus a one-line call-site to keep review/rebase easy. Battle-tested in our production since 2026-07-21.
Summary by Sourcery
Decrypt secret-encrypted message edit events and normalize them into the standard cleartext edit shape before forwarding to webhooks.
New Features:
Enhancements: