diff --git a/example/src/App.tsx b/example/src/App.tsx index 241924b..cf33793 100644 --- a/example/src/App.tsx +++ b/example/src/App.tsx @@ -1,54 +1,315 @@ import React from 'react'; -import { Text, View, StyleSheet } from 'react-native'; +import { + ScrollView, + Text, + TouchableOpacity, + View, + StyleSheet, + Platform, +} from 'react-native'; import { isRooted, + isEmulator, + isDebuggerAttached, + isDeveloperModeEnabled, + isHooked, + isVPNDetected, + getRootReasons, + authenticateWithBiometrics, + getBiometricStrength, + addSSLPinning, preventScreenshot, + protectClipboard, setSecureString, getSecureString, -} from 'react-native-shield'; - -const result = isRooted(); + removeSecureString, + getAllSecureKeys, + clearAllSecureStorage, + requestIntegrityToken, +} from '@think-grid-labs/react-native-shield'; -// Example Pinning (would normally be called early in app lifecycle) -// addSSLPinning('google.com', ['sha256/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=']); +type ResultMap = Record; export default function App() { - const [storageStatus, setStorageStatus] = React.useState( - 'Storage: Waiting...' + const [results, setResults] = React.useState({}); + + const set = (key: string, value: string) => + setResults((prev) => ({ ...prev, [key]: value })); + + // ─── Device Integrity ────────────────────────────────────────────────────── + + const runIntegrityChecks = () => { + set('rooted', isRooted() ? '🚨 YES' : '✅ NO'); + set('emulator', isEmulator() ? '🚨 YES' : '✅ NO'); + set('debugger', isDebuggerAttached() ? '🚨 YES' : '✅ NO'); + set('devMode', isDeveloperModeEnabled() ? '🚨 YES' : '✅ NO'); + set('hooked', isHooked() ? '🚨 YES' : '✅ NO'); + set('vpn', isVPNDetected() ? '⚠️ YES' : '✅ NO'); + + const reasons = getRootReasons(); + set( + 'rootReasons', + reasons.length > 0 ? `🚨 ${reasons.join(', ')}` : '✅ None' + ); + }; + + // ─── Platform Attestation ────────────────────────────────────────────────── + + const runAttestation = async () => { + set('attestation', '⏳ Requesting...'); + try { + // In production: fetch this nonce from your server + const nonce = `nonce-${Date.now()}`; + const token = await requestIntegrityToken(nonce); + set('attestation', `✅ Token (${token.slice(0, 20)}...)`); + } catch (e: any) { + set('attestation', `⚠️ ${e?.message ?? 'Unsupported'}`); + } + }; + + // ─── Biometrics ──────────────────────────────────────────────────────────── + + const runBiometrics = async () => { + set('biometricStrength', '⏳ Checking...'); + const strength = await getBiometricStrength(); + set('biometricStrength', `Strength: ${strength}`); + + set('biometricAuth', '⏳ Authenticating...'); + const success = await authenticateWithBiometrics( + 'Authenticate to test Shield' + ); + set( + 'biometricAuth', + success ? '✅ Authenticated' : '❌ Failed / Cancelled' + ); + }; + + // ─── SSL Pinning ─────────────────────────────────────────────────────────── + + const runSSLPinning = async () => { + set('sslPinning', '⏳ Configuring...'); + try { + await addSSLPinning('httpbin.org', [ + 'sha256/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=', // placeholder + ]); + set('sslPinning', '✅ Pinning active for httpbin.org'); + } catch (e: any) { + set('sslPinning', `❌ ${e?.message}`); + } + }; + + // ─── UI Privacy ──────────────────────────────────────────────────────────── + + const toggleScreenshot = async () => { + const current = results.screenshot; + const enable = current !== '🔒 Protected'; + await preventScreenshot(enable); + set('screenshot', enable ? '🔒 Protected' : '🔓 Unprotected'); + }; + + const toggleClipboard = async () => { + const current = results.clipboard; + const enable = current !== '🔒 Protected'; + await protectClipboard(enable); + set('clipboard', enable ? '🔒 Protected' : '🔓 Unprotected'); + }; + + // ─── Secure Storage ──────────────────────────────────────────────────────── + + const runStorage = async () => { + set('storage', '⏳ Running...'); + try { + await setSecureString('access_token', 'secret_abc123'); + await setSecureString('refresh_token', 'refresh_xyz789'); + + const token = await getSecureString('access_token'); + const keys = await getAllSecureKeys(); + + await removeSecureString('refresh_token'); + const keysAfter = await getAllSecureKeys(); + + set( + 'storage', + `✅ Stored → Retrieved: "${token}" | Keys: [${keys.join( + ', ' + )}] → after delete: [${keysAfter.join(', ')}]` + ); + } catch (e: any) { + set('storage', `❌ ${e?.message}`); + } + }; + + const runClearStorage = async () => { + set('clearStorage', '⏳ Clearing...'); + try { + await clearAllSecureStorage(); + const keys = await getAllSecureKeys(); + set('clearStorage', `✅ Cleared — Keys remaining: ${keys.length}`); + } catch (e: any) { + set('clearStorage', `❌ ${e?.message}`); + } + }; + + // ─── Render ──────────────────────────────────────────────────────────────── + + return ( + + react-native-shield demo + Platform: {Platform.OS} + +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+
); +} - React.useEffect(() => { - preventScreenshot(true); - - // Test Secure Storage - const testStorage = async () => { - try { - await setSecureString('api_token', 'secret_12345'); - const val = await getSecureString('api_token'); - setStorageStatus(`Storage: Stored 'secret_12345', Retrieved '${val}'`); - } catch { - setStorageStatus('Storage: Failed'); - } - }; - testStorage(); - }, []); +// ─── Sub-components ────────────────────────────────────────────────────────── +function Section({ + title, + children, +}: { + title: string; + children: React.ReactNode; +}) { return ( - - Is Device Rooted/Jailbroken? {result ? 'YES' : 'NO'} - Privacy Mode: ACTIVE - {storageStatus} + + {title} + {children} ); } +function Button({ label, onPress }: { label: string; onPress: () => void }) { + return ( + + {label} + + ); +} + +function Result({ label, value }: { label: string; value?: string }) { + if (!value) return null; + return ( + + {label}: + {value} + + ); +} + +// ─── Styles ────────────────────────────────────────────────────────────────── + const styles = StyleSheet.create({ container: { - flex: 1, - alignItems: 'center', - justifyContent: 'center', + padding: 20, + paddingTop: 60, + backgroundColor: '#0f0f0f', + }, + header: { + fontSize: 22, + fontWeight: '700', + color: '#ffffff', + marginBottom: 4, + }, + platform: { + fontSize: 13, + color: '#888', + marginBottom: 24, + }, + section: { + marginBottom: 28, + borderWidth: 1, + borderColor: '#2a2a2a', + borderRadius: 12, + padding: 16, + backgroundColor: '#1a1a1a', + }, + sectionTitle: { + fontSize: 15, + fontWeight: '600', + color: '#a78bfa', + marginBottom: 12, + textTransform: 'uppercase', + letterSpacing: 0.8, + }, + button: { + backgroundColor: '#7c3aed', + paddingVertical: 10, + paddingHorizontal: 16, + borderRadius: 8, + marginBottom: 10, + alignSelf: 'flex-start', + }, + buttonText: { + color: '#ffffff', + fontSize: 13, + fontWeight: '600', + }, + result: { + flexDirection: 'row', + flexWrap: 'wrap', + marginBottom: 6, + gap: 4, + }, + resultLabel: { + fontSize: 12, + color: '#9ca3af', + fontWeight: '500', }, - text: { - marginTop: 20, + resultValue: { + fontSize: 12, + color: '#e5e7eb', + flexShrink: 1, }, }); diff --git a/tsconfig.json b/tsconfig.json index abf7d4c..30aafac 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -2,7 +2,8 @@ "compilerOptions": { "rootDir": ".", "paths": { - "react-native-shield": ["./src/index"] + "react-native-shield": ["./src/index"], + "@think-grid-labs/react-native-shield": ["./src/index"] }, "allowUnreachableCode": false, "allowUnusedLabels": false,