Skip to content

Commit 30edeb2

Browse files
US-2039 [Security] Prevent user from taking screenshot of the mnemonic (#828)
* feat: prevent screenshoting of menmonic * test: mock react-native-screenshot-prevent
1 parent 3347032 commit 30edeb2

File tree

8 files changed

+78
-6
lines changed

8 files changed

+78
-6
lines changed

ios/Podfile.lock

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -500,6 +500,8 @@ PODS:
500500
- RNScreens (3.18.2):
501501
- React-Core
502502
- React-RCTImage
503+
- RNScreenshotPrevent (1.1.9):
504+
- React
503505
- RNSVG (12.4.4):
504506
- React-Core
505507
- RNVectorIcons (9.2.0):
@@ -599,6 +601,7 @@ DEPENDENCIES:
599601
- RNQrGenerator (from `../node_modules/rn-qr-generator`)
600602
- RNReanimated (from `../node_modules/react-native-reanimated`)
601603
- RNScreens (from `../node_modules/react-native-screens`)
604+
- RNScreenshotPrevent (from `../node_modules/react-native-screenshot-prevent`)
602605
- RNSVG (from `../node_modules/react-native-svg`)
603606
- RNVectorIcons (from `../node_modules/react-native-vector-icons`)
604607
- vision-camera-code-scanner (from `../node_modules/vision-camera-code-scanner`)
@@ -746,6 +749,8 @@ EXTERNAL SOURCES:
746749
:path: "../node_modules/react-native-reanimated"
747750
RNScreens:
748751
:path: "../node_modules/react-native-screens"
752+
RNScreenshotPrevent:
753+
:path: "../node_modules/react-native-screenshot-prevent"
749754
RNSVG:
750755
:path: "../node_modules/react-native-svg"
751756
RNVectorIcons:
@@ -838,6 +843,7 @@ SPEC CHECKSUMS:
838843
RNQrGenerator: 90461ba3ca88c1d38ef73da50fade35d9648215d
839844
RNReanimated: bec7736122a268883bdede07f1bf9cf4b40158db
840845
RNScreens: 34cc502acf1b916c582c60003dc3089fa01dc66d
846+
RNScreenshotPrevent: cdfbe213203774c7486c6a2f198af8a4ebe10c47
841847
RNSVG: ecd661f380a07ba690c9c5929c475a44f432d674
842848
RNVectorIcons: fcc2f6cb32f5735b586e66d14103a74ce6ad61f8
843849
SocketRocket: f32cd54efbe0f095c4d7594881e52619cfe80b17
@@ -850,4 +856,4 @@ SPEC CHECKSUMS:
850856

851857
PODFILE CHECKSUM: 078a56513c6ebe1366f300f18d66914e074eca6f
852858

853-
COCOAPODS: 1.13.0
859+
COCOAPODS: 1.12.0

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@
8888
"react-native-reanimated-carousel": "^3.3.0",
8989
"react-native-safe-area-context": "^4.4.1",
9090
"react-native-screens": "^3.18.2",
91+
"react-native-screenshot-prevent": "^1.1.9",
9192
"react-native-snap-carousel": "^3.9.1",
9293
"react-native-ssl-public-key-pinning": "^1.1.3",
9394
"react-native-svg": "^12.1.1",

src/lib/i18n.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -388,6 +388,9 @@ const resources = {
388388
ramp_error_title: 'Not Available',
389389
ramp_error: 'Adding funds is not available yet.',
390390
wallet_deployment_label: 'Rif Wallet Deployment',
391+
wallet_backup_title: 'Warning!',
392+
wallet_backup_message:
393+
'We have disabled the ability to take a picture of the mnemonic because it is important that you keep it private. Please write it down instead',
391394
},
392395
},
393396
es: {

src/screens/createKeys/new/NewMasterKeyScreen.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ import {
2121
RootTabsScreenProps,
2222
} from 'navigation/rootNavigator'
2323
import { sharedColors, sharedStyles } from 'shared/constants'
24-
import { castStyle } from 'shared/utils'
24+
import { castStyle, usePreventScreenshot } from 'shared/utils'
2525
import { useAppDispatch } from 'store/storeUtils'
2626
import { createWallet } from 'store/slices/settingsSlice'
2727
import { saveKeyVerificationReminder } from 'storage/MainStorage'
@@ -39,6 +39,7 @@ export const NewMasterKeyScreen = ({ navigation }: Props) => {
3939
const initializeWallet = useInitializeWallet()
4040
const dispatch = useAppDispatch()
4141
const { t } = useTranslation()
42+
usePreventScreenshot(t)
4243
const mnemonic = useMemo(() => KeyManagementSystem.create().mnemonic, [])
4344
const mnemonicArray = mnemonic ? mnemonic.split(' ') : []
4445
const [isMnemonicVisible, setIsMnemonicVisible] = useState(false)

src/screens/settings/WalletBackup.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,15 +15,16 @@ import {
1515
SettingsScreenProps,
1616
settingsStackRouteNames,
1717
} from 'navigation/settingsNavigator/types'
18-
import { castStyle } from 'shared/utils'
18+
import { castStyle, usePreventScreenshot } from 'shared/utils'
1919
import { getKeys } from 'storage/SecureStorage'
2020
import { DeleteWalletModal } from 'components/modal/deleteWalletModal'
21-
import { getCurrentChainId } from 'src/storage/ChainStorage'
21+
import { getCurrentChainId } from 'storage/ChainStorage'
2222

2323
type Props = SettingsScreenProps<settingsStackRouteNames.WalletBackup>
2424

2525
export const WalletBackup = (_: Props) => {
2626
const { t } = useTranslation()
27+
usePreventScreenshot(t)
2728
const [isDeleteConfirmationVisible, setIsDeleteConfirmationVisible] =
2829
useState<boolean>(false)
2930

src/shared/utils/index.ts

Lines changed: 50 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,19 @@
1-
import { createRef } from 'react'
2-
import { ImageStyle, TextInput, TextStyle, ViewStyle } from 'react-native'
1+
import { createRef, useEffect } from 'react'
2+
import {
3+
Alert,
4+
ImageStyle,
5+
TextInput,
6+
TextStyle,
7+
ViewStyle,
8+
} from 'react-native'
9+
import {
10+
addListener,
11+
enabled,
12+
enableSecureView,
13+
disableSecureView,
14+
} from 'react-native-screenshot-prevent'
15+
import { useTranslation } from 'react-i18next'
16+
import { useIsFocused } from '@react-navigation/native'
317

418
import { ErrorWithMessage } from '../types'
519

@@ -35,3 +49,37 @@ export const getRandomNumber = (max: number, min: number) => {
3549
max = Math.floor(max)
3650
return Math.floor(Math.random() * (max - min + 1)) + min
3751
}
52+
53+
export const usePreventScreenshot = (
54+
t: ReturnType<typeof useTranslation>['t'],
55+
) => {
56+
const isFocused = useIsFocused()
57+
58+
useEffect(() => {
59+
const subs = addListener(() => {
60+
Alert.alert(t('wallet_backup_title'), t('wallet_backup_message'), [
61+
{ text: t('ok') },
62+
])
63+
})
64+
65+
console.log('CREATE SUBS', subs)
66+
67+
return () => {
68+
subs.remove()
69+
}
70+
}, [t])
71+
72+
useEffect(() => {
73+
if (isFocused) {
74+
console.log('ENABLE')
75+
enabled(true)
76+
enableSecureView()
77+
return
78+
}
79+
80+
console.log('DISABLE')
81+
82+
enabled(false)
83+
disableSecureView()
84+
}, [isFocused])
85+
}

testLib/setupTests.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,3 +19,10 @@ jest.mock('redux-persist', () => ({
1919
jest.mock('react-native-bootsplash', () => ({
2020
hide: jest.fn(),
2121
}))
22+
23+
jest.mock('react-native-screenshot-prevent', () => ({
24+
addListener: jest.fn(),
25+
enabled: jest.fn(),
26+
enableSecureView: jest.fn(),
27+
disableSecureView: jest.fn(),
28+
}))

yarn.lock

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9464,6 +9464,11 @@ react-native-screens@^3.18.2:
94649464
react-freeze "^1.0.0"
94659465
warn-once "^0.1.0"
94669466

9467+
react-native-screenshot-prevent@^1.1.9:
9468+
version "1.1.9"
9469+
resolved "https://registry.yarnpkg.com/react-native-screenshot-prevent/-/react-native-screenshot-prevent-1.1.9.tgz#937f41cd685c12c1e0ef20c8020a93600790cc92"
9470+
integrity sha512-VGhW3MkuHw3fK9nJD4ijC8truQXv0pSDj9x+mmoA5xTNIeuSoFSfFhXZG+WFcFknK54iIniFLMoGToKuKXkB3g==
9471+
94679472
react-native-snap-carousel@^3.9.1:
94689473
version "3.9.1"
94699474
resolved "https://registry.yarnpkg.com/react-native-snap-carousel/-/react-native-snap-carousel-3.9.1.tgz#6fd9bd8839546c2c6043a41d2035afbc6fe0443e"

0 commit comments

Comments
 (0)