From 43685d15c634c4242a5d3f2cda5e8b502b0acbe6 Mon Sep 17 00:00:00 2001 From: Josh Perez <60019601+josh-signal@users.noreply.github.com> Date: Thu, 16 Sep 2021 11:52:56 -0400 Subject: [PATCH] Context isolation for About, ScreenShare, Preferences --- about.html | 53 +---------------------- about_preload.js | 28 ------------ js/about_start.js | 30 ------------- main.js | 22 +++++++--- package.json | 2 - screenShare.html | 4 -- screenShare_preload.js | 62 --------------------------- settings.html | 5 +-- stylesheets/components/About.scss | 22 ++++++++++ stylesheets/manifest.scss | 1 + ts/components/About.tsx | 63 +++++++++++++++++++++++++++ ts/util/lint/exceptions.json | 42 +----------------- ts/window.d.ts | 27 +++--------- ts/windows/about/preload.ts | 34 +++++++++++++++ ts/windows/configure.ts | 30 +++++++++++++ ts/windows/init.ts | 4 ++ ts/windows/screenShare.ts | 11 ----- ts/windows/screenShare/preload.ts | 31 ++++++++++++++ ts/windows/settings/init.ts | 13 ------ ts/windows/settings/preload.ts | 71 +++++++++---------------------- 20 files changed, 234 insertions(+), 321 deletions(-) delete mode 100644 about_preload.js delete mode 100644 js/about_start.js delete mode 100644 screenShare_preload.js create mode 100644 stylesheets/components/About.scss create mode 100644 ts/components/About.tsx create mode 100644 ts/windows/about/preload.ts create mode 100644 ts/windows/configure.ts create mode 100644 ts/windows/init.ts delete mode 100644 ts/windows/screenShare.ts create mode 100644 ts/windows/screenShare/preload.ts delete mode 100644 ts/windows/settings/init.ts diff --git a/about.html b/about.html index e6eeeb0c1..6e62b3d11 100644 --- a/about.html +++ b/about.html @@ -6,11 +6,7 @@ - -
- - -
-
-
- signal.org -
-
-
- Software Acknowledgments -
-
- Terms & Privacy Policy -
-
- - +
+ diff --git a/about_preload.js b/about_preload.js deleted file mode 100644 index 2f9fa80f7..000000000 --- a/about_preload.js +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright 2018-2021 Signal Messenger, LLC -// SPDX-License-Identifier: AGPL-3.0-only - -/* global window */ - -const { ipcRenderer } = require('electron'); -const url = require('url'); -const i18n = require('./js/modules/i18n'); -const { - getEnvironment, - setEnvironment, - parseEnvironment, -} = require('./ts/environment'); - -const config = url.parse(window.location.toString(), true).query; -const { locale } = config; -const localeMessages = ipcRenderer.sendSync('locale-data'); -setEnvironment(parseEnvironment(config.environment)); - -window.getEnvironment = getEnvironment; -window.getVersion = () => config.version; -window.getAppInstance = () => config.appInstance; - -window.closeAbout = () => ipcRenderer.send('close-about'); - -window.i18n = i18n.setup(locale, localeMessages); - -require('./ts/logging/set_up_renderer_logging').initialize(); diff --git a/js/about_start.js b/js/about_start.js deleted file mode 100644 index 775c8a32c..000000000 --- a/js/about_start.js +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright 2018-2020 Signal Messenger, LLC -// SPDX-License-Identifier: AGPL-3.0-only - -/* global $: false */ - -// Add version -$('.version').text(`v${window.getVersion()}`); - -// Add debugging metadata - environment if not production, app instance name -const states = []; - -if (window.getEnvironment() !== 'production') { - states.push(window.getEnvironment()); -} -if (window.getAppInstance()) { - states.push(window.getAppInstance()); -} - -$('.environment').text(states.join(' - ')); - -// Install the 'dismiss with escape key' handler -$(document).on('keydown', e => { - if (e.keyCode === 27) { - window.closeAbout(); - } -}); - -// Localize the acknowledgment and privacy strings -$('.acknowledgments').text(window.i18n('softwareAcknowledgments')); -$('.privacy').text(window.i18n('privacyPolicy')); diff --git a/main.js b/main.js index d99134db9..b7bf49467 100644 --- a/main.js +++ b/main.js @@ -824,8 +824,14 @@ function showScreenShareWindow(sourceName) { ...defaultWebPrefs, nodeIntegration: false, nodeIntegrationInWorker: false, - contextIsolation: false, - preload: path.join(__dirname, 'screenShare_preload.js'), + contextIsolation: true, + preload: path.join( + __dirname, + 'ts', + 'windows', + 'screenShare', + 'preload.js' + ), }, x: Math.floor(display.size.width / 2) - width / 2, y: 24, @@ -847,6 +853,9 @@ function showScreenShareWindow(sourceName) { 'render-screen-sharing-controller', sourceName ); + if (config.get('openDevTools')) { + screenShareWindow.webContents.openDevTools(); + } }); } @@ -869,8 +878,8 @@ function showAbout() { ...defaultWebPrefs, nodeIntegration: false, nodeIntegrationInWorker: false, - contextIsolation: false, - preload: path.join(__dirname, 'about_preload.js'), + contextIsolation: true, + preload: path.join(__dirname, 'ts', 'windows', 'about', 'preload.js'), nativeWindowOpen: true, }, }; @@ -887,6 +896,9 @@ function showAbout() { aboutWindow.once('ready-to-show', () => { aboutWindow.show(); + if (config.get('openDevTools')) { + aboutWindow.webContents.openDevTools(); + } }); } @@ -911,7 +923,7 @@ function showSettingsWindow() { ...defaultWebPrefs, nodeIntegration: false, nodeIntegrationInWorker: false, - contextIsolation: false, + contextIsolation: true, enableRemoteModule: true, preload: path.join(__dirname, 'ts', 'windows', 'settings', 'preload.js'), nativeWindowOpen: true, diff --git a/package.json b/package.json index 678a6eafb..1a56a6f05 100644 --- a/package.json +++ b/package.json @@ -416,8 +416,6 @@ "app/*", "preload.bundle.js", "preload_utils.js", - "about_preload.js", - "screenShare_preload.js", "permissions_popup_preload.js", "debug_log_preload.js", "main.js", diff --git a/screenShare.html b/screenShare.html index 81c0fb99f..141cbce57 100644 --- a/screenShare.html +++ b/screenShare.html @@ -22,9 +22,5 @@
- diff --git a/screenShare_preload.js b/screenShare_preload.js deleted file mode 100644 index c347ddfdb..000000000 --- a/screenShare_preload.js +++ /dev/null @@ -1,62 +0,0 @@ -// Copyright 2021 Signal Messenger, LLC -// SPDX-License-Identifier: AGPL-3.0-only - -/* global window */ - -const React = require('react'); -const ReactDOM = require('react-dom'); -const url = require('url'); -const { ipcRenderer } = require('electron'); - -// It is important to call this as early as possible -require('./ts/windows/context'); - -const i18n = require('./js/modules/i18n'); -const { - getEnvironment, - setEnvironment, - parseEnvironment, -} = require('./ts/environment'); -const { - CallingScreenSharingController, -} = require('./ts/components/CallingScreenSharingController'); - -const config = url.parse(window.location.toString(), true).query; -const { locale } = config; -const localeMessages = ipcRenderer.sendSync('locale-data'); -setEnvironment(parseEnvironment(config.environment)); - -window.React = React; -window.ReactDOM = ReactDOM; -window.getAppInstance = () => config.appInstance; -window.getEnvironment = getEnvironment; -window.getVersion = () => config.version; -window.i18n = i18n.setup(locale, localeMessages); - -let renderComponent; -window.registerScreenShareControllerRenderer = f => { - renderComponent = f; -}; - -function renderScreenSharingController(event, presentedSourceName) { - if (!renderComponent) { - setTimeout(renderScreenSharingController, 100); - return; - } - - const props = { - i18n: window.i18n, - onCloseController: () => ipcRenderer.send('close-screen-share-controller'), - onStopSharing: () => ipcRenderer.send('stop-screen-share'), - presentedSourceName, - }; - - renderComponent(CallingScreenSharingController, props); -} - -ipcRenderer.once( - 'render-screen-sharing-controller', - renderScreenSharingController -); - -require('./ts/logging/set_up_renderer_logging').initialize(); diff --git a/settings.html b/settings.html index 93ce26b8d..85082483a 100644 --- a/settings.html +++ b/settings.html @@ -22,9 +22,6 @@
- + diff --git a/stylesheets/components/About.scss b/stylesheets/components/About.scss new file mode 100644 index 000000000..fe73d8734 --- /dev/null +++ b/stylesheets/components/About.scss @@ -0,0 +1,22 @@ +// Copyright 2021 Signal Messenger, LLC +// SPDX-License-Identifier: AGPL-3.0-only + +.About { + align-items: center; + background-color: $color-ultramarine-icon; + color: $color-white; + display: flex; + font-size: 14px; + height: 100vh; + justify-content: center; + overflow: hidden; + text-align: center; + + img { + margin-top: 1em; + } + + a { + color: $color-white; + } +} diff --git a/stylesheets/manifest.scss b/stylesheets/manifest.scss index c6cceb19f..6d94f29c0 100644 --- a/stylesheets/manifest.scss +++ b/stylesheets/manifest.scss @@ -26,6 +26,7 @@ @import 'options'; // New style: components +@import './components/About.scss'; @import './components/AddGroupMembersModal.scss'; @import './components/AnnouncementsOnlyGroupBanner.scss'; @import './components/App.scss'; diff --git a/ts/components/About.tsx b/ts/components/About.tsx new file mode 100644 index 000000000..527cb1778 --- /dev/null +++ b/ts/components/About.tsx @@ -0,0 +1,63 @@ +// Copyright 2021 Signal Messenger, LLC +// SPDX-License-Identifier: AGPL-3.0-only + +import React, { useEffect } from 'react'; +import { LocalizerType } from '../types/Util'; + +export type PropsType = { + closeAbout: () => unknown; + environment: string; + i18n: LocalizerType; + version: string; +}; + +export const About = ({ + closeAbout, + i18n, + environment, + version, +}: PropsType): JSX.Element => { + useEffect(() => { + const handler = (event: KeyboardEvent) => { + if (event.key === 'Escape') { + closeAbout(); + + event.preventDefault(); + event.stopPropagation(); + } + }; + document.addEventListener('keydown', handler); + + return () => { + document.removeEventListener('keydown', handler); + }; + }, [closeAbout]); + + return ( +
+
+
+ +
{version}
+
{environment}
+
+ signal.org +
+
+
+ + {i18n('softwareAcknowledgments')} + +
+
+ + {i18n('privacyPolicy')} + +
+
+
+ ); +}; diff --git a/ts/util/lint/exceptions.json b/ts/util/lint/exceptions.json index 06a44cf4b..514f19f7b 100644 --- a/ts/util/lint/exceptions.json +++ b/ts/util/lint/exceptions.json @@ -106,46 +106,6 @@ "updated": "2018-09-18T19:19:27.699Z", "reasonDetail": "Very limited in what HTML can be injected - dark/light options specify colors for the light/dark parts of QRCode" }, - { - "rule": "jQuery-$(", - "path": "js/about_start.js", - "line": "$('.version').text(`v${window.getVersion()}`);", - "reasonCategory": "usageTrusted", - "updated": "2018-09-19T21:59:32.770Z", - "reasonDetail": "Protected from arbitrary input" - }, - { - "rule": "jQuery-$(", - "path": "js/about_start.js", - "line": "$('.environment').text(states.join(' - '));", - "reasonCategory": "usageTrusted", - "updated": "2018-09-19T21:59:32.770Z", - "reasonDetail": "Protected from arbitrary input" - }, - { - "rule": "jQuery-$(", - "path": "js/about_start.js", - "line": "$(document).on('keydown', e => {", - "reasonCategory": "usageTrusted", - "updated": "2018-09-19T21:59:32.770Z", - "reasonDetail": "Protected from arbitrary input" - }, - { - "rule": "jQuery-$(", - "path": "js/about_start.js", - "line": "$('.acknowledgments').text(window.i18n('softwareAcknowledgments'));", - "reasonCategory": "usageTrusted", - "updated": "2020-09-16T14:49:26.520Z", - "reasonDetail": "Protected from arbitrary input" - }, - { - "rule": "jQuery-$(", - "path": "js/about_start.js", - "line": "$('.privacy').text(window.i18n('privacyPolicy'));", - "reasonCategory": "usageTrusted", - "updated": "2020-09-16T14:49:26.520Z", - "reasonDetail": "Protected from arbitrary input" - }, { "rule": "jQuery-$(", "path": "js/debug_log_start.js", @@ -13944,4 +13904,4 @@ "reasonCategory": "usageTrusted", "updated": "2021-07-22T03:00:34.561Z" } -] \ No newline at end of file +] diff --git a/ts/window.d.ts b/ts/window.d.ts index 7c5f76f39..5b40325d0 100644 --- a/ts/window.d.ts +++ b/ts/window.d.ts @@ -85,10 +85,6 @@ import { ConversationModel } from './models/conversations'; import { combineNames } from './util'; import { BatcherType } from './util/batcher'; import { AttachmentList } from './components/conversation/AttachmentList'; -import { - CallingScreenSharingController, - PropsType as CallingScreenSharingControllerProps, -} from './components/CallingScreenSharingController'; import { CaptionEditor } from './components/CaptionEditor'; import { ChatColorPicker } from './components/ChatColorPicker'; import { ConfirmationDialog } from './components/ConfirmationDialog'; @@ -166,13 +162,6 @@ declare global { WhatIsThis: WhatIsThis; - registerScreenShareControllerRenderer: ( - f: ( - component: typeof CallingScreenSharingController, - props: CallingScreenSharingControllerProps - ) => void - ) => void; - addSetupMenuItems: () => void; attachmentDownloadQueue: Array | undefined; startupProcessingQueue: StartupQueue | undefined; @@ -497,16 +486,14 @@ declare global { RETRY_DELAY: boolean; - // These elements are only available in the Settings window - SignalModule: { - registerReactRenderer: ( - f:

( - component: FunctionComponent

| ComponentClass

, - props?: (Attributes & P) | null - ) => void - ) => void; + SignalWindow: { + config: string; + getAppInstance: () => string | undefined; + getEnvironment: () => string; + getVersion: () => string; + i18n: LocalizerType; + renderWindow: () => void; }; - renderPreferences: () => unknown; } // We want to extend `Error`, so we need an interface. diff --git a/ts/windows/about/preload.ts b/ts/windows/about/preload.ts new file mode 100644 index 000000000..d5ab23988 --- /dev/null +++ b/ts/windows/about/preload.ts @@ -0,0 +1,34 @@ +// Copyright 2018-2021 Signal Messenger, LLC +// SPDX-License-Identifier: AGPL-3.0-only + +import React from 'react'; +import ReactDOM from 'react-dom'; +import { contextBridge, ipcRenderer } from 'electron'; + +// It is important to call this as early as possible +import '../context'; + +import { SignalWindow } from '../configure'; +import { About } from '../../components/About'; + +contextBridge.exposeInMainWorld('SignalWindow', { + ...SignalWindow, + renderWindow: () => { + const environmentText: Array = [SignalWindow.getEnvironment()]; + + const appInstance = SignalWindow.getAppInstance(); + if (appInstance) { + environmentText.push(appInstance); + } + + ReactDOM.render( + React.createElement(About, { + closeAbout: () => ipcRenderer.send('close-about'), + environment: environmentText.join(' - '), + i18n: SignalWindow.i18n, + version: SignalWindow.getVersion(), + }), + document.getElementById('app') + ); + }, +}); diff --git a/ts/windows/configure.ts b/ts/windows/configure.ts new file mode 100644 index 000000000..771bfad98 --- /dev/null +++ b/ts/windows/configure.ts @@ -0,0 +1,30 @@ +// Copyright 2018-2021 Signal Messenger, LLC +// SPDX-License-Identifier: AGPL-3.0-only + +import url from 'url'; +import { ipcRenderer } from 'electron'; + +import i18n from '../../js/modules/i18n'; +import { + getEnvironment, + parseEnvironment, + setEnvironment, +} from '../environment'; +import { strictAssert } from '../util/assert'; + +const config = url.parse(window.location.toString(), true).query; +const { locale } = config; +strictAssert(locale, 'locale could not be parsed from config'); +strictAssert(typeof locale === 'string', 'locale is not a string'); + +const localeMessages = ipcRenderer.sendSync('locale-data'); +setEnvironment(parseEnvironment(config.environment)); + +export const SignalWindow = { + config, + getAppInstance: (): string | undefined => + config.appInstance ? String(config.appInstance) : undefined, + getEnvironment, + getVersion: (): string => String(config.version), + i18n: i18n.setup(locale, localeMessages), +}; diff --git a/ts/windows/init.ts b/ts/windows/init.ts new file mode 100644 index 000000000..b4b263970 --- /dev/null +++ b/ts/windows/init.ts @@ -0,0 +1,4 @@ +// Copyright 2021 Signal Messenger, LLC +// SPDX-License-Identifier: AGPL-3.0-only + +window.SignalWindow.renderWindow(); diff --git a/ts/windows/screenShare.ts b/ts/windows/screenShare.ts deleted file mode 100644 index 94a862262..000000000 --- a/ts/windows/screenShare.ts +++ /dev/null @@ -1,11 +0,0 @@ -// Copyright 2021 Signal Messenger, LLC -// SPDX-License-Identifier: AGPL-3.0-only - -// This needs to use window.React & window.ReactDOM since it's -// not commonJS compatible. -window.registerScreenShareControllerRenderer((Component, props) => { - window.ReactDOM.render( - window.React.createElement(Component, props), - document.getElementById('app') - ); -}); diff --git a/ts/windows/screenShare/preload.ts b/ts/windows/screenShare/preload.ts new file mode 100644 index 000000000..905daee04 --- /dev/null +++ b/ts/windows/screenShare/preload.ts @@ -0,0 +1,31 @@ +// Copyright 2021 Signal Messenger, LLC +// SPDX-License-Identifier: AGPL-3.0-only + +import React from 'react'; +import ReactDOM from 'react-dom'; +import { contextBridge, ipcRenderer } from 'electron'; + +// It is important to call this as early as possible +import '../context'; + +import { SignalWindow } from '../configure'; +import { CallingScreenSharingController } from '../../components/CallingScreenSharingController'; + +contextBridge.exposeInMainWorld('SignalWindow', SignalWindow); + +function renderScreenSharingController(presentedSourceName: string): void { + ReactDOM.render( + React.createElement(CallingScreenSharingController, { + i18n: SignalWindow.i18n, + onCloseController: () => + ipcRenderer.send('close-screen-share-controller'), + onStopSharing: () => ipcRenderer.send('stop-screen-share'), + presentedSourceName, + }), + document.getElementById('app') + ); +} + +ipcRenderer.once('render-screen-sharing-controller', (_, name: string) => { + renderScreenSharingController(name); +}); diff --git a/ts/windows/settings/init.ts b/ts/windows/settings/init.ts deleted file mode 100644 index f92f2c059..000000000 --- a/ts/windows/settings/init.ts +++ /dev/null @@ -1,13 +0,0 @@ -// Copyright 2021 Signal Messenger, LLC -// SPDX-License-Identifier: AGPL-3.0-only - -// This needs to use window.React & window.ReactDOM since it's -// not commonJS compatible. -window.SignalModule.registerReactRenderer((Component, props) => { - window.ReactDOM.render( - window.React.createElement(Component, props), - document.getElementById('app') - ); -}); - -window.renderPreferences(); diff --git a/ts/windows/settings/preload.ts b/ts/windows/settings/preload.ts index 315dea910..998c5b1fc 100644 --- a/ts/windows/settings/preload.ts +++ b/ts/windows/settings/preload.ts @@ -1,20 +1,17 @@ -// Copyright 2018-2021 Signal Messenger, LLC +// Copyright 2021 Signal Messenger, LLC // SPDX-License-Identifier: AGPL-3.0-only import React from 'react'; import ReactDOM from 'react-dom'; -import url from 'url'; -import { ipcRenderer } from 'electron'; +import { contextBridge, ipcRenderer } from 'electron'; +import { initialize as initializeLogging } from '../../logging/set_up_renderer_logging'; // It is important to call this as early as possible import '../context'; +import { SignalWindow } from '../configure'; import * as Settings from '../../types/Settings'; -import i18n from '../../../js/modules/i18n'; -import { - Preferences, - PropsType as PreferencesPropsType, -} from '../../components/Preferences'; +import { Preferences } from '../../components/Preferences'; import { SystemTraySetting, parseSystemTraySetting, @@ -22,27 +19,7 @@ import { } from '../../types/SystemTraySetting'; import { awaitObject } from '../../util/awaitObject'; import { createSetting, createCallback } from '../../util/preload'; -import { - getEnvironment, - setEnvironment, - parseEnvironment, -} from '../../environment'; -import { initialize as initializeLogging } from '../../logging/set_up_renderer_logging'; -import { strictAssert } from '../../util/assert'; -const config = url.parse(window.location.toString(), true).query; -const { locale } = config; -strictAssert(locale, 'locale could not be parsed from config'); -strictAssert(typeof locale === 'string', 'locale is not a string'); - -const localeMessages = ipcRenderer.sendSync('locale-data'); -setEnvironment(parseEnvironment(config.environment)); - -window.React = React; -window.ReactDOM = ReactDOM; -window.getEnvironment = getEnvironment; -window.getVersion = () => String(config.version); -window.i18n = i18n.setup(locale, localeMessages); function doneRendering() { ipcRenderer.send('settings-done-rendering'); } @@ -130,16 +107,6 @@ const ipcSetGlobalDefaultConversationColor = createCallback( const DEFAULT_NOTIFICATION_SETTING = 'message'; -let renderComponent: ( - component: typeof Preferences, - props: PreferencesPropsType -) => void; -window.SignalModule = { - registerReactRenderer: f => { - renderComponent = f; - }, -}; - function getSystemTraySettingValues( systemTraySetting: SystemTraySetting ): { @@ -161,11 +128,6 @@ function getSystemTraySettingValues( } const renderPreferences = async () => { - if (!renderComponent) { - setTimeout(window.renderPreferences, 100); - return; - } - const { blockedCount, deviceName, @@ -300,7 +262,7 @@ const renderPreferences = async () => { editCustomColor: ipcEditCustomColor, getConversationsWithCustomColor: ipcGetConversationsWithCustomColor, initialSpellCheckSetting: - config.appStartInitialSpellcheckSetting === 'true', + SignalWindow.config.appStartInitialSpellcheckSetting === 'true', makeSyncRequest: ipcMakeSyncRequest, removeCustomColor: ipcRemoveCustomColor, removeCustomColorOnConversations: ipcRemoveCustomColorOnConversations, @@ -316,7 +278,9 @@ const renderPreferences = async () => { isNotificationAttentionSupported: Settings.isDrawAttentionSupported(), isPhoneNumberSharingSupported, isSyncSupported: !isSyncNotSupported, - isSystemTraySupported: Settings.isSystemTraySupported(window.getVersion()), + isSystemTraySupported: Settings.isSystemTraySupported( + SignalWindow.getVersion() + ), // Change handlers onAudioNotificationsChange: reRender(settingAudioNotification.setValue), @@ -381,7 +345,7 @@ const renderPreferences = async () => { // rerender. onZoomFactorChange: settingZoomFactor.setValue, - i18n: window.i18n, + i18n: SignalWindow.i18n, }; function reRender(f: (value: Value) => Promise) { @@ -391,10 +355,17 @@ const renderPreferences = async () => { }; } - renderComponent(Preferences, props); + ReactDOM.render( + React.createElement(Preferences, props), + document.getElementById('app') + ); }; -window.renderPreferences = renderPreferences; - -initializeLogging(); ipcRenderer.on('render', () => renderPreferences()); + +contextBridge.exposeInMainWorld('SignalWindow', { + ...SignalWindow, + renderWindow: renderPreferences, +}); + +initializeLogging();