parseContact: Be resilient to invalid phone numbers

Co-authored-by: Scott Nonnenberg <scott@signal.org>
This commit is contained in:
automated-signal 2025-01-22 18:43:06 -06:00 committed by GitHub
parent f0d112fa1b
commit 5abf21789c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 52 additions and 20 deletions

View file

@ -88,7 +88,7 @@ describe('timestamp', () => {
}); });
it('formats month name, day of month, year, and time for other times', () => { it('formats month name, day of month, year, and time for other times', () => {
const rx = /Apr 20, 2000, \d+:\d+ [A|P]M/; const rx = /Apr (19|20|21), 2000, \d+:\d+ [A|P]M/;
const datetime = formatDateTimeLong(i18n, new Date(956216013000)); const datetime = formatDateTimeLong(i18n, new Date(956216013000));
assert.isTrue(rx.test(datetime)); assert.isTrue(rx.test(datetime));
}); });

View file

@ -10,9 +10,11 @@ import type { MessageAttributesType } from '../../model-types.d';
import type { Avatar, Email, Phone } from '../../types/EmbeddedContact'; import type { Avatar, Email, Phone } from '../../types/EmbeddedContact';
import { import {
_validate, _validate,
ContactFormType,
embeddedContactSelector, embeddedContactSelector,
getName, getName,
parseAndWriteAvatar, parseAndWriteAvatar,
parsePhoneItem,
} from '../../types/EmbeddedContact'; } from '../../types/EmbeddedContact';
import { fakeAttachment } from '../../test-both/helpers/fakeAttachment'; import { fakeAttachment } from '../../test-both/helpers/fakeAttachment';
import { generateAci } from '../../types/ServiceId'; import { generateAci } from '../../types/ServiceId';
@ -632,6 +634,48 @@ describe('Contact', () => {
}); });
}); });
describe('parsePhoneItem', () => {
it('adds default phone type', () => {
const phone: Phone = {
value: '+18005550000',
// @ts-expect-error Forcing an invalid value here
type: null,
};
const expected = {
value: '+18005550000',
type: ContactFormType.HOME,
};
const actual = parsePhoneItem(phone, { regionCode: '805' });
assert.deepEqual(actual, expected);
});
it('passes invalid phone numbers through', () => {
const phone: Phone = {
value: '+1800555u000',
type: ContactFormType.WORK,
};
const expected = {
value: '+1800555u000',
type: ContactFormType.WORK,
};
const actual = parsePhoneItem(phone, { regionCode: '805' });
assert.deepEqual(actual, expected);
});
it('returns original data if regionCode not provided', () => {
const phone: Phone = {
value: '+18005550000',
type: ContactFormType.MOBILE,
};
const expected = {
value: '+18005550000',
type: ContactFormType.MOBILE,
};
const actual = parsePhoneItem(phone, { regionCode: undefined });
assert.deepEqual(actual, expected);
});
});
describe('_validate', () => { describe('_validate', () => {
it('returns error if contact has no name.displayName or organization', () => { it('returns error if contact has no name.displayName or organization', () => {
const messageId = 'the-message-id'; const messageId = 'the-message-id';

View file

@ -10,7 +10,7 @@ import type { ReadonlyMessageAttributesType } from '../model-types.d';
import { isNotNil } from '../util/isNotNil'; import { isNotNil } from '../util/isNotNil';
import { import {
format as formatPhoneNumber, format as formatPhoneNumber,
parse as parsePhoneNumber, normalize as normalizePhoneNumber,
} from './PhoneNumber'; } from './PhoneNumber';
import type { import type {
AttachmentType, AttachmentType,
@ -317,7 +317,7 @@ export function _validate(
return undefined; return undefined;
} }
function parsePhoneItem( export function parsePhoneItem(
item: Phone, item: Phone,
{ regionCode }: { regionCode: string | undefined } { regionCode }: { regionCode: string | undefined }
): Phone | undefined { ): Phone | undefined {
@ -325,10 +325,14 @@ function parsePhoneItem(
return undefined; return undefined;
} }
const value = regionCode
? normalizePhoneNumber(item.value, { regionCode })
: item.value;
return { return {
...item, ...item,
type: item.type || DEFAULT_PHONE_TYPE, type: item.type || DEFAULT_PHONE_TYPE,
value: parsePhoneNumber(item.value, { regionCode }), value: value ?? item.value,
}; };
} }

View file

@ -71,22 +71,6 @@ export const format = memoizee(_format, {
max: 5000, max: 5000,
}); });
export function parse(
phoneNumber: string,
options: {
regionCode: string | undefined;
}
): string {
const { regionCode } = options;
const parsedNumber = instance.parse(phoneNumber, regionCode);
if (instance.isValidNumber(parsedNumber)) {
return instance.format(parsedNumber, PhoneNumberFormat.E164);
}
return phoneNumber;
}
export function normalize( export function normalize(
phoneNumber: string, phoneNumber: string,
options: { regionCode: string } options: { regionCode: string }