UNPKG

react-native-security-checker

Version:

A comprehensive React Native security checker that detects jailbreak, root, emulators, hooks, tampering, and other security threats

234 lines (206 loc) 6.98 kB
import { useEffect, useRef, useCallback, useState } from 'react'; import { AppState, type AppStateStatus } from 'react-native'; import { detectEnvironment, type SecurityCheckConfig, type SecurityCheckResult, } from './index'; export interface UseSecurityCheckerOptions { /** Configuration for security checks */ config?: SecurityCheckConfig; /** Interval in milliseconds for periodic checks (default: 5000) */ interval?: number; /** Whether to check when app goes to background (default: true) */ checkOnBackground?: boolean; /** Whether to check when app comes to foreground (default: true) */ checkOnForeground?: boolean; /** Whether to start monitoring immediately (default: true) */ startImmediately?: boolean; /** Whether to enable continuous monitoring (default: true) */ enableContinuousMonitoring?: boolean; } export interface SecurityCheckerState { /** Current security check result */ result: SecurityCheckResult | null; /** Whether a security check is currently running */ isChecking: boolean; /** Last error that occurred during checking */ lastError: Error | null; /** Whether monitoring is currently active */ isMonitoring: boolean; /** Number of checks performed */ checkCount: number; /** Timestamp of last check */ lastCheckTime: number | null; } export interface UseSecurityCheckerReturn { /** Current state of the security checker */ state: SecurityCheckerState; /** Manually trigger a security check */ checkSecurity: () => Promise<void>; /** Start monitoring */ startMonitoring: () => void; /** Stop monitoring */ stopMonitoring: () => void; /** Reset the checker state */ reset: () => void; } /** * Custom hook for continuous security monitoring * * @param onSecurityIssue - Callback function called when security issues are detected * @param interval - Interval in milliseconds for periodic checks (default: 5000) * @param options - Additional configuration options * @returns Security checker state and control functions */ export function useSecurityChecker( onSecurityIssue: (result: SecurityCheckResult) => void, interval: number = 5000, options: UseSecurityCheckerOptions = {} ): UseSecurityCheckerReturn { const { config, checkOnBackground = true, checkOnForeground = true, startImmediately = true, enableContinuousMonitoring = true, } = options; // State const [state, setState] = useState<SecurityCheckerState>({ result: null, isChecking: false, lastError: null, isMonitoring: false, checkCount: 0, lastCheckTime: null, }); // Refs for cleanup const intervalRef = useRef<NodeJS.Timeout | null>(null); const appStateRef = useRef<AppStateStatus | null>( AppState.currentState as AppStateStatus | null ); const isCheckingRef = useRef<boolean>(false); const isMonitoringRef = useRef<boolean>(false); const startMonitoringRef = useRef<() => void>(() => {}); const stopMonitoringRef = useRef<() => void>(() => {}); const handleAppStateChangeRef = useRef< (nextAppState: AppStateStatus) => void >(() => {}); // Security check function const checkSecurity = useCallback(async () => { if (isCheckingRef.current) return; // Prevent multiple simultaneous checks isCheckingRef.current = true; setState((prev) => ({ ...prev, isChecking: true, lastError: null })); try { const result = await detectEnvironment(config); setState((prev) => ({ ...prev, result, isChecking: false, checkCount: prev.checkCount + 1, lastCheckTime: Date.now(), })); // Call callback if security issues are detected if (result.isInsecureEnvironment) { onSecurityIssue(result); } } catch (error) { const errorObj = error instanceof Error ? error : new Error(String(error)); setState((prev) => ({ ...prev, isChecking: false, lastError: errorObj, })); } finally { isCheckingRef.current = false; } }, [config, onSecurityIssue]); // Start monitoring const startMonitoring = useCallback(() => { if (isMonitoringRef.current) return; // Prevent multiple starts isMonitoringRef.current = true; setState((prev) => ({ ...prev, isMonitoring: true })); // Start interval if continuous monitoring is enabled if (enableContinuousMonitoring && interval > 0) { intervalRef.current = setInterval(checkSecurity, interval); } }, [enableContinuousMonitoring, interval, checkSecurity]); // Store the latest function reference startMonitoringRef.current = startMonitoring; // Stop monitoring const stopMonitoring = useCallback(() => { isMonitoringRef.current = false; setState((prev) => ({ ...prev, isMonitoring: false })); if (intervalRef.current) { clearInterval(intervalRef.current); intervalRef.current = null; } }, []); // Store the latest function reference stopMonitoringRef.current = stopMonitoring; // Reset state const reset = useCallback(() => { stopMonitoring(); isCheckingRef.current = false; setState({ result: null, isChecking: false, lastError: null, isMonitoring: false, checkCount: 0, lastCheckTime: null, }); }, [stopMonitoring]); // App state change handler const handleAppStateChange = useCallback( (nextAppState: AppStateStatus) => { const currentState = appStateRef.current; appStateRef.current = nextAppState; // Check on background if ( checkOnBackground && currentState === 'active' && nextAppState === 'background' ) { checkSecurity(); } // Check on foreground if ( checkOnForeground && currentState === 'background' && nextAppState === 'active' ) { checkSecurity(); } }, [checkOnBackground, checkOnForeground, checkSecurity] ); // Store the latest function reference handleAppStateChangeRef.current = handleAppStateChange; // Setup effects - single useEffect with no dependencies to prevent loops useEffect(() => { // Start monitoring if enabled if (startImmediately) { startMonitoringRef.current?.(); } // Setup app state listener const appStateSubscription = AppState.addEventListener( 'change', (nextAppState: AppStateStatus) => { handleAppStateChangeRef.current?.(nextAppState); } ); return () => { appStateSubscription?.remove(); stopMonitoringRef.current?.(); }; }, []); // Empty dependency array - only run once return { state, checkSecurity, startMonitoring, stopMonitoring, reset, }; }