From 3d7c0361db1e7406f4018983a296970fcc257c64 Mon Sep 17 00:00:00 2001 From: Tomas Psota Date: Mon, 16 Mar 2026 03:50:20 +0100 Subject: [PATCH 1/2] feat: make dispatchers singletons --- .../FreeraspReactNativeModule.kt | 27 +++++----- .../PluginThreatHandler.kt | 53 +++++++++---------- .../dispatchers/ExecutionStateDispatcher.kt | 6 ++- .../dispatchers/ThreatDispatcher.kt | 6 ++- 4 files changed, 48 insertions(+), 44 deletions(-) diff --git a/android/src/main/java/com/freeraspreactnative/FreeraspReactNativeModule.kt b/android/src/main/java/com/freeraspreactnative/FreeraspReactNativeModule.kt index ccb94cf..0f1869a 100644 --- a/android/src/main/java/com/freeraspreactnative/FreeraspReactNativeModule.kt +++ b/android/src/main/java/com/freeraspreactnative/FreeraspReactNativeModule.kt @@ -20,6 +20,8 @@ import com.facebook.react.bridge.UiThreadUtil.runOnUiThread import com.facebook.react.bridge.WritableArray import com.facebook.react.bridge.WritableMap import com.facebook.react.modules.core.DeviceEventManagerModule +import com.freeraspreactnative.dispatchers.ExecutionStateDispatcher +import com.freeraspreactnative.dispatchers.ThreatDispatcher import com.freeraspreactnative.events.RaspExecutionStateEvent import com.freeraspreactnative.events.ThreatEvent import com.freeraspreactnative.interfaces.PluginExecutionStateListener @@ -37,22 +39,22 @@ class FreeraspReactNativeModule(private val reactContext: ReactApplicationContex private val lifecycleListener = object : LifecycleEventListener { override fun onHostResume() { - PluginThreatHandler.threatDispatcher.onResume() - PluginThreatHandler.executionStateDispatcher.onResume() + ThreatDispatcher.onResume() + ExecutionStateDispatcher.onResume() if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) { reactContext.currentActivity?.let { ScreenProtector.register(it) } } } override fun onHostPause() { - PluginThreatHandler.threatDispatcher.onPause() - PluginThreatHandler.executionStateDispatcher.onPause() + ThreatDispatcher.onPause() + ExecutionStateDispatcher.onPause() if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) { reactContext.currentActivity?.let { ScreenProtector.unregister(it) } } if (reactContext.currentActivity?.isFinishing == true) { - PluginThreatHandler.threatDispatcher.unregisterListener() - PluginThreatHandler.executionStateDispatcher.unregisterListener() + ThreatDispatcher.unregisterListener() + ExecutionStateDispatcher.unregisterListener() PluginThreatHandler.unregisterSDKListener(reactContext) } } @@ -66,10 +68,11 @@ class FreeraspReactNativeModule(private val reactContext: ReactApplicationContex return NAME } - init { + override fun initialize() { reactContext.addLifecycleEventListener(lifecycleListener) initializeEventKeys() - PluginThreatHandler.initializeDispatchers(PluginListener(reactContext)) + PluginThreatHandler.initializePluginListener(PluginListener(reactContext)) + super.initialize() } @ReactMethod @@ -162,10 +165,10 @@ class FreeraspReactNativeModule(private val reactContext: ReactApplicationContex @ReactMethod fun addListener(eventName: String) { if (eventName == ThreatEvent.CHANNEL_NAME) { - PluginThreatHandler.threatDispatcher.registerListener() + ThreatDispatcher.registerListener(PluginListener(reactContext)) } if (eventName == RaspExecutionStateEvent.CHANNEL_NAME) { - PluginThreatHandler.executionStateDispatcher.registerListener() + ExecutionStateDispatcher.registerListener(PluginListener(reactContext)) } } @@ -178,10 +181,10 @@ class FreeraspReactNativeModule(private val reactContext: ReactApplicationContex @ReactMethod fun removeListenerForEvent(eventName: String, promise: Promise) { if (eventName == ThreatEvent.CHANNEL_NAME) { - PluginThreatHandler.threatDispatcher.unregisterListener() + ThreatDispatcher.unregisterListener() } if (eventName == RaspExecutionStateEvent.CHANNEL_NAME) { - PluginThreatHandler.executionStateDispatcher.unregisterListener() + ExecutionStateDispatcher.unregisterListener() } promise.resolve("Listener unregistered") } diff --git a/android/src/main/java/com/freeraspreactnative/PluginThreatHandler.kt b/android/src/main/java/com/freeraspreactnative/PluginThreatHandler.kt index 46b7f9c..d3e06c3 100644 --- a/android/src/main/java/com/freeraspreactnative/PluginThreatHandler.kt +++ b/android/src/main/java/com/freeraspreactnative/PluginThreatHandler.kt @@ -10,107 +10,104 @@ import com.freeraspreactnative.events.ThreatEvent internal object PluginThreatHandler { - internal lateinit var threatDispatcher: ThreatDispatcher - internal lateinit var executionStateDispatcher: ExecutionStateDispatcher - - fun initializeDispatchers(listener: FreeraspReactNativeModule.PluginListener) { - threatDispatcher = ThreatDispatcher(listener) - executionStateDispatcher = ExecutionStateDispatcher(listener) + fun initializePluginListener(listener: FreeraspReactNativeModule.PluginListener) { + ThreatDispatcher.listener = listener + ExecutionStateDispatcher.listener = listener } private val threatDetected = object : ThreatListener.ThreatDetected() { override fun onRootDetected() { - threatDispatcher.dispatchThreat(ThreatEvent.PrivilegedAccess) + ThreatDispatcher.dispatchThreat(ThreatEvent.PrivilegedAccess) } override fun onDebuggerDetected() { - threatDispatcher.dispatchThreat(ThreatEvent.Debug) + ThreatDispatcher.dispatchThreat(ThreatEvent.Debug) } override fun onEmulatorDetected() { - threatDispatcher.dispatchThreat(ThreatEvent.Simulator) + ThreatDispatcher.dispatchThreat(ThreatEvent.Simulator) } override fun onTamperDetected() { - threatDispatcher.dispatchThreat(ThreatEvent.AppIntegrity) + ThreatDispatcher.dispatchThreat(ThreatEvent.AppIntegrity) } override fun onUntrustedInstallationSourceDetected() { - threatDispatcher.dispatchThreat(ThreatEvent.UnofficialStore) + ThreatDispatcher.dispatchThreat(ThreatEvent.UnofficialStore) } override fun onHookDetected() { - threatDispatcher.dispatchThreat(ThreatEvent.Hooks) + ThreatDispatcher.dispatchThreat(ThreatEvent.Hooks) } override fun onDeviceBindingDetected() { - threatDispatcher.dispatchThreat(ThreatEvent.DeviceBinding) + ThreatDispatcher.dispatchThreat(ThreatEvent.DeviceBinding) } override fun onObfuscationIssuesDetected() { - threatDispatcher.dispatchThreat(ThreatEvent.ObfuscationIssues) + ThreatDispatcher.dispatchThreat(ThreatEvent.ObfuscationIssues) } override fun onMalwareDetected(suspiciousAppInfos: MutableList) { - threatDispatcher.dispatchMalware(suspiciousAppInfos ?: mutableListOf()) + ThreatDispatcher.dispatchMalware(suspiciousAppInfos ?: mutableListOf()) } override fun onScreenshotDetected() { - threatDispatcher.dispatchThreat(ThreatEvent.Screenshot) + ThreatDispatcher.dispatchThreat(ThreatEvent.Screenshot) } override fun onScreenRecordingDetected() { - threatDispatcher.dispatchThreat(ThreatEvent.ScreenRecording) + ThreatDispatcher.dispatchThreat(ThreatEvent.ScreenRecording) } override fun onMultiInstanceDetected() { - threatDispatcher.dispatchThreat(ThreatEvent.MultiInstance) + ThreatDispatcher.dispatchThreat(ThreatEvent.MultiInstance) } override fun onUnsecureWifiDetected() { - threatDispatcher.dispatchThreat(ThreatEvent.UnsecureWifi) + ThreatDispatcher.dispatchThreat(ThreatEvent.UnsecureWifi) } override fun onTimeSpoofingDetected() { - threatDispatcher.dispatchThreat(ThreatEvent.TimeSpoofing) + ThreatDispatcher.dispatchThreat(ThreatEvent.TimeSpoofing) } override fun onLocationSpoofingDetected() { - threatDispatcher.dispatchThreat(ThreatEvent.LocationSpoofing) + ThreatDispatcher.dispatchThreat(ThreatEvent.LocationSpoofing) } override fun onAutomationDetected() { - threatDispatcher.dispatchThreat(ThreatEvent.Automation) + ThreatDispatcher.dispatchThreat(ThreatEvent.Automation) } } private val deviceState = object : ThreatListener.DeviceState() { override fun onUnlockedDeviceDetected() { - threatDispatcher.dispatchThreat(ThreatEvent.Passcode) + ThreatDispatcher.dispatchThreat(ThreatEvent.Passcode) } override fun onHardwareBackedKeystoreNotAvailableDetected() { - threatDispatcher.dispatchThreat(ThreatEvent.SecureHardwareNotAvailable) + ThreatDispatcher.dispatchThreat(ThreatEvent.SecureHardwareNotAvailable) } override fun onDeveloperModeDetected() { - threatDispatcher.dispatchThreat(ThreatEvent.DevMode) + ThreatDispatcher.dispatchThreat(ThreatEvent.DevMode) } override fun onADBEnabledDetected() { - threatDispatcher.dispatchThreat(ThreatEvent.ADBEnabled) + ThreatDispatcher.dispatchThreat(ThreatEvent.ADBEnabled) } override fun onSystemVPNDetected() { - threatDispatcher.dispatchThreat(ThreatEvent.SystemVPN) + ThreatDispatcher.dispatchThreat(ThreatEvent.SystemVPN) } } private val raspExecutionState = object : ThreatListener.RaspExecutionState() { override fun onAllChecksFinished() { - executionStateDispatcher.dispatch(RaspExecutionStateEvent.AllChecksFinished) + ExecutionStateDispatcher.dispatch(RaspExecutionStateEvent.AllChecksFinished) } } diff --git a/android/src/main/java/com/freeraspreactnative/dispatchers/ExecutionStateDispatcher.kt b/android/src/main/java/com/freeraspreactnative/dispatchers/ExecutionStateDispatcher.kt index 6efc075..20b11af 100644 --- a/android/src/main/java/com/freeraspreactnative/dispatchers/ExecutionStateDispatcher.kt +++ b/android/src/main/java/com/freeraspreactnative/dispatchers/ExecutionStateDispatcher.kt @@ -3,13 +3,15 @@ package com.freeraspreactnative.dispatchers import com.freeraspreactnative.events.RaspExecutionStateEvent import com.freeraspreactnative.interfaces.PluginExecutionStateListener -internal class ExecutionStateDispatcher(private val listener: PluginExecutionStateListener) { +internal object ExecutionStateDispatcher { + lateinit var listener: PluginExecutionStateListener private val cache = mutableSetOf() private var isAppInForeground = false private var isListenerRegistered = false - fun registerListener() { + fun registerListener(newListener: PluginExecutionStateListener) { + listener = newListener isListenerRegistered = true isAppInForeground = true flushCache() diff --git a/android/src/main/java/com/freeraspreactnative/dispatchers/ThreatDispatcher.kt b/android/src/main/java/com/freeraspreactnative/dispatchers/ThreatDispatcher.kt index 90b8aa9..f2282d0 100644 --- a/android/src/main/java/com/freeraspreactnative/dispatchers/ThreatDispatcher.kt +++ b/android/src/main/java/com/freeraspreactnative/dispatchers/ThreatDispatcher.kt @@ -4,14 +4,16 @@ import com.aheaditec.talsec_security.security.api.SuspiciousAppInfo import com.freeraspreactnative.events.ThreatEvent import com.freeraspreactnative.interfaces.PluginThreatListener -internal class ThreatDispatcher(private val listener: PluginThreatListener) { +internal object ThreatDispatcher { + lateinit var listener: PluginThreatListener private val threatCache = mutableSetOf() private val malwareCache = mutableSetOf() private var isAppInForeground = false private var isListenerRegistered = false - fun registerListener() { + fun registerListener(newListener: PluginThreatListener) { + listener = newListener isListenerRegistered = true isAppInForeground = true flushCache() From ab0bd8671a4cec19544988b0762bc1cc6f1bbc71 Mon Sep 17 00:00:00 2001 From: Tomas Psota Date: Mon, 16 Mar 2026 03:55:16 +0100 Subject: [PATCH 2/2] release: freeRASP 4.5.1 --- CHANGELOG.md | 11 +++++++++++ package.json | 2 +- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 752e04d..af61455 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,17 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [4.5.1] - 2026-03-16 + +- Android SDK version: 18.0.4 +- iOS SDK version: 6.14.1 + +### React Native + +#### Fixed + +- Fixed case where event dispatchers on Android were not initialized before calling onResume at the app launch + ## [4.5.0] - 2026-03-03 - Android SDK version: 18.0.4 diff --git a/package.json b/package.json index d4d07dd..6d904a1 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "freerasp-react-native", - "version": "4.5.0", + "version": "4.5.1", "description": "React Native plugin for improving app security and threat monitoring on Android and iOS mobile devices.", "main": "lib/commonjs/index", "module": "lib/module/index",