diff --git a/src/app/serverViewState.ts b/src/app/serverViewState.ts index 9987e7ca..bbb84662 100644 --- a/src/app/serverViewState.ts +++ b/src/app/serverViewState.ts @@ -16,6 +16,7 @@ import { SHOW_NEW_SERVER_MODAL, SHOW_REMOVE_SERVER_MODAL, SWITCH_SERVER, + TOGGLE_SECURE_INPUT, UPDATE_SERVER_ORDER, UPDATE_SHORTCUT_MENU, UPDATE_TAB_ORDER, @@ -87,6 +88,7 @@ export class ServerViewState { ServerManager.getServerLog(serverId, 'WindowManager').error('Cannot find server in config'); return; } + ipcMain.emit(TOGGLE_SECURE_INPUT, null, false); this.currentServerId = serverId; const nextView = ServerManager.getLastActiveTabForServer(serverId); if (waitForViewToExist) { diff --git a/src/main/app/intercom.test.js b/src/main/app/intercom.test.js index b18e7884..a5b1948b 100644 --- a/src/main/app/intercom.test.js +++ b/src/main/app/intercom.test.js @@ -1,6 +1,8 @@ // Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. +import {app} from 'electron'; + import {getLocalURLString, getLocalPreload} from 'main/utils'; import ServerManager from 'common/servers/serverManager'; import MainWindow from 'main/windows/mainWindow'; @@ -9,8 +11,15 @@ import ModalManager from 'main/views/modalManager'; import { handleWelcomeScreenModal, handleMainWindowIsShown, + handleToggleSecureInput, } from './intercom'; +jest.mock('electron', () => ({ + app: { + setSecureKeyboardEntryEnabled: jest.fn(), + }, +})); + jest.mock('app/serverViewState', () => ({})); jest.mock('common/config', () => ({ setServers: jest.fn(), @@ -73,4 +82,38 @@ describe('main/app/intercom', () => { expect(ModalManager.addModal).not.toHaveBeenCalled(); }); }); + + describe('handleToggleSecureInput', () => { + beforeEach(() => { + MainWindow.get.mockReturnValue({ + isFocused: () => true, + }); + }); + + afterEach(() => { + jest.resetAllMocks(); + }); + + it('should not fire for OSes that are not macOS', () => { + const originalPlatform = process.platform; + Object.defineProperty(process, 'platform', { + value: 'linux', + }); + + handleToggleSecureInput({}, true); + + Object.defineProperty(process, 'platform', { + value: originalPlatform, + }); + + expect(app.setSecureKeyboardEntryEnabled).not.toHaveBeenCalled(); + }); + + it('should not fire if window is not focused', () => { + MainWindow.get.mockReturnValue({isFocused: () => false}); + handleToggleSecureInput({}, true); + + expect(app.setSecureKeyboardEntryEnabled).not.toHaveBeenCalled(); + }); + }); }); diff --git a/src/main/app/intercom.ts b/src/main/app/intercom.ts index 611dad90..d2663cbb 100644 --- a/src/main/app/intercom.ts +++ b/src/main/app/intercom.ts @@ -153,6 +153,11 @@ export function handleToggleSecureInput(event: IpcMainEvent, secureInput: boolea return; } + // Don't allow this to turn on if the main window isn't focused + if (secureInput && !MainWindow.get()?.isFocused()) { + return; + } + // Enforce macOS to restrict processes from reading the keyboard input when in a password field log.debug('handleToggleSecureInput', secureInput); app.setSecureKeyboardEntryEnabled(secureInput); diff --git a/src/main/preload/mattermost.js b/src/main/preload/mattermost.js index f927d701..d8b54da6 100644 --- a/src/main/preload/mattermost.js +++ b/src/main/preload/mattermost.js @@ -376,14 +376,21 @@ window.addEventListener('resize', () => { }); let isPasswordBox = false; - -window.addEventListener('focusin', (event) => { - const targetIsPasswordBox = event.target.tagName === 'INPUT' && event.target.type === 'password'; - if (targetIsPasswordBox && !isPasswordBox) { +const shouldSecureInput = (element, force = false) => { + const targetIsPasswordBox = (element && element.tagName === 'INPUT' && element.type === 'password'); + if (targetIsPasswordBox && (!isPasswordBox || force)) { ipcRenderer.send(TOGGLE_SECURE_INPUT, true); - } else if (!targetIsPasswordBox && isPasswordBox) { + } else if (!targetIsPasswordBox && (isPasswordBox || force)) { ipcRenderer.send(TOGGLE_SECURE_INPUT, false); } isPasswordBox = targetIsPasswordBox; +}; + +window.addEventListener('focusin', (event) => { + shouldSecureInput(event.target); +}); + +window.addEventListener('focus', () => { + shouldSecureInput(document.activeElement, true); }); diff --git a/src/main/windows/mainWindow.ts b/src/main/windows/mainWindow.ts index 0b66b1d2..1052af18 100644 --- a/src/main/windows/mainWindow.ts +++ b/src/main/windows/mainWindow.ts @@ -27,6 +27,7 @@ import { MAIN_WINDOW_RESIZED, MAIN_WINDOW_FOCUSED, VIEW_FINISHED_RESIZING, + TOGGLE_SECURE_INPUT, } from 'common/communication'; import Config from 'common/config'; import {Logger} from 'common/log'; @@ -314,6 +315,7 @@ export class MainWindow extends EventEmitter { globalShortcut.unregisterAll(); this.emit(MAIN_WINDOW_RESIZED, this.getBounds()); + ipcMain.emit(TOGGLE_SECURE_INPUT, null, false); // App should save bounds when a window is closed. // However, 'close' is not fired in some situations(shutdown, ctrl+c)