From 0fa069f260faef77a9e436c1b4228eed390ac80c Mon Sep 17 00:00:00 2001 From: Josh Perez <60019601+josh-signal@users.noreply.github.com> Date: Wed, 26 Jan 2022 16:58:00 -0500 Subject: [PATCH] Trim profile names when setting them --- ts/components/ProfileEditor.tsx | 17 +++++++++--- ts/services/writeProfile.ts | 11 ++++++-- ts/util/whitespaceStringUtil.ts | 47 +++++++++++++++++++++++++++++++++ 3 files changed, 70 insertions(+), 5 deletions(-) create mode 100644 ts/util/whitespaceStringUtil.ts diff --git a/ts/components/ProfileEditor.tsx b/ts/components/ProfileEditor.tsx index 0e3c4302c..49becbda0 100644 --- a/ts/components/ProfileEditor.tsx +++ b/ts/components/ProfileEditor.tsx @@ -1,4 +1,4 @@ -// Copyright 2021 Signal Messenger, LLC +// Copyright 2021-2022 Signal Messenger, LLC // SPDX-License-Identifier: AGPL-3.0-only import React, { useCallback, useEffect, useRef, useState } from 'react'; @@ -37,6 +37,7 @@ import { import { Spinner } from './Spinner'; import { UsernameSaveState } from '../state/ducks/conversationsEnums'; import { MAX_USERNAME, MIN_USERNAME } from '../types/Username'; +import { isWhitespace, trim } from '../util/whitespaceStringUtil'; export enum EditState { None = 'None', @@ -286,7 +287,16 @@ export const ProfileEditor = ({ (avatar: Uint8Array | undefined) => { setAvatarBuffer(avatar); setEditState(EditState.None); - onProfileChanged(stagedProfile, avatar); + onProfileChanged( + { + ...stagedProfile, + firstName: trim(stagedProfile.firstName), + familyName: stagedProfile.familyName + ? trim(stagedProfile.familyName) + : undefined, + }, + avatar + ); }, [onProfileChanged, stagedProfile] ); @@ -422,7 +432,8 @@ export const ProfileEditor = ({ const shouldDisableSave = !stagedProfile.firstName || (stagedProfile.firstName === fullName.firstName && - stagedProfile.familyName === fullName.familyName); + stagedProfile.familyName === fullName.familyName) || + isWhitespace(stagedProfile.firstName); content = ( <> diff --git a/ts/services/writeProfile.ts b/ts/services/writeProfile.ts index aebe094c3..5f85b96f4 100644 --- a/ts/services/writeProfile.ts +++ b/ts/services/writeProfile.ts @@ -3,12 +3,14 @@ import dataInterface from '../sql/Client'; import type { ConversationType } from '../state/ducks/conversations'; +import * as Errors from '../types/errors'; +import * as log from '../logging/log'; import { computeHash } from '../Crypto'; import { encryptProfileData } from '../util/encryptProfileData'; import { getProfile } from '../util/getProfile'; import { singleProtoJobQueue } from '../jobs/singleProtoJobQueue'; -import * as Errors from '../types/errors'; -import * as log from '../logging/log'; +import { strictAssert } from '../util/assert'; +import { isWhitespace } from '../util/whitespaceStringUtil'; export async function writeProfile( conversation: ConversationType, @@ -32,6 +34,11 @@ export async function writeProfile( firstName, } = conversation; + strictAssert( + !isWhitespace(String(conversation.firstName)), + 'writeProfile: Cannot set an empty profile name' + ); + const [profileData, encryptedAvatarData] = await encryptProfileData( conversation, avatarBuffer diff --git a/ts/util/whitespaceStringUtil.ts b/ts/util/whitespaceStringUtil.ts new file mode 100644 index 000000000..19e0037f0 --- /dev/null +++ b/ts/util/whitespaceStringUtil.ts @@ -0,0 +1,47 @@ +// Copyright 2022 Signal Messenger, LLC +// SPDX-License-Identifier: AGPL-3.0-only + +const WHITESPACE = new Set([ + ' ', + '\u200E', // left-to-right mark + '\u200F', // right-to-left mark + '\u2007', // figure space + '\u200B', // zero-width space + '\u2800', // braille blank +]); + +export function trim(str: string): string { + let start = 0; + let end = str.length - 1; + + for (start; start < str.length; start += 1) { + const char = str[start]; + if (!WHITESPACE.has(char)) { + break; + } + } + + for (end; end > 0; end -= 1) { + const char = str[end]; + if (!WHITESPACE.has(char)) { + break; + } + } + + if (start > 0 || end < str.length - 1) { + return str.substring(start, end + 1); + } + + return str; +} + +export function isWhitespace(str: string): boolean { + for (let i = 0; i < str.length; i += 1) { + const char = str[i]; + if (!WHITESPACE.has(char)) { + return false; + } + } + + return true; +}