From fc12d02a8d08e515f1d7f0a9a51758799e733d8d Mon Sep 17 00:00:00 2001 From: Scott Nonnenberg Date: Thu, 6 May 2021 12:06:20 -0700 Subject: [PATCH] Clean up inviteLink handling, harden compose input --- main.js | 6 ++++++ preload.js | 3 +++ ts/components/CompositionArea.stories.tsx | 1 + ts/components/CompositionArea.tsx | 3 +++ ts/components/CompositionInput.stories.tsx | 1 + ts/components/CompositionInput.tsx | 20 +++++++++++++++++++ ts/components/ForwardMessageModal.stories.tsx | 1 + ts/components/ForwardMessageModal.tsx | 3 +++ ts/state/smart/ForwardMessageModal.tsx | 3 +++ ts/textsecure/WebAPI.ts | 14 ++++++++++--- ts/views/conversation_view.ts | 6 ++++++ ts/window.d.ts | 1 + 12 files changed, 59 insertions(+), 3 deletions(-) diff --git a/main.js b/main.js index 737f3e38b..2a87a9d9a 100644 --- a/main.js +++ b/main.js @@ -593,6 +593,12 @@ ipc.on('show-window', () => { showWindow(); }); +ipc.on('set-secure-input', (_sender, enabled) => { + if (app.setSecureKeyboardEntryEnabled) { + app.setSecureKeyboardEntryEnabled(enabled); + } +}); + ipc.on('title-bar-double-click', () => { if (!mainWindow) { return; diff --git a/preload.js b/preload.js index 878e581b3..97be98183 100644 --- a/preload.js +++ b/preload.js @@ -138,6 +138,9 @@ try { window.log.info('show window'); ipc.send('show-window'); }; + window.setSecureInput = enabled => { + ipc.send('set-secure-input', enabled); + }; window.titleBarDoubleClick = () => { ipc.send('title-bar-double-click'); diff --git a/ts/components/CompositionArea.stories.tsx b/ts/components/CompositionArea.stories.tsx index da16ca29c..b70abcdc8 100644 --- a/ts/components/CompositionArea.stories.tsx +++ b/ts/components/CompositionArea.stories.tsx @@ -40,6 +40,7 @@ const createProps = (overrideProps: Partial = {}): Props => ({ clearQuotedMessage: action('clearQuotedMessage'), getQuotedMessage: action('getQuotedMessage'), sortedGroupMembers: [], + setSecureInput: action('setSecureInput'), // EmojiButton onPickEmoji: action('onPickEmoji'), onSetSkinTone: action('onSetSkinTone'), diff --git a/ts/components/CompositionArea.tsx b/ts/components/CompositionArea.tsx index f1927a0b5..dcb3cda86 100644 --- a/ts/components/CompositionArea.tsx +++ b/ts/components/CompositionArea.tsx @@ -67,6 +67,7 @@ export type Props = Pick< | 'draftBodyRanges' | 'clearQuotedMessage' | 'getQuotedMessage' + | 'setSecureInput' > & Pick< EmojiButtonProps, @@ -113,6 +114,7 @@ export const CompositionArea = ({ clearQuotedMessage, getQuotedMessage, sortedGroupMembers, + setSecureInput, // EmojiButton onPickEmoji, onSetSkinTone, @@ -470,6 +472,7 @@ export const CompositionArea = ({ clearQuotedMessage={clearQuotedMessage} getQuotedMessage={getQuotedMessage} sortedGroupMembers={sortedGroupMembers} + setSecureInput={setSecureInput} /> {!large ? ( diff --git a/ts/components/CompositionInput.stories.tsx b/ts/components/CompositionInput.stories.tsx index abeb4ad7e..81794f64c 100644 --- a/ts/components/CompositionInput.stories.tsx +++ b/ts/components/CompositionInput.stories.tsx @@ -28,6 +28,7 @@ const createProps = (overrideProps: Partial = {}): Props => ({ getQuotedMessage: action('getQuotedMessage'), onPickEmoji: action('onPickEmoji'), large: boolean('large', overrideProps.large || false), + setSecureInput: action('setSecureInput'), sortedGroupMembers: overrideProps.sortedGroupMembers || [], skinTone: select( 'skinTone', diff --git a/ts/components/CompositionInput.tsx b/ts/components/CompositionInput.tsx index 473c41cdf..f3c65c888 100644 --- a/ts/components/CompositionInput.tsx +++ b/ts/components/CompositionInput.tsx @@ -76,6 +76,7 @@ export type Props = { onSubmit(message: string, mentions: Array): unknown; getQuotedMessage(): unknown; clearQuotedMessage(): unknown; + setSecureInput(enabled: boolean): unknown; }; const MAX_LENGTH = 64 * 1024; @@ -103,6 +104,7 @@ export const CompositionInput: React.ComponentType = props => { skinTone, draftText, draftBodyRanges, + setSecureInput, getQuotedMessage, clearQuotedMessage, sortedGroupMembers, @@ -259,6 +261,20 @@ export const CompositionInput: React.ComponentType = props => { return false; }; + const onFocus = (): void => { + setSecureInput(true); + }; + + const onBlur = (): void => { + setSecureInput(false); + }; + + React.useEffect(() => { + return () => { + setSecureInput(false); + }; + }, [setSecureInput]); + const onEnter = (): boolean => { const quill = quillRef.current; const emojiCompletion = emojiCompletionRef.current; @@ -494,6 +510,8 @@ export const CompositionInput: React.ComponentType = props => { onChange, onEnter, onEscape, + onFocus, + onBlur, onPickEmoji, onShortKeyEnter, onTab, @@ -508,6 +526,8 @@ export const CompositionInput: React.ComponentType = props => { return ( callbacksRef.current.onFocus()} + onBlur={() => callbacksRef.current.onBlur()} onChange={() => callbacksRef.current.onChange()} defaultValue={delta} modules={{ diff --git a/ts/components/ForwardMessageModal.stories.tsx b/ts/components/ForwardMessageModal.stories.tsx index f9e78bf42..cb0f2902a 100644 --- a/ts/components/ForwardMessageModal.stories.tsx +++ b/ts/components/ForwardMessageModal.stories.tsx @@ -53,6 +53,7 @@ const createProps = (overrideProps: Partial = {}): PropsType => ({ onSetSkinTone: action('onSetSkinTone'), recentEmojis: [], removeLinkPreview: action('removeLinkPreview'), + setSecureInput: action('setSecureInput'), skinTone: 0, }); diff --git a/ts/components/ForwardMessageModal.tsx b/ts/components/ForwardMessageModal.tsx index 1bdcf1adf..00c3980c7 100644 --- a/ts/components/ForwardMessageModal.tsx +++ b/ts/components/ForwardMessageModal.tsx @@ -49,6 +49,7 @@ export type DataPropsType = { caretLocation?: number ) => unknown; onTextTooLong: () => void; + setSecureInput: (enabled: boolean) => void; } & Pick; type ActionPropsType = Pick< @@ -78,6 +79,7 @@ export const ForwardMessageModal: FunctionComponent = ({ recentEmojis, removeLinkPreview, skinTone, + setSecureInput, }) => { const inputRef = useRef(null); const inputApiRef = React.useRef(); @@ -306,6 +308,7 @@ export const ForwardMessageModal: FunctionComponent = ({ onPickEmoji={onPickEmoji} onSubmit={forwardMessage} onTextTooLong={onTextTooLong} + setSecureInput={setSecureInput} />
unknown; onTextTooLong: () => void; + setSecureInput: (enabled: boolean) => void; }; const mapStateToProps = ( @@ -48,6 +49,7 @@ const mapStateToProps = ( onClose, onEditorStateChange, onTextTooLong, + setSecureInput, } = props; const candidateConversations = getAllComposableConversations(state); @@ -68,6 +70,7 @@ const mapStateToProps = ( recentEmojis, skinTone, onTextTooLong, + setSecureInput, }; }; diff --git a/ts/textsecure/WebAPI.ts b/ts/textsecure/WebAPI.ts index cd9d96bc3..9266c6637 100644 --- a/ts/textsecure/WebAPI.ts +++ b/ts/textsecure/WebAPI.ts @@ -2119,6 +2119,7 @@ export function initialize({ auth.groupPublicParamsHex, auth.authCredentialPresentationHex ); + const safeInviteLinkPassword = toWebSafeBase64(inviteLinkPassword); const response: ArrayBuffer = await _ajax({ basicAuth, @@ -2127,7 +2128,8 @@ export function initialize({ host: storageUrl, httpType: 'GET', responseType: 'arraybuffer', - urlParameters: `/${toWebSafeBase64(inviteLinkPassword)}`, + urlParameters: `/${safeInviteLinkPassword}`, + redactUrl: _createRedactor(safeInviteLinkPassword), }); return window.textsecure.protobuf.GroupJoinInfo.decode(response); @@ -2143,6 +2145,9 @@ export function initialize({ options.authCredentialPresentationHex ); const data = changes.toArrayBuffer(); + const safeInviteLinkPassword = inviteLinkBase64 + ? toWebSafeBase64(inviteLinkBase64) + : undefined; const response: ArrayBuffer = await _ajax({ basicAuth, @@ -2152,8 +2157,11 @@ export function initialize({ host: storageUrl, httpType: 'PATCH', responseType: 'arraybuffer', - urlParameters: inviteLinkBase64 - ? `?inviteLinkPassword=${toWebSafeBase64(inviteLinkBase64)}` + urlParameters: safeInviteLinkPassword + ? `?inviteLinkPassword=${safeInviteLinkPassword}` + : undefined, + redactUrl: safeInviteLinkPassword + ? _createRedactor(safeInviteLinkPassword) : undefined, }); diff --git a/ts/views/conversation_view.ts b/ts/views/conversation_view.ts index a0ebf1654..aaa47d555 100644 --- a/ts/views/conversation_view.ts +++ b/ts/views/conversation_view.ts @@ -631,6 +631,9 @@ Whisper.ConversationView = Whisper.View.extend({ bodyRanges: Array, caretLocation?: number ) => this.onEditorStateChange(msg, bodyRanges, caretLocation), + setSecureInput: (enabled: boolean) => { + window.setSecureInput(enabled); + }, onTextTooLong: () => this.showToast(Whisper.MessageBodyTooLongToast), onChooseAttachment: this.onChooseAttachment.bind(this), getQuotedMessage: () => this.model.get('quotedMessageId'), @@ -2255,6 +2258,9 @@ Whisper.ConversationView = Whisper.View.extend({ {}, document.querySelector('.module-ForwardMessageModal') ), + setSecureInput: (enabled: boolean) => { + window.setSecureInput(enabled); + }, } ), }); diff --git a/ts/window.d.ts b/ts/window.d.ts index e102d46d6..8d39e6e8c 100644 --- a/ts/window.d.ts +++ b/ts/window.d.ts @@ -224,6 +224,7 @@ declare global { setAutoHideMenuBar: (value: WhatIsThis) => void; setBadgeCount: (count: number) => void; setMenuBarVisibility: (value: WhatIsThis) => void; + setSecureInput: (enabled: boolean) => void; showConfirmationDialog: (options: ConfirmationDialogViewProps) => void; showKeyboardShortcuts: () => void; storage: {