Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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)
}
}
Expand All @@ -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
Expand Down Expand Up @@ -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))
}
}

Expand All @@ -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")
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<SuspiciousAppInfo>) {
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)
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<RaspExecutionStateEvent>()

private var isAppInForeground = false
private var isListenerRegistered = false

fun registerListener() {
fun registerListener(newListener: PluginExecutionStateListener) {
listener = newListener
isListenerRegistered = true
isAppInForeground = true
flushCache()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<ThreatEvent>()
private val malwareCache = mutableSetOf<SuspiciousAppInfo>()

private var isAppInForeground = false
private var isListenerRegistered = false

fun registerListener() {
fun registerListener(newListener: PluginThreatListener) {
listener = newListener
isListenerRegistered = true
isAppInForeground = true
flushCache()
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -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",
Expand Down
Loading