← Back to PRs

#22335: fix: harden normalizeE164 against empty/invalid inputs returning bare '+'

by CZH-THU open 2026-02-21 01:59 View on GitHub →
size: S
## Summary - Problem: `normalizeE164` returns a bare `"+"` for invalid inputs (empty string, whitespace, non-digit strings, bare `whatsapp:` prefix), which is not a valid E.164 number. - Why it matters: Downstream functions silently treat `"+"` as valid, causing incorrect matches in `isSelfChatMode` (two invalid numbers both normalize to `"+"` and match), invalid JIDs from `toWhatsappJid` (`"@s.whatsapp.net"`), and bypassed guards in group gating (`if (!sender)` fails because `"+"` is truthy). - What changed: `normalizeE164` now returns an empty string `""` when no digits are present; `isSelfChatMode` guards against empty normalized results; `toWhatsappJid` falls back to the raw input when normalization yields empty. Added 12 edge case tests. - What did NOT change (scope boundary): Return type stays `string` (not `string | null`); all existing callers remain compatible. ## Change Type (select all) - [x] Bug fix ## Scope (select all touched areas) - [x] Integrations ## Linked Issue/PR - Related: WhatsApp/Signal phone number normalization ## User-visible / Behavior Changes - Invalid phone number inputs no longer silently produce `"+"` — they now produce `""` which is correctly handled as invalid by downstream logic. - `isSelfChatMode` no longer incorrectly returns `true` when both self and allowFrom entries have no digits. ## Security Impact (required) - New permissions/capabilities? No - Secrets/tokens handling changed? No - New/changed network calls? No - Command/tool execution surface changed? No - Data access scope changed? No ## Repro + Verification ### Environment - OS: Windows 10 - Runtime/container: Node 22+ - Integration/channel: WhatsApp, Signal ### Steps 1. Call `normalizeE164("")` — previously returned `"+"`, now returns `""` 2. Call `isSelfChatMode("abc", ["def"])` — previously returned `true`, now returns `false` 3. Call `toWhatsappJid("")` — previously returned `"@s.whatsapp.net"`, now returns `""` ### Expected - Empty/invalid inputs should not produce `"+"` ### Actual (before fix) - `normalizeE164("")` returned `"+"` ## Evidence - [x] Failing test/log before + passing after: 38 tests pass, including 12 new edge case tests ## Human Verification (required) - Verified scenarios: All 38 unit tests pass; also ran WhatsApp normalize tests (8 pass) - Edge cases checked: empty string, whitespace, non-digit strings, bare `"+"`, bare `"whatsapp:"`, valid numbers with/without prefix - What you did **not** verify: Live WhatsApp/Signal integration test ## Compatibility / Migration - Backward compatible? Yes - Config/env changes? No - Migration needed? No ## Failure Recovery (if this breaks) - How to disable/revert this change quickly: Revert the single commit - Files/config to restore: `src/utils.ts` - Known bad symptoms: Phone normalization returning empty string where `"+"` was expected ## Risks and Mitigations - Risk: Callers that compared `normalizeE164(x) === "+"` would break — but no such callers exist (verified via grep). - Mitigation: Searched all 22 call sites; all handle empty string correctly. ## AI-assisted PR - [x] Mark as AI-assisted - Testing: Fully tested (38 unit tests pass) - Confirmed understanding of all code changes <!-- greptile_comment --> <h3>Greptile Summary</h3> Hardens `normalizeE164` against empty/invalid inputs that previously returned a bare `"+"`, which is not a valid E.164 number. Now returns `""` for invalid inputs, with corresponding guards added in `isSelfChatMode` and `toWhatsappJid`. - **`normalizeE164`**: Extracts pure digits before prepending `+`; returns `""` when no digits are present instead of `"+"`. - **`isSelfChatMode`**: Guards against empty normalized self-number to prevent false `true` when two invalid inputs both normalize to the same value. - **`toWhatsappJid`**: Falls back to the raw input when normalization yields empty, avoiding invalid JIDs like `"@s.whatsapp.net"`. - Adds 12 new edge case tests covering empty strings, whitespace, non-digit inputs, bare prefixes, and valid number normalization. - The changes are backward-compatible — return type stays `string`, and callers that already handle empty strings or use `.filter(Boolean)` are unaffected. <h3>Confidence Score: 5/5</h3> - This PR is safe to merge — it fixes a clear bug with well-tested, minimal, backward-compatible changes. - The changes are narrowly scoped to three functions, all with correct logic. The fix addresses a real bug where invalid inputs produced a bare "+" that downstream code treated as valid. The new behavior (returning empty string) is properly guarded in all three modified functions. 12 new tests cover the edge cases thoroughly. I examined 22+ call sites across the codebase and found no callers that would break from this change — callers already use `.filter(Boolean)`, length checks, or truthy guards. The redundant `normalizedN !== ""` check in `isSelfChatMode` is defensive but harmless. - No files require special attention <sub>Last reviewed commit: 9594192</sub> <!-- greptile_other_comments_section --> <sub>(2/5) Greptile learns from your feedback when you react with thumbs up/down!</sub> <!-- /greptile_comment -->

Most Similar PRs