diff --git a/packages/suite/src/components/suite/Preloader/Preloader.tsx b/packages/suite/src/components/suite/Preloader/Preloader.tsx index 20489e1a8eb..86d61a1a38e 100644 --- a/packages/suite/src/components/suite/Preloader/Preloader.tsx +++ b/packages/suite/src/components/suite/Preloader/Preloader.tsx @@ -1,8 +1,6 @@ import { FC, PropsWithChildren, useEffect } from 'react'; -import { Feature, selectIsFeatureDisabled } from '@suite-common/message-system'; import { - selectIsEntropyCheckFailed, selectIsFirmwareAuthenticityCheckDismissed, selectSelectedDevice, } from '@suite-common/wallet-core'; @@ -12,7 +10,7 @@ import { useGuideKeyboard } from 'src/hooks/guide'; import { useDispatch, useSelector } from 'src/hooks/suite'; import { useWindowVisibility } from 'src/hooks/suite/useWindowVisibility'; import { - selectIsEntropyCheckEnabled, + selectIsEntropyCheckEnabledAndFailed, selectIsFirmwareAuthenticityCheckEnabledAndHardFailed, selectIsLoggedOut, selectIsTransportInitialized, @@ -63,18 +61,14 @@ export const Preloader = ({ children }: PropsWithChildren) => { const isLoggedOut = useSelector(selectIsLoggedOut); const selectedDevice = useSelector(selectSelectedDevice); const { initialRun, viewOnlyPromoClosed } = useSelector(selectSuiteFlags); - const isFirmwareCheckFailed = useSelector( + const isFirmwareCheckEnabledAndFailed = useSelector( selectIsFirmwareAuthenticityCheckEnabledAndHardFailed, ); const isFirmwareAuthenticityCheckDismissed = useSelector( selectIsFirmwareAuthenticityCheckDismissed, ); - const isEntropyCheckEnabled = useSelector(selectIsEntropyCheckEnabled); - // Entropy check won't be performed if disabled but we also have to check it here to avoid showing the UI when the failed state is stored in database. - const isEntropyCheckDisabledByMessageSystem = useSelector(state => - selectIsFeatureDisabled(state, Feature.entropyCheck), - ); - const isEntropyCheckFailed = useSelector(selectIsEntropyCheckFailed); + // Entropy check won't be performed if disabled but we must also check it here to avoid showing the UI when the failed state is stored in database. + const isEntropyCheckEnabledAndFailed = useSelector(selectIsEntropyCheckEnabledAndFailed); // report firmware authenticity failures even when the UI is disabled useReportDeviceCompromised(); @@ -102,16 +96,13 @@ export const Preloader = ({ children }: PropsWithChildren) => { return ; } - const isEntropyCheckEnabledAndFailed = - isEntropyCheckEnabled && !isEntropyCheckDisabledByMessageSystem && isEntropyCheckFailed; - if ( (router.route?.app === undefined || !ROUTES_TO_SKIP_FIRMWARE_CHECK.includes(router.route?.app)) && - ((!isFirmwareAuthenticityCheckDismissed && isFirmwareCheckFailed) || + ((!isFirmwareAuthenticityCheckDismissed && isFirmwareCheckEnabledAndFailed) || isEntropyCheckEnabledAndFailed) ) { - return ; + return ; } if ( diff --git a/packages/suite/src/components/suite/SecurityCheck/DeviceCompromised.tsx b/packages/suite/src/components/suite/SecurityCheck/DeviceCompromised.tsx index ed0a4ea9582..c2e4a968489 100644 --- a/packages/suite/src/components/suite/SecurityCheck/DeviceCompromised.tsx +++ b/packages/suite/src/components/suite/SecurityCheck/DeviceCompromised.tsx @@ -7,22 +7,34 @@ import { useDevice, useDispatch, useSelector } from 'src/hooks/suite'; import { selectFirmwareHashCheckErrorIfEnabled, selectFirmwareRevisionCheckErrorIfEnabled, + selectIsEntropyCheckEnabledAndFailed, } from 'src/reducers/suite/suiteReducer'; import { SecurityCheckFail, SecurityCheckFailProps } from './SecurityCheckFail'; import { hardFailureChecklistItems, softFailureChecklistItems } from './checklistItems'; -const useSecurityCheckFailProps = ( - isEntropyCheckFailed: boolean, -): Partial => { +const useSecurityCheckFailProps = (): SecurityCheckFailProps => { + const { device } = useDevice(); const revisionCheckError = useSelector(selectFirmwareRevisionCheckErrorIfEnabled); const hashCheckError = useSelector(selectFirmwareHashCheckErrorIfEnabled); + const isEntropyCheckFailed = useSelector(selectIsEntropyCheckEnabledAndFailed); + const dispatch = useDispatch(); + + // Let user access the wallet if it may have been initiated before so that they can access the funds and send them to safety. + const goToSuite = () => { + // Condition to satisfy TypeScript, device.id is always defined at this point. + if (device?.id) { + dispatch(deviceActions.dismissFirmwareAuthenticityCheck(device.id)); + } + }; if (isEntropyCheckFailed) { return { heading: 'TR_DEVICE_COMPROMISED_HEADING', text: 'TR_DEVICE_COMPROMISED_ENTROPY_CHECK_TEXT', checklistItems: hardFailureChecklistItems, + goBack: undefined, + supportUrl: TREZOR_SUPPORT_URL, // TODO: add specific URL when it is created }; } // revision check has precedence over hash check, because it does not have the ambiguous other-error state @@ -31,6 +43,8 @@ const useSecurityCheckFailProps = ( heading: 'TR_DEVICE_COMPROMISED_HEADING', text: 'TR_DEVICE_COMPROMISED_FW_REVISION_CHECK_TEXT', checklistItems: hardFailureChecklistItems, + goBack: goToSuite, + supportUrl: TREZOR_SUPPORT_FW_REVISION_CHECK_FAILED_URL, }; } // hash check other-error shall display softer wording than standard hash check errors @@ -40,6 +54,8 @@ const useSecurityCheckFailProps = ( text: 'TR_FAILED_VERIFY_DEVICE_TEXT', checklistItems: softFailureChecklistItems, supportButtonVariant: 'warning', + goBack: goToSuite, + supportUrl: TREZOR_SUPPORT_FW_REVISION_CHECK_FAILED_URL, }; } if (hashCheckError !== null) { @@ -47,43 +63,22 @@ const useSecurityCheckFailProps = ( heading: 'TR_DEVICE_COMPROMISED_HEADING', text: 'TR_DEVICE_COMPROMISED_FW_HASH_CHECK_TEXT', checklistItems: hardFailureChecklistItems, + goBack: goToSuite, + supportUrl: TREZOR_SUPPORT_FW_REVISION_CHECK_FAILED_URL, }; } // should not happen, but default props will be used with no problem - return {}; -}; - -type DeviceCompromisedProps = { - isEntropyCheckFailed: boolean; + return { supportUrl: TREZOR_SUPPORT_FW_REVISION_CHECK_FAILED_URL }; }; -export const DeviceCompromised = ({ isEntropyCheckFailed }: DeviceCompromisedProps) => { - const dispatch = useDispatch(); - const { device } = useDevice(); - - const securityCheckFailProps = useSecurityCheckFailProps(isEntropyCheckFailed); - - const goToSuite = () => { - // Condition to satisfy TypeScript, device.id is always defined at this point. - if (device?.id) { - dispatch(deviceActions.dismissFirmwareAuthenticityCheck(device.id)); - } - }; +export const DeviceCompromised = () => { + const securityCheckFailProps = useSecurityCheckFailProps(); return ( - + ); diff --git a/packages/suite/src/reducers/suite/suiteReducer.ts b/packages/suite/src/reducers/suite/suiteReducer.ts index 50293f3609d..6715eb71b57 100644 --- a/packages/suite/src/reducers/suite/suiteReducer.ts +++ b/packages/suite/src/reducers/suite/suiteReducer.ts @@ -4,7 +4,12 @@ import { Feature, selectIsFeatureDisabled } from '@suite-common/message-system'; import { isDeviceAcquired } from '@suite-common/suite-utils'; import type { InvityServerEnvironment } from '@suite-common/trading'; import { NetworkSymbol } from '@suite-common/wallet-config'; -import { DeviceRootState, discoveryActions, selectSelectedDevice } from '@suite-common/wallet-core'; +import { + DeviceRootState, + discoveryActions, + selectIsEntropyCheckFailed, + selectSelectedDevice, +} from '@suite-common/wallet-core'; import { AddressDisplayOptions, WalletType } from '@suite-common/wallet-types'; import { ConnectSettings, InstallerInfo, TRANSPORT, TransportInfo } from '@trezor/connect'; import { isWeb } from '@trezor/env-utils'; @@ -577,4 +582,18 @@ export const selectIsFirmwareAuthenticityCheckEnabledAndHardFailed = (state: App return isRevisionHardError || isHashHardError; }; +/** + * Return true if entropy check has failed and is not disabled via settings nor message system. + */ +export const selectIsEntropyCheckEnabledAndFailed = (state: AppState) => { + const isEntropyCheckEnabled = selectIsEntropyCheckEnabled(state); + const isEntropyCheckDisabledByMessageSystem = selectIsFeatureDisabled( + state, + Feature.entropyCheck, + ); + const isEntropyCheckFailed = selectIsEntropyCheckFailed(state); + + return isEntropyCheckEnabled && !isEntropyCheckDisabledByMessageSystem && isEntropyCheckFailed; +}; + export default suiteReducer;