fix(utils): normalizeE164 returns "" when there are no digits#95974
fix(utils): normalizeE164 returns "" when there are no digits#95974he-yufeng wants to merge 1 commit into
Conversation
A From with no digits — a blocked caller the carrier reports as "Anonymous", or a bare "sms://" scheme — normalized to a stray "+", which is not a valid E.164. Since "+" is truthy, deriveSessionKey's `from || "unknown"` fallback never fired and those messages landed in a junk "+" session bucket instead of "unknown". Return "" when there is nothing to normalize so callers fall back as intended; valid numbers are unaffected.
|
Codex review: needs maintainer review before merge. Reviewed June 24, 2026, 1:32 PM ET / 17:32 UTC. Summary PR surface: Source +4, Tests +20. Total +24 across 2 files. Reproducibility: yes. Source inspection on current main shows no-digit Review metrics: 1 noteworthy metric.
Merge readiness Overall follows the weaker of proof and patch quality, so missing proof can cap an otherwise strong patch. Rank-up moves:
Risk before merge
Maintainer options:
Next step before merge
Security Review detailsBest possible solution: Land the shared normalizer fix after maintainer sign-off that empty string is the SDK invalid-input contract; otherwise preserve the helper behavior and guard Do we have a high-confidence way to reproduce the issue? Yes. Source inspection on current main shows no-digit Is this the best way to solve the issue? Yes, if maintainers accept the SDK contract correction: the shared normalizer should not manufacture an invalid E.164 value. If SDK compatibility is prioritized, the narrow alternative is a local no-digit guard in AGENTS.md: found and applied where relevant. Codex review notes: model internal, reasoning high; reviewed against f57a30289d32. Label changesLabel justifications:
Evidence reviewedPR surface: Source +4, Tests +20. Total +24 across 2 files. View PR surface stats
What I checked:
Likely related people:
What the crustacean ranks mean
Shiny media proof means a screenshot, video, or linked artifact directly shows the changed behavior. Runtime, network, CSP, and security claims still need visible diagnostics. How this review workflow works
|
|
Added inspectable before/after terminal output (real post-fix |
|
Superseded by #100467, landed in That canonical fix includes this PR's digit-free normalization behavior, plus stray-plus canonicalization, Signal invalid-input/UUID handling, iMessage non-phone handle preservation, and the corresponding regression coverage. Its exact-head hosted CI and maintainer review passed before merge. Thanks @he-yufeng for identifying and fixing the no-digit case. |
What Problem This Solves
An inbound
Fromthat contains no digits normalizes to a stray"+". Two real cases hit this:Anonymoussms://with no number attached"+"is not a valid E.164 number, and because it is truthy,deriveSessionKey'sfrom || "unknown"fallback never fires. Those messages get keyed to a junk"+"session bucket instead of the intended"unknown", so anything that special-cases the unknown sender silently skips them.Why This Change Was Made
normalizeE164only ever returned`+${digits}`, so emptydigitsproduced a bare"+". The fix returns""when there is nothing to normalize — which is exactly the value the internal caller already treats as "fall back to unknown". Valid numbers go through the same path as before.User Impact
Anonymous / unidentified senders now collapse to the intended
"unknown"session bucket instead of a bogus"+"one, so unknown-sender handling and session bookkeeping behave as designed. No change for normal phone numbers.Evidence
The full vitest suite won't start on my Windows checkout (the
@rolldownnative binding is missing — npm optional-dependency bug), so I ran a small script that imports the real post-fixnormalizeE164fromsrc/utils.ts, puts it next to main's verbatim pre-fix implementation, and feeds both through the verbatim direct-chat branch ofderiveSessionKey(const from = ctx.From ? normalizeE164(ctx.From) : ""; return from || "unknown"), using the repo's bundledtsx:So for a no-digit sender like
Anonymous/sms:///+, the session key moves from the bogus"+"bucket to"unknown", while a real number is untouched. (An emptyFromalready short-circuited to"unknown"because the ternary never callsnormalizeE164; the regression was specifically the non-empty no-digit inputs.)Added a
normalizeE164unit test insrc/utils.test.tscovering both valid numbers and the no-digit cases; it fails on the old behavior and passes with this change (runs in CI).Note for maintainers
normalizeE164is re-exported through the plugin SDK, so this is an intentional contract change for invalid no-digit input: plugin callers that previously saw"+"will now see"". That seems correct since"+"was never a valid E.164 value, but happy to gate it behind a guard at thederiveSessionKeycall site instead if you'd rather not change the exported helper's behavior.