Skip to content

whatsapp: vCard contact import lost strict E.164 enforcement after shared parser extraction #320

@wesm

Description

@wesm

Follow-up to #303.

internal/whatsapp/contacts.go used to call a private normalizeVCardPhone that rejected bare numbers without an explicit + or 00 country-code prefix:

// Without an explicit country code indicator (+ or 00), we cannot
// reliably determine the country code. Skip ambiguous numbers rather
// than guessing — a wrong prefix would match the wrong participant.
return ""

Commit 5eb7762 extracted the parser into internal/vcard, and 014d39d switched its phone normalization to textimport.NormalizePhone, which defaults bare 10-digit numbers to +1. So a vCard TEL:2025551234 now resolves to +12025551234 and matches WhatsApp participants at that E.164 number.

That trade-off is justified for iMessage (both sides of the comparison run the same defaulting, so keys round-trip), but WhatsApp stores E.164 directly from JIDs — there's no symmetric defaulting on the participant side. Net effect:

  • US users with bare 10-digit numbers in Contacts.app: matching improves.
  • Non-US users with bare local 10-digit numbers that coincidentally collide with real US numbers on their WhatsApp: small risk of cross-attribution where the old code would have skipped the row.

UK-style 07738006043 is unaffected — textimport.NormalizePhone doesn't strip the leading 0, so the result (+07738006043) never matches any real WhatsApp ID.

Suggested fix

Let internal/vcard accept a normalization strategy (parameter on ParseFile, or a second strict entrypoint). WhatsApp opts into strict mode (+ or 00 required); iMessage keeps the current textimport.NormalizePhone defaulting for parity.

References

  • internal/whatsapp/contacts.go:14 (call site)
  • internal/vcard/vcard.go:186 (normalizePhone)
  • internal/textimport/phone.go:50-57 (US defaulting branch)
  • Pre-5eb7762 internal/whatsapp/contacts.go normalizeVCardPhone for the prior strict rule.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions