Skip to content

fix(group-translate): prototype-safe lookups, per-chat serialization, strict translate response (v1.0.3)#3

Merged
rmyndharis merged 2 commits into
mainfrom
group-translate-harden
Jun 23, 2026
Merged

fix(group-translate): prototype-safe lookups, per-chat serialization, strict translate response (v1.0.3)#3
rmyndharis merged 2 commits into
mainfrom
group-translate-harden

Conversation

@rmyndharis

Copy link
Copy Markdown
Owner

Summary

Three robustness fixes to group-translate, released as v1.0.3.

Prototype-safe participant lookups

ensureParticipant indexed state.participants[wid] directly. A crafted author/target id of __proto__ resolved to Object.prototype (truthy), and the subsequent pushName/enabled write landed on the global prototype — prototype pollution. Lookups now reject __proto__/constructor/prototype (a real WhatsApp id never equals these) and use Object.prototype.hasOwnProperty.call(...) for existence.

Per-(session, chat) serialization

Concurrent messages for the same group each ran load → mutate → save independently, so the second save clobbered the first (lost participant-language updates) and two first-messages could both send the help announcement. handleMessage now chains per ${sessionId}:${chatId} through a promise lock with a settled tail (no chain poisoning) and self-eviction when the chain drains.

Strict translate response

LibreTranslateClient.translate returned data.translatedText from a type-asserted body with no runtime check; a partial/empty response became the literal string undefined in the group. It now throws when translatedText isn't a string — the call is counted by the circuit breaker and excluded from the combined reply, mirroring the existing /detect guard.

Tests

  • New: a __proto__ sender wid does not pollute Object.prototype.
  • New: concurrent first messages announce exactly once.
  • New: a /translate response lacking translatedText rejects.
  • Full suite green (97 tests), tsc --noEmit clean, bundle packages cleanly.

…rialization, strict translate response (v1.0.3)

- ensureParticipant rejects prototype keys (__proto__/constructor/prototype) and uses
  hasOwnProperty, so a crafted participant/target id can't read or write Object.prototype.
- handleMessage is serialized per (session, chat) via a self-evicting promise chain,
  closing a load->mutate->save race that duplicated the help announcement and dropped
  participant-language updates.
- LibreTranslateClient.translate validates translatedText is a string; a partial/empty
  response now fails (circuit-counted, excluded from the reply) instead of posting 'undefined'.
@rmyndharis rmyndharis merged commit 94cdac3 into main Jun 23, 2026
@rmyndharis rmyndharis deleted the group-translate-harden branch June 23, 2026 09:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant