freerasp-react-native 4.3.1 → 4.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (63) hide show
  1. package/CHANGELOG.md +60 -0
  2. package/android/build.gradle +8 -2
  3. package/android/gradle.properties +2 -2
  4. package/android/src/main/java/com/freeraspreactnative/FreeraspReactNativeModule.kt +68 -22
  5. package/android/src/main/java/com/freeraspreactnative/PluginThreatHandler.kt +121 -0
  6. package/android/src/main/java/com/freeraspreactnative/dispatchers/ExecutionStateDispatcher.kt +38 -0
  7. package/android/src/main/java/com/freeraspreactnative/dispatchers/ThreatDispatcher.kt +61 -0
  8. package/android/src/main/java/com/freeraspreactnative/events/ThreatEvent.kt +3 -1
  9. package/android/src/main/java/com/freeraspreactnative/interfaces/PluginExecutionStateListener.kt +7 -0
  10. package/android/src/main/java/com/freeraspreactnative/interfaces/PluginThreatListener.kt +9 -0
  11. package/example/src/App.tsx +10 -0
  12. package/example/src/DemoApp.tsx +32 -0
  13. package/example/src/checks.ts +1 -0
  14. package/freerasp-react-native.podspec +5 -1
  15. package/ios/FreeraspReactNative.m +5 -0
  16. package/ios/FreeraspReactNative.swift +39 -10
  17. package/ios/dispatchers/ExecutionStateDispatcher.swift +35 -0
  18. package/ios/dispatchers/ThreatDispatcher.swift +36 -0
  19. package/lib/commonjs/api/listeners/raspExecutionState.js +28 -8
  20. package/lib/commonjs/api/listeners/raspExecutionState.js.map +1 -1
  21. package/lib/commonjs/api/listeners/threat.js +34 -10
  22. package/lib/commonjs/api/listeners/threat.js.map +1 -1
  23. package/lib/commonjs/api/methods/native.js +9 -1
  24. package/lib/commonjs/api/methods/native.js.map +1 -1
  25. package/lib/commonjs/api/methods/reactNative.js +13 -8
  26. package/lib/commonjs/api/methods/reactNative.js.map +1 -1
  27. package/lib/commonjs/channels/threat.js +1 -3
  28. package/lib/commonjs/channels/threat.js.map +1 -1
  29. package/lib/commonjs/models/threat.js +2 -1
  30. package/lib/commonjs/models/threat.js.map +1 -1
  31. package/lib/module/api/listeners/raspExecutionState.js +29 -9
  32. package/lib/module/api/listeners/raspExecutionState.js.map +1 -1
  33. package/lib/module/api/listeners/threat.js +35 -11
  34. package/lib/module/api/listeners/threat.js.map +1 -1
  35. package/lib/module/api/methods/native.js +6 -0
  36. package/lib/module/api/methods/native.js.map +1 -1
  37. package/lib/module/api/methods/reactNative.js +13 -7
  38. package/lib/module/api/methods/reactNative.js.map +1 -1
  39. package/lib/module/channels/threat.js +1 -3
  40. package/lib/module/channels/threat.js.map +1 -1
  41. package/lib/module/models/threat.js +2 -1
  42. package/lib/module/models/threat.js.map +1 -1
  43. package/lib/typescript/src/api/listeners/raspExecutionState.d.ts +1 -1
  44. package/lib/typescript/src/api/listeners/raspExecutionState.d.ts.map +1 -1
  45. package/lib/typescript/src/api/listeners/threat.d.ts +1 -1
  46. package/lib/typescript/src/api/listeners/threat.d.ts.map +1 -1
  47. package/lib/typescript/src/api/methods/native.d.ts +2 -0
  48. package/lib/typescript/src/api/methods/native.d.ts.map +1 -1
  49. package/lib/typescript/src/api/methods/reactNative.d.ts.map +1 -1
  50. package/lib/typescript/src/channels/threat.d.ts.map +1 -1
  51. package/lib/typescript/src/models/threat.d.ts +1 -0
  52. package/lib/typescript/src/models/threat.d.ts.map +1 -1
  53. package/lib/typescript/src/types/types.d.ts +1 -0
  54. package/lib/typescript/src/types/types.d.ts.map +1 -1
  55. package/package.json +1 -1
  56. package/src/api/listeners/raspExecutionState.ts +39 -11
  57. package/src/api/listeners/threat.ts +41 -10
  58. package/src/api/methods/native.ts +8 -0
  59. package/src/api/methods/reactNative.ts +17 -8
  60. package/src/channels/threat.ts +1 -3
  61. package/src/models/threat.ts +2 -0
  62. package/src/types/types.ts +1 -0
  63. package/android/src/main/java/com/freeraspreactnative/FreeraspThreatHandler.kt +0 -103
