freerasp-react-native 4.3.2 → 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 +50 -1
  2. package/android/build.gradle +8 -2
  3. package/android/gradle.properties +2 -2
  4. package/android/src/main/java/com/freeraspreactnative/FreeraspReactNativeModule.kt +61 -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,7 +5,56 @@ 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.3.2] - 2025-01-03
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
9
58
 
10
59
  - Android SDK version: 17.0.1
11
60
  - iOS SDK version: 6.13.0
@@ -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,7 +59,6 @@ class FreeraspReactNativeModule(private val reactContext: ReactApplicationContex
57
59
  }
58
60
 
59
61
  init {
60
- appReactContext = reactContext
61
62
  reactContext.addLifecycleEventListener(lifecycleListener)
62
63
  initializeEventKeys()
63
64
  }
@@ -69,8 +70,7 @@ class FreeraspReactNativeModule(private val reactContext: ReactApplicationContex
69
70
 
70
71
  try {
71
72
  val config = buildTalsecConfig(options)
72
- FreeraspThreatHandler.listener = ThreatListener
73
- listener.registerListener(reactContext)
73
+ PluginThreatHandler.registerListener(reactContext)
74
74
  runOnUiThread {
75
75
  Talsec.start(reactContext, config, TalsecMode.BACKGROUND)
76
76
  mainHandler.post {
@@ -147,13 +147,30 @@ class FreeraspReactNativeModule(private val reactContext: ReactApplicationContex
147
147
  }
148
148
 
149
149
  @ReactMethod
150
- fun addListener(@Suppress("UNUSED_PARAMETER") eventName: String) {
151
- // 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
+ }
152
157
  }
153
158
 
154
159
  @ReactMethod
155
160
  fun removeListeners(@Suppress("UNUSED_PARAMETER") count: Int) {
156
- // 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")
157
174
  }
158
175
 
159
176
  /**
@@ -218,17 +235,41 @@ class FreeraspReactNativeModule(private val reactContext: ReactApplicationContex
218
235
 
219
236
  @ReactMethod
220
237
  fun storeExternalId(
221
- externalId: String, promise: Promise
238
+ externalId: String, promise: Promise
222
239
  ) {
223
- try {
224
- Talsec.storeExternalId(reactContext, externalId)
225
- promise.resolve("OK - Store external ID")
226
- } catch (e: Exception) {
240
+ try {
241
+ when (val result = Talsec.storeExternalId(reactContext, externalId)) {
242
+ is ExternalIdResult.Error -> {
227
243
  promise.reject(
228
- "NativePluginError",
229
- "Error during storeExternalId operation in Talsec Native Plugin"
244
+ "ExternalIdError",
245
+ "Setting up External ID failed - ${result.errorMsg}"
230
246
  )
247
+ }
248
+
249
+ is ExternalIdResult.Success -> {
250
+ promise.resolve("OK - Store external ID")
251
+ return
252
+ }
231
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
+ }
232
273
  }
233
274
 
234
275
  private fun buildTalsecConfig(config: ReadableMap): TalsecConfig {
@@ -260,11 +301,9 @@ class FreeraspReactNativeModule(private val reactContext: ReactApplicationContex
260
301
  private val backgroundHandler = Handler(backgroundHandlerThread.looper)
261
302
  private val mainHandler = Handler(Looper.getMainLooper())
262
303
 
263
- private lateinit var appReactContext: ReactApplicationContext
264
-
265
304
  internal var talsecStarted = false
266
305
 
267
- private fun notifyEvent(event: BaseRaspEvent) {
306
+ private fun notifyEvent(event: BaseRaspEvent, appReactContext: ReactApplicationContext) {
268
307
  val params = Arguments.createMap()
269
308
  params.putInt(event.channelKey, event.value)
270
309
  appReactContext.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java)
@@ -274,7 +313,7 @@ class FreeraspReactNativeModule(private val reactContext: ReactApplicationContex
274
313
  /**
275
314
  * Sends malware detected event to React Native
276
315
  */
277
- private fun notifyMalware(suspiciousApps: MutableList<SuspiciousAppInfo>) {
316
+ private fun notifyMalware(suspiciousApps: MutableList<SuspiciousAppInfo>, appReactContext: ReactApplicationContext) {
278
317
  // Perform the malware encoding on a background thread
279
318
  backgroundHandler.post {
280
319
 
@@ -294,17 +333,17 @@ class FreeraspReactNativeModule(private val reactContext: ReactApplicationContex
294
333
  }
295
334
  }
296
335
 
297
- internal object ThreatListener : FreeraspThreatHandler.TalsecReactNative {
336
+ internal class PluginListener(private val reactContext: ReactApplicationContext) : PluginThreatListener, PluginExecutionStateListener {
298
337
  override fun threatDetected(threatEventType: ThreatEvent) {
299
- notifyEvent(threatEventType)
338
+ notifyEvent(threatEventType, reactContext)
300
339
  }
301
340
 
302
341
  override fun malwareDetected(suspiciousApps: MutableList<SuspiciousAppInfo>) {
303
- notifyMalware(suspiciousApps)
342
+ notifyMalware(suspiciousApps, reactContext)
304
343
  }
305
344
 
306
345
  override fun raspExecutionStateChanged(event: RaspExecutionStateEvent) {
307
- notifyEvent(event)
346
+ notifyEvent(event, reactContext)
308
347
  }
309
348
  }
310
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