diff --git a/package.json b/package.json index a8399ab63e..f461417634 100644 --- a/package.json +++ b/package.json @@ -69,7 +69,9 @@ "oidc-client-ts": "3.0.1", "jwt-decode": "4.0.0", "@floating-ui/react": "0.26.11", - "@radix-ui/react-id": "1.1.0" + "@radix-ui/react-id": "1.1.0", + "caniuse-lite": "1.0.30001643", + "electron-to-chromium": "1.5.2" }, "dependencies": { "@babel/runtime": "^7.12.5", @@ -88,12 +90,14 @@ "await-lock": "^2.1.0", "bloom-filters": "^3.0.1", "blurhash": "^2.0.3", + "browserslist": "^4.23.2", "classnames": "^2.2.6", "commonmark": "^0.31.0", "counterpart": "^0.18.6", "css-tree": "^2.3.1", "diff-dom": "^5.0.0", "diff-match-patch": "^1.0.5", + "electron-to-chromium": "^1.5.2", "emojibase-regex": "15.3.2", "escape-html": "^1.0.3", "file-saver": "^2.0.5", diff --git a/src/@types/electron-to-chromium.d.ts b/src/@types/electron-to-chromium.d.ts new file mode 100644 index 0000000000..8ee13e92ad --- /dev/null +++ b/src/@types/electron-to-chromium.d.ts @@ -0,0 +1,22 @@ +/* +Copyright 2024 The Matrix.org Foundation C.I.C. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +declare module "electron-to-chromium/versions" { + const versionMap: { + [electronVersion: string]: string; + }; + export default versionMap; +} diff --git a/src/Lifecycle.ts b/src/Lifecycle.ts index f2cc7e57f1..385188dc1b 100644 --- a/src/Lifecycle.ts +++ b/src/Lifecycle.ts @@ -83,6 +83,7 @@ import { tryDecryptToken, } from "./utils/tokens/tokens"; import { TokenRefresher } from "./utils/oidc/TokenRefresher"; +import { checkBrowserSupport } from "./SupportedBrowser"; const HOMESERVER_URL_KEY = "mx_hs_url"; const ID_SERVER_URL_KEY = "mx_is_url"; @@ -1001,6 +1002,7 @@ async function startMatrixClient( IntegrationManagers.sharedInstance().startWatching(); ActiveWidgetStore.instance.start(); LegacyCallHandler.instance.start(); + checkBrowserSupport(); // Start Mjolnir even though we haven't checked the feature flag yet. Starting // the thing just wastes CPU cycles, but should result in no actual functionality diff --git a/src/SupportedBrowser.ts b/src/SupportedBrowser.ts new file mode 100644 index 0000000000..4314e27961 --- /dev/null +++ b/src/SupportedBrowser.ts @@ -0,0 +1,123 @@ +/* +Copyright 2024 The Matrix.org Foundation C.I.C. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +import { logger } from "matrix-js-sdk/src/logger"; +import browserlist from "browserslist"; +import electronToChromium from "electron-to-chromium/versions"; + +import { DeviceType, parseUserAgent } from "./utils/device/parseUserAgent"; +import ToastStore from "./stores/ToastStore"; +import GenericToast from "./components/views/toasts/GenericToast"; +import { _t } from "./languageHandler"; +import SdkConfig from "./SdkConfig"; + +export const LOCAL_STORAGE_KEY = "mx_accepts_unsupported_browser"; +const TOAST_KEY = "unsupportedbrowser"; + +const SUPPORTED_DEVICE_TYPES = [DeviceType.Web, DeviceType.Desktop]; +const SUPPORTED_BROWSER_QUERY = + "last 2 Chrome versions, last 2 Firefox versions, last 2 Safari versions, last 2 Edge versions"; +const LEARN_MORE_URL = "https://github.com/element-hq/element-web#supported-environments"; + +function onLearnMoreClick(): void { + onDismissClick(); + window.open(LEARN_MORE_URL, "_blank", "noopener,noreferrer"); +} + +function onDismissClick(): void { + localStorage.setItem(LOCAL_STORAGE_KEY, String(true)); + ToastStore.sharedInstance().dismissToast(TOAST_KEY); +} + +function getBrowserNameVersion(browser: string): [name: string, version: number] { + const [browserName, browserVersion] = browser.split(" "); + const browserNameLc = browserName.toLowerCase(); + if (browserNameLc === "electron") { + // The electron-to-chromium map is keyed by the major and minor version of Electron + const chromiumVersion = electronToChromium[browserVersion.split(".").slice(0, 2).join(".")]; + if (chromiumVersion) { + return ["chrome", parseInt(chromiumVersion, 10)]; + } + } + + return [browserNameLc, parseInt(browserVersion, 10)]; +} + +/** + * Function to check if the current browser is considered supported by our support policy. + * Based on user agent parsing so may be inaccurate if the user has fingerprint prevention turned up to 11. + */ +export function getBrowserSupport(): boolean { + const browsers = browserlist(SUPPORTED_BROWSER_QUERY).sort(); + const minimumBrowserVersions = new Map(); + for (const browser of browsers) { + const [browserName, browserVersion] = getBrowserNameVersion(browser); + // We sorted the browsers so will encounter the minimum version first + if (minimumBrowserVersions.has(browserName)) continue; + minimumBrowserVersions.set(browserName, browserVersion); + } + + const details = parseUserAgent(navigator.userAgent); + + let supported = true; + if (!SUPPORTED_DEVICE_TYPES.includes(details.deviceType)) { + logger.warn("Browser unsupported, unsupported device type", details.deviceType); + supported = false; + } + + if (details.client) { + const [browserName, browserVersion] = getBrowserNameVersion(details.client); + const minimumVersion = minimumBrowserVersions.get(browserName); + // Check both with the sub-version cut off and without as some browsers have less granular versioning e.g. Safari + if (!minimumVersion || browserVersion < minimumVersion) { + logger.warn("Browser unsupported, unsupported user agent", details.client); + supported = false; + } + } else { + logger.warn("Browser unsupported, unknown client", navigator.userAgent); + supported = false; + } + + return supported; +} + +/** + * Shows a user warning toast if the user's browser is not supported. + */ +export function checkBrowserSupport(): void { + const supported = getBrowserSupport(); + if (supported) return; + + if (localStorage.getItem(LOCAL_STORAGE_KEY)) { + logger.warn("Browser unsupported, but user has previously accepted"); + return; + } + + const brand = SdkConfig.get().brand; + ToastStore.sharedInstance().addOrReplaceToast({ + key: TOAST_KEY, + title: _t("unsupported_browser|title", { brand }), + props: { + description: _t("unsupported_browser|description", { brand }), + acceptLabel: _t("action|learn_more"), + onAccept: onLearnMoreClick, + rejectLabel: _t("action|dismiss"), + onReject: onDismissClick, + }, + component: GenericToast, + priority: 40, + }); +} diff --git a/src/components/views/dialogs/BugReportDialog.tsx b/src/components/views/dialogs/BugReportDialog.tsx index 5d826f283e..9ae17491e1 100644 --- a/src/components/views/dialogs/BugReportDialog.tsx +++ b/src/components/views/dialogs/BugReportDialog.tsx @@ -32,6 +32,7 @@ import DialogButtons from "../elements/DialogButtons"; import { sendSentryReport } from "../../../sentry"; import defaultDispatcher from "../../../dispatcher/dispatcher"; import { Action } from "../../../dispatcher/actions"; +import { getBrowserSupport } from "../../../SupportedBrowser"; interface IProps { onFinished: (success: boolean) => void; @@ -206,7 +207,10 @@ export default class BugReportDialog extends React.Component { } let warning: JSX.Element | undefined; - if (window.Modernizr && Object.values(window.Modernizr).some((support) => support === false)) { + if ( + (window.Modernizr && Object.values(window.Modernizr).some((support) => support === false)) || + !getBrowserSupport() + ) { warning = (

{_t("bug_reporting|unsupported_browser")} diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 37e69b7a77..025fbf8964 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -3694,6 +3694,10 @@ "truncated_list_n_more": { "other": "And %(count)s more..." }, + "unsupported_browser": { + "description": "If you continue, some features may stop working and there is a risk that you may lose data in the future. Update your browser to continue using %(brand)s.", + "title": "%(brand)s does not support this browser" + }, "unsupported_server_description": "This server is using an older version of Matrix. Upgrade to Matrix %(version)s to use %(brand)s without errors.", "unsupported_server_title": "Your server is unsupported", "update": { diff --git a/src/utils/device/parseUserAgent.ts b/src/utils/device/parseUserAgent.ts index 724ef617da..2d36b1209d 100644 --- a/src/utils/device/parseUserAgent.ts +++ b/src/utils/device/parseUserAgent.ts @@ -42,15 +42,15 @@ const getDeviceType = ( browser: UAParser.IBrowser, operatingSystem: UAParser.IOS, ): DeviceType => { + if (device.type === "mobile" || operatingSystem.name?.includes("Android") || userAgent.indexOf(IOS_KEYWORD) > -1) { + return DeviceType.Mobile; + } if (browser.name === "Electron") { return DeviceType.Desktop; } if (!!browser.name) { return DeviceType.Web; } - if (device.type === "mobile" || operatingSystem.name?.includes("Android") || userAgent.indexOf(IOS_KEYWORD) > -1) { - return DeviceType.Mobile; - } return DeviceType.Unknown; }; diff --git a/test/SupportedBrowser-test.ts b/test/SupportedBrowser-test.ts new file mode 100644 index 0000000000..f5cccf221b --- /dev/null +++ b/test/SupportedBrowser-test.ts @@ -0,0 +1,123 @@ +/* +Copyright 2024 The Matrix.org Foundation C.I.C. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +import { logger } from "matrix-js-sdk/src/logger"; + +import { checkBrowserSupport, LOCAL_STORAGE_KEY } from "../src/SupportedBrowser"; +import ToastStore from "../src/stores/ToastStore"; +import GenericToast from "../src/components/views/toasts/GenericToast"; + +jest.mock("matrix-js-sdk/src/logger"); + +describe("SupportedBrowser", () => { + beforeEach(() => { + jest.resetAllMocks(); + localStorage.clear(); + }); + + const testUserAgentFactory = + (expectedWarning?: string) => + async (userAgent: string): Promise => { + const toastSpy = jest.spyOn(ToastStore.sharedInstance(), "addOrReplaceToast"); + const warnLogSpy = jest.spyOn(logger, "warn"); + Object.defineProperty(window, "navigator", { value: { userAgent: userAgent }, writable: true }); + checkBrowserSupport(); + if (expectedWarning) { + expect(warnLogSpy).toHaveBeenCalledWith(expectedWarning, expect.any(String)); + expect(toastSpy).toHaveBeenCalled(); + } else { + expect(warnLogSpy).not.toHaveBeenCalled(); + expect(toastSpy).not.toHaveBeenCalled(); + } + }; + + it.each([ + // Safari on iOS + "Mozilla/5.0 (iPhone; CPU iPhone OS 17_5_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.5 Mobile/15E148 Safari/604.1", + // Firefox on iOS + "Mozilla/5.0 (iPhone; CPU iPhone OS 17_5_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) FxiOS/128.0 Mobile/15E148 Safari/605.1.15", + // Opera on Samsung + "Mozilla/5.0 (Linux; Android 10; SM-G970F) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.6533.64 Mobile Safari/537.36 OPR/76.2.4027.73374", + ])("should warn for mobile browsers", testUserAgentFactory("Browser unsupported, unsupported device type")); + + it.each([ + // Chrome on Chrome OS + "Mozilla/5.0 (X11; CrOS x86_64 15633.69.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.6045.212 Safari/537.36", + // Opera on Windows + "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36 OPR/113.0.0.0", + // Vivaldi on Linux + "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36 Vivaldi/6.8.3381.48", + // IE11 on Windows 10 + "Mozilla/5.0 (Windows NT 10.0; Trident/7.0; rv:11.0) like Gecko", + // Firefox 115 on macOS + "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_4_5; rv:115.0) Gecko/20000101 Firefox/115.0", + ])( + "should warn for unsupported desktop browsers", + testUserAgentFactory("Browser unsupported, unsupported user agent"), + ); + + it.each([ + // Safari 17.5 on macOS Sonoma + "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.5 Safari/605.1.15", + // Firefox 127 on macOS Sonoma + "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:127.0) Gecko/20100101 Firefox/127.0", + // Edge 126 on Windows + "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36 Edg/126.0.2592.113", + // Edge 126 on macOS + "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36 Edg/126.0.2592.113", + // Firefox 128 on Windows + "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:128.0) Gecko/20100101 Firefox/128.0", + // Firefox 128 on Linux + "Mozilla/5.0 (X11; Fedora; Linux x86_64; rv:128.0) Gecko/20100101 Firefox/128.0", + // Chrome 127 on Windows + "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36", + ])("should not warn for supported browsers", testUserAgentFactory()); + + it.each([ + // Element Nightly on macOS + "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) ElementNightly/2024072501 Chrome/126.0.6478.127 Electron/31.2.1 Safari/537.36", + ])("should not warn for Element Desktop", testUserAgentFactory()); + + it.each(["AppleTV11,1/11.1"])( + "should handle unknown user agent sanely", + testUserAgentFactory("Browser unsupported, unknown client"), + ); + + it("should not warn for unsupported browser if user accepted already", async () => { + const toastSpy = jest.spyOn(ToastStore.sharedInstance(), "addOrReplaceToast"); + const warnLogSpy = jest.spyOn(logger, "warn"); + const userAgent = + "Mozilla/5.0 (X11; CrOS x86_64 15633.69.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.6045.212 Safari/537.36"; + Object.defineProperty(window, "navigator", { value: { userAgent: userAgent }, writable: true }); + + checkBrowserSupport(); + expect(warnLogSpy).toHaveBeenCalledWith("Browser unsupported, unsupported user agent", expect.any(String)); + expect(toastSpy).toHaveBeenCalledWith( + expect.objectContaining({ + component: GenericToast, + title: "Element does not support this browser", + }), + ); + + localStorage.setItem(LOCAL_STORAGE_KEY, String(true)); + toastSpy.mockClear(); + warnLogSpy.mockClear(); + + checkBrowserSupport(); + expect(warnLogSpy).toHaveBeenCalledWith("Browser unsupported, but user has previously accepted"); + expect(toastSpy).not.toHaveBeenCalled(); + }); +}); diff --git a/test/utils/device/parseUserAgent-test.ts b/test/utils/device/parseUserAgent-test.ts index 65e31a68a0..7e418702de 100644 --- a/test/utils/device/parseUserAgent-test.ts +++ b/test/utils/device/parseUserAgent-test.ts @@ -40,6 +40,7 @@ const ANDROID_UA = [ // Legacy User Agent Implementation "Element/1.0.0 (Linux; U; Android 6.0.1; SM-A510F Build/MMB29; Flavour GPlay; MatrixAndroidSdk2 1.0)", "Element/1.0.0 (Linux; Android 7.0; SM-G610M Build/NRD90M; Flavour GPlay; MatrixAndroidSdk2 1.0)", + "Mozilla/5.0 (Linux; Android 9; SM-G973U Build/PPR1.180610.011) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Mobile Safari/537.36", ]; const ANDROID_EXPECTED_RESULT = [ @@ -50,6 +51,7 @@ const ANDROID_EXPECTED_RESULT = [ makeDeviceExtendedInfo(DeviceType.Mobile, "Google (Nexus) (5)", "Android 7.0"), makeDeviceExtendedInfo(DeviceType.Mobile, "Samsung SM-A510F", "Android 6.0.1"), makeDeviceExtendedInfo(DeviceType.Mobile, "Samsung SM-G610M", "Android 7.0"), + makeDeviceExtendedInfo(DeviceType.Mobile, "Samsung SM-G973U", "Android 9", "Chrome", "69.0.3497.100"), ]; const IOS_UA = [ @@ -57,12 +59,16 @@ const IOS_UA = [ "Element/1.8.21 (iPhone XS Max; iOS 15.2; Scale/3.00)", "Element/1.8.21 (iPad Pro (11-inch); iOS 15.2; Scale/3.00)", "Element/1.8.21 (iPad Pro (12.9-inch) (3rd generation); iOS 15.2; Scale/3.00)", + "Mozilla/5.0 (iPad; CPU OS 8_4_1 like Mac OS X) AppleWebKit/600.1.4 (KHTML, like Gecko) Version/8.0 Mobile/12H321 Safari/600.1.4", + "Mozilla/5.0 (iPhone; CPU iPhone OS 8_4_1 like Mac OS X) AppleWebKit/600.1.4 (KHTML, like Gecko) Version/8.0 Mobile/12H321 Safari/600.1.4", ]; const IOS_EXPECTED_RESULT = [ makeDeviceExtendedInfo(DeviceType.Mobile, "Apple iPhone", "iOS 15.2"), makeDeviceExtendedInfo(DeviceType.Mobile, "Apple iPhone XS Max", "iOS 15.2"), makeDeviceExtendedInfo(DeviceType.Mobile, "iPad Pro (11-inch)", "iOS 15.2"), makeDeviceExtendedInfo(DeviceType.Mobile, "iPad Pro (12.9-inch) (3rd generation)", "iOS 15.2"), + makeDeviceExtendedInfo(DeviceType.Web, "Apple iPad", "iOS", "Mobile Safari", "8.0"), + makeDeviceExtendedInfo(DeviceType.Mobile, "Apple iPhone", "iOS 8.4.1", "Mobile Safari", "8.0"), ]; const DESKTOP_UA = [ "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) ElementNightly/2022091301 Chrome/104.0.5112.102" + @@ -81,10 +87,6 @@ const WEB_UA = [ "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_2) AppleWebKit/600.3.18 (KHTML, like Gecko) Version/8.0.3 Safari/600.3.18", "Mozilla/5.0 (Windows NT 6.0; rv:40.0) Gecko/20100101 Firefox/40.0", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/42.0.2311.135 Safari/537.36 Edge/12.246", - // using mobile browser - "Mozilla/5.0 (iPad; CPU OS 8_4_1 like Mac OS X) AppleWebKit/600.1.4 (KHTML, like Gecko) Version/8.0 Mobile/12H321 Safari/600.1.4", - "Mozilla/5.0 (iPhone; CPU iPhone OS 8_4_1 like Mac OS X) AppleWebKit/600.1.4 (KHTML, like Gecko) Version/8.0 Mobile/12H321 Safari/600.1.4", - "Mozilla/5.0 (Linux; Android 9; SM-G973U Build/PPR1.180610.011) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Mobile Safari/537.36", ]; const WEB_EXPECTED_RESULT = [ @@ -94,10 +96,6 @@ const WEB_EXPECTED_RESULT = [ makeDeviceExtendedInfo(DeviceType.Web, "Apple Macintosh", "Mac OS", "Safari", "8.0.3"), makeDeviceExtendedInfo(DeviceType.Web, undefined, "Windows", "Firefox", "40.0"), makeDeviceExtendedInfo(DeviceType.Web, undefined, "Windows", "Edge", "12.246"), - // using mobile browser - makeDeviceExtendedInfo(DeviceType.Web, "Apple iPad", "iOS", "Mobile Safari", "8.0"), - makeDeviceExtendedInfo(DeviceType.Web, "Apple iPhone", "iOS", "Mobile Safari", "8.0"), - makeDeviceExtendedInfo(DeviceType.Web, "Samsung SM-G973U", "Android", "Chrome", "69.0.3497.100"), ]; const MISC_UA = [ diff --git a/yarn.lock b/yarn.lock index 4ad49f2a73..6a1e4a487e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3562,7 +3562,7 @@ braces@^3.0.3, braces@~3.0.2: dependencies: fill-range "^7.1.1" -browserslist@^4.22.2, browserslist@^4.23.0, browserslist@^4.23.1: +browserslist@^4.22.2, browserslist@^4.23.0, browserslist@^4.23.1, browserslist@^4.23.2: version "4.23.2" resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.23.2.tgz#244fe803641f1c19c28c48c4b6ec9736eb3d32ed" integrity sha512-qkqSyistMYdxAcw+CzbZwlBy8AGmS/eEWs+sEV5TnLRGDOL+C5M2EnH6tlZyg0YoAxGJAFKh61En9BR941GnHA== @@ -3627,7 +3627,7 @@ camelcase@^6.2.0: resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.3.0.tgz#5685b95eb209ac9c0c177467778c9c84df58ba9a" integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA== -caniuse-lite@^1.0.30001640: +caniuse-lite@1.0.30001643, caniuse-lite@^1.0.30001640: version "1.0.30001643" resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001643.tgz#9c004caef315de9452ab970c3da71085f8241dbd" integrity sha512-ERgWGNleEilSrHM6iUz/zJNSQTP8Mr21wDWpdgvRwcTXGAq6jMtOUPP4dqFPTdKqZ2wKTdtB+uucZ3MRpAUSmg== @@ -4278,10 +4278,10 @@ ee-first@1.1.1: resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" integrity sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow== -electron-to-chromium@^1.4.820: - version "1.5.0" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.5.0.tgz#0d3123a9f09189b9c7ab4b5d6848d71b3c1fd0e8" - integrity sha512-Vb3xHHYnLseK8vlMJQKJYXJ++t4u1/qJ3vykuVrVjvdiOEhYyT1AuP4x03G8EnPmYvYOhe9T+dADTmthjRQMkA== +electron-to-chromium@1.5.2, electron-to-chromium@^1.4.820, electron-to-chromium@^1.5.2: + version "1.5.2" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.5.2.tgz#6126ad229ce45e781ec54ca40db0504787f23d19" + integrity sha512-kc4r3U3V3WLaaZqThjYz/Y6z8tJe+7K0bbjUVo3i+LWIypVdMx5nXCkwRe6SWbY6ILqLdc1rKcKmr3HoH7wjSQ== emittery@^0.13.1: version "0.13.1"