package/CHANGELOG.md CHANGED
@@ -5,6 +5,66 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [4.4.0] - 2026-02-06
9
+
10
+ - Android SDK version: 18.0.1
11
+ - iOS SDK version: 6.13.0
12
+
13
+ ### React Native
14
+
15
+ #### Added
16
+
17
+ - Added cache for freeRASP callbacks when listener is not registered with the app
18
+ - Added API for `automation` callback into `ThreatEventActions` (Android only)
19
+
20
+ #### Fixed
21
+
22
+ - Prevent multiple registration of the freeRASP listeners on the native side
23
+
24
+ #### Changed
25
+
26
+ - Updated compile and target SDK versions to 36 on Android
27
+ - Higher compileSdk from [rootProject, plugin] is now used in build.gradle on Android
28
+
29
+ ### Android
30
+
31
+ #### Added
32
+
33
+ - Added support for `KernelSU` to the existing root detection capabilities
34
+ - Added support for `HMA` to the existing root detection capabilities
35
+ - Added new malware detection capabilities
36
+ - Added `onAutomationDetected()` callback to `ThreatDetected` interface
37
+ - We are introducing a new capability, detecting whether the device is being automated using tools like Appium
38
+ - Added value restrictions to `externalId`
39
+ - Method `storeExternalId()` now returns `ExternalIdResult`, which indicates `Success` or `Error` when `externalId` violates restrictions
40
+
41
+ #### Fixed
42
+
43
+ - Fixed exception handling for the KeyStore `getEntry` operation
44
+ - Fixed issue in `ScreenProtector` concerning the `onScreenRecordingDetected` invocations
45
+ - Merged internal shared libraries into a single one, reducing the final APK size
46
+ - Fixed bug related to key storing in keystore type detection (hw-backed keystore check)
47
+ - Fixed manifest queries merge
48
+
49
+ #### Changed
50
+
51
+ - Removed unused library `tmlib`
52
+ - Refactoring of signature verification code
53
+ - Updated compile and target API to 36
54
+ - Improved root detection capabilities
55
+ - Detection of wireless ADB added to ADB detections
56
+
57
+ ## [4.3.2] - 2026-01-03
58
+
59
+ - Android SDK version: 17.0.1
60
+ - iOS SDK version: 6.13.0
61
+
62
+ ### React Native
63
+
64
+ #### Fixed
65
+
66
+ - Resolved potential NullPointerException when execution state events are being sent
67
+
8
68
  ## [4.3.1] - 2025-12-16
9
69
 
10
70
  - Android SDK version: 17.0.1
@@ -44,8 +44,14 @@ def getDefault(name) {
44
44
  return project.properties["FreeraspReactNative_" + name]
45
45
  }
46
46
 
47
+ def getMaxVersion(name) {
48
+ def rootVersion = rootProject.ext.has(name) ? rootProject.ext.get(name).toInteger() : 0
49
+ def pluginVersion = getIntegerDefault(name)
50
+ return Math.max(rootVersion, pluginVersion)
51
+ }
52
+
47
53
  android {
48
- compileSdkVersion getIntegerDefault("compileSdkVersion")
54
+ compileSdk getMaxVersion("compileSdk")
49
55
 
50
56
  defaultConfig {
51
57
  minSdkVersion getIntegerDefault("minSdkVersion")
@@ -99,7 +105,7 @@ dependencies {
99
105
  implementation "com.facebook.react:react-native:$react_native_version"
100
106
  implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
101
107
  implementation "org.jetbrains.kotlinx:kotlinx-serialization-json:1.4.1"
102
- implementation "com.aheaditec.talsec.security:TalsecSecurity-Community-ReactNative:17.0.1"
108
+ implementation "com.aheaditec.talsec.security:TalsecSecurity-Community-ReactNative:18.0.2"
103
109
  }
104
110
 
105
111
  if (isNewArchitectureEnabled()) {
@@ -1,6 +1,6 @@
1
1
  FreeraspReactNative_kotlinVersion=2.1.0
2
2
  FreeraspReactNative_minSdkVersion=23
3
- FreeraspReactNative_targetSdkVersion=35
4
- FreeraspReactNative_compileSdkVersion=35
3
+ FreeraspReactNative_targetSdkVersion=36
4
+ FreeraspReactNative_compileSdk=36
5
5
  FreeraspReactNative_ndkversion=21.4.7075529
6
6
  FreeraspReactNative_reactNativeVersion=+
@@ -4,6 +4,7 @@ import android.os.Build
4
4
  import android.os.Handler
5
5
  import android.os.HandlerThread
6
6
  import android.os.Looper
7
+ import com.aheaditec.talsec_security.security.api.ExternalIdResult
7
8
  import com.aheaditec.talsec_security.security.api.SuspiciousAppInfo
8
9
  import com.aheaditec.talsec_security.security.api.Talsec
9
10
  import com.aheaditec.talsec_security.security.api.TalsecConfig
@@ -22,6 +23,8 @@ import com.facebook.react.modules.core.DeviceEventManagerModule
22
23
  import com.freeraspreactnative.events.BaseRaspEvent
23
24
  import com.freeraspreactnative.events.RaspExecutionStateEvent
24
25
  import com.freeraspreactnative.events.ThreatEvent
26
+ import com.freeraspreactnative.interfaces.PluginExecutionStateListener
27
+ import com.freeraspreactnative.interfaces.PluginThreatListener
25
28
  import com.freeraspreactnative.utils.Utils
26
29
  import com.freeraspreactnative.utils.getArraySafe
27
30
  import com.freeraspreactnative.utils.getBooleanSafe
@@ -33,7 +36,6 @@ import com.freeraspreactnative.utils.toEncodedWritableArray
33
36
  class FreeraspReactNativeModule(private val reactContext: ReactApplicationContext) :
34
37
  ReactContextBaseJavaModule(reactContext) {
35
38
 
36
- private val listener = ThreatListener(FreeraspThreatHandler, FreeraspThreatHandler, FreeraspThreatHandler)
37
39
  private val lifecycleListener = object : LifecycleEventListener {
38
40
  override fun onHostResume() {
39
41
  if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
@@ -57,8 +59,8 @@ class FreeraspReactNativeModule(private val reactContext: ReactApplicationContex
57
59
  }
58
60
 
59
61
  init {
60
- appReactContext = reactContext
61
62
  reactContext.addLifecycleEventListener(lifecycleListener)
63
+ initializeEventKeys()
62
64
  }
63
65
 
64
66
  @ReactMethod
@@ -68,8 +70,7 @@ class FreeraspReactNativeModule(private val reactContext: ReactApplicationContex
68
70
 
69
71
  try {
70
72
  val config = buildTalsecConfig(options)
71
- FreeraspThreatHandler.listener = ThreatListener
72
- listener.registerListener(reactContext)
73
+ PluginThreatHandler.registerListener(reactContext)
73
74
  runOnUiThread {
74
75
  Talsec.start(reactContext, config, TalsecMode.BACKGROUND)
75
76
  mainHandler.post {
@@ -89,6 +90,12 @@ class FreeraspReactNativeModule(private val reactContext: ReactApplicationContex
89
90
  }
90
91
  }
91
92
 
93
+ // Trigger lazy initialization of the freeRASP events
94
+ private fun initializeEventKeys() {
95
+ ThreatEvent.ALL_EVENTS
96
+ RaspExecutionStateEvent.ALL_EVENTS
97
+ }
98
+
92
99
  /**
93
100
  * Method to get the random identifiers of callbacks
94
101
  */
@@ -140,13 +147,30 @@ class FreeraspReactNativeModule(private val reactContext: ReactApplicationContex
140
147
  }
141
148
 
142
149
  @ReactMethod
143
- fun addListener(@Suppress("UNUSED_PARAMETER") eventName: String) {
144
- // Set up any upstream listeners or background tasks as necessary
150
+ fun addListener(eventName: String) {
151
+ if (eventName == ThreatEvent.CHANNEL_NAME) {
152
+ PluginThreatHandler.threatDispatcher.listener = PluginListener(reactContext)
153
+ }
154
+ if (eventName == RaspExecutionStateEvent.CHANNEL_NAME) {
155
+ PluginThreatHandler.executionStateDispatcher.listener = PluginListener(reactContext)
156
+ }
145
157
  }
146
158
 
147
159
  @ReactMethod
148
160
  fun removeListeners(@Suppress("UNUSED_PARAMETER") count: Int) {
149
- // Remove upstream listeners, stop unnecessary background tasks
161
+ // built-in RN method, however it does not suit us as it just tells
162
+ // number of un-registered listeners, so we use `removeListenerForEvent`
163
+ }
164
+
165
+ @ReactMethod
166
+ fun removeListenerForEvent(eventName: String, promise: Promise) {
167
+ if (eventName == ThreatEvent.CHANNEL_NAME) {
168
+ PluginThreatHandler.threatDispatcher.listener = null
169
+ }
170
+ if (eventName == RaspExecutionStateEvent.CHANNEL_NAME) {
171
+ PluginThreatHandler.executionStateDispatcher.listener = null
172
+ }
173
+ promise.resolve("Listener unregistered")
150
174
  }
151
175
 
152
176
  /**
@@ -211,17 +235,41 @@ class FreeraspReactNativeModule(private val reactContext: ReactApplicationContex
211
235
 
212
236
  @ReactMethod
213
237
  fun storeExternalId(
214
- externalId: String, promise: Promise
238
+ externalId: String, promise: Promise
215
239
  ) {
216
- try {
217
- Talsec.storeExternalId(reactContext, externalId)
218
- promise.resolve("OK - Store external ID")
219
- } catch (e: Exception) {
240
+ try {
241
+ when (val result = Talsec.storeExternalId(reactContext, externalId)) {
242
+ is ExternalIdResult.Error -> {
220
243
  promise.reject(
221
- "NativePluginError",
222
- "Error during storeExternalId operation in Talsec Native Plugin"
244
+ "ExternalIdError",
245
+ "Setting up External ID failed - ${result.errorMsg}"
223
246
  )
247
+ }
248
+
249
+ is ExternalIdResult.Success -> {
250
+ promise.resolve("OK - Store external ID")
251
+ return
252
+ }
224
253
  }
254
+ } catch (e: Exception) {
255
+ promise.reject(
256
+ "NativePluginError",
257
+ "Error during storeExternalId operation in Talsec Native Plugin"
258
+ )
259
+ }
260
+ }
261
+
262
+ @ReactMethod
263
+ fun removeExternalId(promise: Promise) {
264
+ try {
265
+ Talsec.removeExternalId(reactContext)
266
+ promise.resolve("OK - External ID removed")
267
+ } catch (e: Exception) {
268
+ promise.reject(
269
+ "NativePluginError",
270
+ "Error during storeExternalId operation in Talsec Native Plugin"
271
+ )
272
+ }
225
273
  }
226
274
 
227
275
  private fun buildTalsecConfig(config: ReadableMap): TalsecConfig {
@@ -253,11 +301,9 @@ class FreeraspReactNativeModule(private val reactContext: ReactApplicationContex
253
301
  private val backgroundHandler = Handler(backgroundHandlerThread.looper)
254
302
  private val mainHandler = Handler(Looper.getMainLooper())
255
303
 
256
- private lateinit var appReactContext: ReactApplicationContext
257
-
258
304
  internal var talsecStarted = false
259
305
 
260
- private fun notifyEvent(event: BaseRaspEvent) {
306
+ private fun notifyEvent(event: BaseRaspEvent, appReactContext: ReactApplicationContext) {
261
307
  val params = Arguments.createMap()
262
308
  params.putInt(event.channelKey, event.value)
263
309
  appReactContext.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java)
@@ -267,7 +313,7 @@ class FreeraspReactNativeModule(private val reactContext: ReactApplicationContex
267
313
  /**
268
314
  * Sends malware detected event to React Native
269
315
  */
270
- private fun notifyMalware(suspiciousApps: MutableList<SuspiciousAppInfo>) {
316
+ private fun notifyMalware(suspiciousApps: MutableList<SuspiciousAppInfo>, appReactContext: ReactApplicationContext) {
271
317
  // Perform the malware encoding on a background thread
272
318
  backgroundHandler.post {
273
319
 
@@ -287,17 +333,17 @@ class FreeraspReactNativeModule(private val reactContext: ReactApplicationContex
287
333
  }
288
334
  }
289
335
 
290
- internal object ThreatListener : FreeraspThreatHandler.TalsecReactNative {
336
+ internal class PluginListener(private val reactContext: ReactApplicationContext) : PluginThreatListener, PluginExecutionStateListener {
291
337
  override fun threatDetected(threatEventType: ThreatEvent) {
292
- notifyEvent(threatEventType)
338
+ notifyEvent(threatEventType, reactContext)
293
339
  }
294
340
 
295
341
  override fun malwareDetected(suspiciousApps: MutableList<SuspiciousAppInfo>) {
296
- notifyMalware(suspiciousApps)
342
+ notifyMalware(suspiciousApps, reactContext)
297
343
  }
298
344
 
299
345
  override fun raspExecutionStateChanged(event: RaspExecutionStateEvent) {
300
- notifyEvent(event)
346
+ notifyEvent(event, reactContext)
301
347
  }
302
348
  }
303
349
  }
@@ -0,0 +1,121 @@
1
+ package com.freeraspreactnative
2
+
3
+ import android.content.Context
4
+ import com.aheaditec.talsec_security.security.api.SuspiciousAppInfo
5
+ import com.aheaditec.talsec_security.security.api.ThreatListener
6
+ import com.freeraspreactnative.dispatchers.ExecutionStateDispatcher
7
+ import com.freeraspreactnative.dispatchers.ThreatDispatcher
8
+ import com.freeraspreactnative.events.RaspExecutionStateEvent
9
+ import com.freeraspreactnative.events.ThreatEvent
10
+
11
+ internal object PluginThreatHandler {
12
+
13
+ internal val threatDispatcher = ThreatDispatcher()
14
+ internal val executionStateDispatcher = ExecutionStateDispatcher()
15
+
16
+ private val threatDetected = object : ThreatListener.ThreatDetected() {
17
+
18
+ override fun onRootDetected() {
19
+ threatDispatcher.dispatchThreat(ThreatEvent.PrivilegedAccess)
20
+ }
21
+
22
+ override fun onDebuggerDetected() {
23
+ threatDispatcher.dispatchThreat(ThreatEvent.Debug)
24
+ }
25
+
26
+ override fun onEmulatorDetected() {
27
+ threatDispatcher.dispatchThreat(ThreatEvent.Simulator)
28
+ }
29
+
30
+ override fun onTamperDetected() {
31
+ threatDispatcher.dispatchThreat(ThreatEvent.AppIntegrity)
32
+ }
33
+
34
+ override fun onUntrustedInstallationSourceDetected() {
35
+ threatDispatcher.dispatchThreat(ThreatEvent.UnofficialStore)
36
+ }
37
+
38
+ override fun onHookDetected() {
39
+ threatDispatcher.dispatchThreat(ThreatEvent.Hooks)
40
+ }
41
+
42
+ override fun onDeviceBindingDetected() {
43
+ threatDispatcher.dispatchThreat(ThreatEvent.DeviceBinding)
44
+ }
45
+
46
+ override fun onObfuscationIssuesDetected() {
47
+ threatDispatcher.dispatchThreat(ThreatEvent.ObfuscationIssues)
48
+ }
49
+
50
+ override fun onMalwareDetected(suspiciousAppInfos: MutableList<SuspiciousAppInfo>) {
51
+ threatDispatcher.dispatchMalware(suspiciousAppInfos ?: mutableListOf())
52
+ }
53
+
54
+ override fun onScreenshotDetected() {
55
+ threatDispatcher.dispatchThreat(ThreatEvent.Screenshot)
56
+ }
57
+
58
+ override fun onScreenRecordingDetected() {
59
+ threatDispatcher.dispatchThreat(ThreatEvent.ScreenRecording)
60
+ }
61
+
62
+ override fun onMultiInstanceDetected() {
63
+ threatDispatcher.dispatchThreat(ThreatEvent.MultiInstance)
64
+ }
65
+
66
+ override fun onUnsecureWifiDetected() {
67
+ threatDispatcher.dispatchThreat(ThreatEvent.UnsecureWifi)
68
+ }
69
+
70
+ override fun onTimeSpoofingDetected() {
71
+ threatDispatcher.dispatchThreat(ThreatEvent.TimeSpoofing)
72
+ }
73
+
74
+ override fun onLocationSpoofingDetected() {
75
+ threatDispatcher.dispatchThreat(ThreatEvent.LocationSpoofing)
76
+ }
77
+
78
+ override fun onAutomationDetected() {
79
+ threatDispatcher.dispatchThreat(ThreatEvent.Automation)
80
+ }
81
+ }
82
+
83
+ private val deviceState = object : ThreatListener.DeviceState() {
84
+
85
+ override fun onUnlockedDeviceDetected() {
86
+ threatDispatcher.dispatchThreat(ThreatEvent.Passcode)
87
+ }
88
+
89
+ override fun onHardwareBackedKeystoreNotAvailableDetected() {
90
+ threatDispatcher.dispatchThreat(ThreatEvent.SecureHardwareNotAvailable)
91
+ }
92
+
93
+ override fun onDeveloperModeDetected() {
94
+ threatDispatcher.dispatchThreat(ThreatEvent.DevMode)
95
+ }
96
+
97
+ override fun onADBEnabledDetected() {
98
+ threatDispatcher.dispatchThreat(ThreatEvent.ADBEnabled)
99
+ }
100
+
101
+ override fun onSystemVPNDetected() {
102
+ threatDispatcher.dispatchThreat(ThreatEvent.SystemVPN)
103
+ }
104
+ }
105
+
106
+ private val raspExecutionState = object : ThreatListener.RaspExecutionState() {
107
+ override fun onAllChecksFinished() {
108
+ executionStateDispatcher.dispatch(RaspExecutionStateEvent.AllChecksFinished)
109
+ }
110
+ }
111
+
112
+ private val internalListener = ThreatListener(threatDetected, deviceState, raspExecutionState)
113
+
114
+ internal fun registerListener(context: Context) {
115
+ internalListener.registerListener(context)
116
+ }
117
+
118
+ internal fun unregisterListener(context: Context) {
119
+ internalListener.unregisterListener(context)
120
+ }
121
+ }
@@ -0,0 +1,38 @@
1
+ package com.freeraspreactnative.dispatchers
2
+
3
+ import com.freeraspreactnative.events.RaspExecutionStateEvent
4
+ import com.freeraspreactnative.interfaces.PluginExecutionStateListener
5
+
6
+ internal class ExecutionStateDispatcher {
7
+ private val cache = mutableSetOf<RaspExecutionStateEvent>()
8
+
9
+ var listener: PluginExecutionStateListener? = null
10
+ set(value) {
11
+ field = value
12
+ if (value != null) {
13
+ flushCache(value)
14
+ }
15
+ }
16
+
17
+ fun dispatch(event: RaspExecutionStateEvent) {
18
+ val currentListener = listener
19
+ if (currentListener != null) {
20
+ currentListener.raspExecutionStateChanged(event)
21
+ } else {
22
+ synchronized(cache) {
23
+ val checkedListener = listener
24
+ checkedListener?.raspExecutionStateChanged(event) ?: cache.add(event)
25
+ }
26
+ }
27
+ }
28
+
29
+ private fun flushCache(registeredListener: PluginExecutionStateListener) {
30
+ val events = synchronized(cache) {
31
+ val snapshot = cache.toSet()
32
+ cache.clear()
33
+ snapshot
34
+ }
35
+ events.forEach { registeredListener.raspExecutionStateChanged(it) }
36
+ }
37
+ }
38
+
@@ -0,0 +1,61 @@
1
+ package com.freeraspreactnative.dispatchers
2
+
3
+ import com.aheaditec.talsec_security.security.api.SuspiciousAppInfo
4
+ import com.freeraspreactnative.events.ThreatEvent
5
+ import com.freeraspreactnative.interfaces.PluginThreatListener
6
+
7
+ internal class ThreatDispatcher {
8
+ private val threatCache = mutableSetOf<ThreatEvent>()
9
+ private val malwareCache = mutableSetOf<SuspiciousAppInfo>()
10
+
11
+ var listener: PluginThreatListener? = null
12
+ set(value) {
13
+ field = value
14
+ if (value != null) {
15
+ flushCache(value)
16
+ }
17
+ }
18
+
19
+ fun dispatchThreat(event: ThreatEvent) {
20
+ val currentListener = listener
21
+ if (currentListener != null) {
22
+ currentListener.threatDetected(event)
23
+ } else {
24
+ synchronized(threatCache) {
25
+ val checkedListener = listener
26
+ checkedListener?.threatDetected(event) ?: threatCache.add(event)
27
+ }
28
+ }
29
+ }
30
+
31
+ fun dispatchMalware(apps: MutableList<SuspiciousAppInfo>) {
32
+ val currentListener = listener
33
+ if (currentListener != null) {
34
+ currentListener.malwareDetected(apps)
35
+ } else {
36
+ synchronized(malwareCache) {
37
+ val checkedListener = listener
38
+ checkedListener?.malwareDetected(apps) ?: malwareCache.addAll(apps)
39
+ }
40
+ }
41
+ }
42
+
43
+ private fun flushCache(registeredListener: PluginThreatListener) {
44
+ val threats = synchronized(threatCache) {
45
+ val snapshot = threatCache.toSet()
46
+ threatCache.clear()
47
+ snapshot
48
+ }
49
+ threats.forEach { registeredListener.threatDetected(it) }
50
+
51
+ val malware = synchronized(malwareCache) {
52
+ val snapshot = malwareCache.toMutableList()
53
+ malwareCache.clear()
54
+ snapshot
55
+ }
56
+ if (malware.isNotEmpty()) {
57
+ registeredListener.malwareDetected(malware)
58
+ }
59
+ }
60
+ }
61
+
@@ -34,6 +34,7 @@ internal sealed class ThreatEvent(override val value: Int) : BaseRaspEvent {
34
34
  data object TimeSpoofing : ThreatEvent(RandomGenerator.next())
35
35
  data object LocationSpoofing : ThreatEvent(RandomGenerator.next())
36
36
  data object UnsecureWifi : ThreatEvent(RandomGenerator.next())
37
+ data object Automation : ThreatEvent(RandomGenerator.next())
37
38
 
38
39
  companion object {
39
40
  internal val CHANNEL_NAME = RandomGenerator.next().toString()
@@ -61,7 +62,8 @@ internal sealed class ThreatEvent(override val value: Int) : BaseRaspEvent {
61
62
  MultiInstance,
62
63
  TimeSpoofing,
63
64
  LocationSpoofing,
64
- UnsecureWifi
65
+ UnsecureWifi,
66
+ Automation
65
67
  ).map { it.value })
66
68
  }
67
69
  }
@@ -0,0 +1,7 @@
1
+ package com.freeraspreactnative.interfaces
2
+
3
+ import com.freeraspreactnative.events.RaspExecutionStateEvent
4
+
5
+ internal interface PluginExecutionStateListener {
6
+ fun raspExecutionStateChanged(event: RaspExecutionStateEvent)
7
+ }
@@ -0,0 +1,9 @@
1
+ package com.freeraspreactnative.interfaces
2
+
3
+ import com.aheaditec.talsec_security.security.api.SuspiciousAppInfo
4
+ import com.freeraspreactnative.events.ThreatEvent
5
+
6
+ internal interface PluginThreatListener {
7
+ fun threatDetected(threatEventType: ThreatEvent)
8
+ fun malwareDetected(suspiciousApps: MutableList<SuspiciousAppInfo>)
9
+ }
@@ -251,6 +251,16 @@ const App = () => {
251
251
  )
252
252
  );
253
253
  },
254
+ // Android only
255
+ automation: () => {
256
+ setAppChecks((currentState) =>
257
+ currentState.map((threat) =>
258
+ threat.name === 'Automation'
259
+ ? { ...threat, status: 'nok' }
260
+ : threat
261
+ )
262
+ );
263
+ },
254
264
  };
255
265
 
256
266
  const raspExecutionStateActions = {
@@ -21,6 +21,7 @@ import {
21
21
  blockScreenCapture,
22
22
  type SuspiciousAppInfo,
23
23
  storeExternalId,
24
+ removeExternalId,
24
25
  } from 'freerasp-react-native';
25
26
  import {
26
27
  ALERT_TYPE,
@@ -84,6 +85,24 @@ export const DemoApp: React.FC<{
84
85
  }
85
86
  };
86
87
 
88
+ const handleModalRemove = async () => {
89
+ setModalVisible(false);
90
+ try {
91
+ await removeExternalId();
92
+ Toast.show({
93
+ type: ALERT_TYPE.SUCCESS,
94
+ title: 'Success',
95
+ textBody: 'External ID removed',
96
+ });
97
+ } catch (error: any) {
98
+ Toast.show({
99
+ type: ALERT_TYPE.WARNING,
100
+ title: 'Warning',
101
+ textBody: 'External ID not removed',
102
+ });
103
+ }
104
+ };
105
+
87
106
  const handleModalDismiss = () => {
88
107
  setModalVisible(false);
89
108
  };
@@ -140,6 +159,11 @@ export const DemoApp: React.FC<{
140
159
  onPress={handleModalSend}>
141
160
  <Text style={styles.modalText}>Send</Text>
142
161
  </TouchableOpacity>
162
+ <TouchableOpacity
163
+ style={styles.removeButton}
164
+ onPress={handleModalRemove}>
165
+ <Text style={styles.modalText}>Remove</Text>
166
+ </TouchableOpacity>
143
167
  <TouchableOpacity
144
168
  style={styles.dismissButton}
145
169
  onPress={handleModalDismiss}>
@@ -346,6 +370,14 @@ const styles = StyleSheet.create({
346
370
  marginRight: 10,
347
371
  alignItems: 'center',
348
372
  },
373
+ removeButton: {
374
+ backgroundColor: '#FF9800',
375
+ padding: 10,
376
+ borderRadius: 5,
377
+ flex: 1,
378
+ marginRight: 10,
379
+ alignItems: 'center',
380
+ },
349
381
  dismissButton: {
350
382
  backgroundColor: '#f44336',
351
383
  padding: 10,
@@ -24,4 +24,5 @@ export const androidChecks = [
24
24
  { name: 'Time Spoofing', status: 'ok' },
25
25
  { name: 'Location Spoofing', status: 'ok' },
26
26
  { name: 'Unsecure Wifi', status: 'ok' },
27
+ { name: 'Automation', status: 'ok' },
27
28
  ];
@@ -14,7 +14,11 @@ Pod::Spec.new do |s|
14
14
  s.platforms = { :ios => "11.0" }
15
15
  s.source = { :git => "https://github.com/talsec/freerasp-react-native.git", :tag => "#{s.version}" }
16
16
 
17
- s.source_files = 'ios/models/*.{h,m,mm,swift}', 'ios/utils/*.{h,m,mm,swift}', 'ios/*.{h,m,mm,swift}', 'ios/TalsecRuntime.xcframework'
17
+ s.source_files = 'ios/models/*.{h,m,mm,swift}',
18
+ 'ios/utils/*.{h,m,mm,swift}',
19
+ 'ios/dispatchers/*.{h,m,mm,swift}',
20
+ 'ios/*.{h,m,mm,swift}',
21
+ 'ios/TalsecRuntime.xcframework'
18
22
  s.xcconfig = { 'OTHER_LDFLAGS' => '-framework TalsecRuntime' }
19
23
  s.ios.vendored_frameworks = "ios/TalsecRuntime.xcframework"
20
24
 
@@ -15,6 +15,9 @@ RCT_EXTERN_METHOD(getRaspExecutionStateChannelData:(RCTPromiseResolveBlock)resol
15
15
  withRejecter:(RCTPromiseRejectBlock)reject)
16
16
  RCT_EXTERN_METHOD(getRaspExecutionStateIdentifiers:(RCTPromiseResolveBlock)resolve
17
17
  withRejecter:(RCTPromiseRejectBlock)reject)
18
+ RCT_EXTERN_METHOD(removeListenerForEvent:(NSString *)eventName
19
+ withResolver:(RCTPromiseResolveBlock)resolve
20
+ withRejecter:(RCTPromiseRejectBlock)reject)
18
21
  RCT_EXTERN_METHOD(blockScreenCapture:(BOOL)enable
19
22
  withResolver:(RCTPromiseResolveBlock)resolve
20
23
  withRejecter:(RCTPromiseRejectBlock)reject)
@@ -23,6 +26,8 @@ RCT_EXTERN_METHOD(isScreenCaptureBlocked:(RCTPromiseResolveBlock)resolve
23
26
  RCT_EXTERN_METHOD(storeExternalId:(NSString *)externalId
24
27
  withResolver:(RCTPromiseResolveBlock)resolve
25
28
  withRejecter:(RCTPromiseRejectBlock)reject)
29
+ RCT_EXTERN_METHOD(removeExternalId:(RCTPromiseResolveBlock)resolve
30
+ withRejecter:(RCTPromiseRejectBlock)reject)
26
31
  RCT_EXTERN_METHOD(onInvalidCallback)
27
32
 
28
33
  + (BOOL)requiresMainQueueSetup