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.
- package/CHANGELOG.md +60 -0
- package/android/build.gradle +8 -2
- package/android/gradle.properties +2 -2
- package/android/src/main/java/com/freeraspreactnative/FreeraspReactNativeModule.kt +68 -22
- package/android/src/main/java/com/freeraspreactnative/PluginThreatHandler.kt +121 -0
- package/android/src/main/java/com/freeraspreactnative/dispatchers/ExecutionStateDispatcher.kt +38 -0
- package/android/src/main/java/com/freeraspreactnative/dispatchers/ThreatDispatcher.kt +61 -0
- package/android/src/main/java/com/freeraspreactnative/events/ThreatEvent.kt +3 -1
- package/android/src/main/java/com/freeraspreactnative/interfaces/PluginExecutionStateListener.kt +7 -0
- package/android/src/main/java/com/freeraspreactnative/interfaces/PluginThreatListener.kt +9 -0
- package/example/src/App.tsx +10 -0
- package/example/src/DemoApp.tsx +32 -0
- package/example/src/checks.ts +1 -0
- package/freerasp-react-native.podspec +5 -1
- package/ios/FreeraspReactNative.m +5 -0
- package/ios/FreeraspReactNative.swift +39 -10
- package/ios/dispatchers/ExecutionStateDispatcher.swift +35 -0
- package/ios/dispatchers/ThreatDispatcher.swift +36 -0
- package/lib/commonjs/api/listeners/raspExecutionState.js +28 -8
- package/lib/commonjs/api/listeners/raspExecutionState.js.map +1 -1
- package/lib/commonjs/api/listeners/threat.js +34 -10
- package/lib/commonjs/api/listeners/threat.js.map +1 -1
- package/lib/commonjs/api/methods/native.js +9 -1
- package/lib/commonjs/api/methods/native.js.map +1 -1
- package/lib/commonjs/api/methods/reactNative.js +13 -8
- package/lib/commonjs/api/methods/reactNative.js.map +1 -1
- package/lib/commonjs/channels/threat.js +1 -3
- package/lib/commonjs/channels/threat.js.map +1 -1
- package/lib/commonjs/models/threat.js +2 -1
- package/lib/commonjs/models/threat.js.map +1 -1
- package/lib/module/api/listeners/raspExecutionState.js +29 -9
- package/lib/module/api/listeners/raspExecutionState.js.map +1 -1
- package/lib/module/api/listeners/threat.js +35 -11
- package/lib/module/api/listeners/threat.js.map +1 -1
- package/lib/module/api/methods/native.js +6 -0
- package/lib/module/api/methods/native.js.map +1 -1
- package/lib/module/api/methods/reactNative.js +13 -7
- package/lib/module/api/methods/reactNative.js.map +1 -1
- package/lib/module/channels/threat.js +1 -3
- package/lib/module/channels/threat.js.map +1 -1
- package/lib/module/models/threat.js +2 -1
- package/lib/module/models/threat.js.map +1 -1
- package/lib/typescript/src/api/listeners/raspExecutionState.d.ts +1 -1
- package/lib/typescript/src/api/listeners/raspExecutionState.d.ts.map +1 -1
- package/lib/typescript/src/api/listeners/threat.d.ts +1 -1
- package/lib/typescript/src/api/listeners/threat.d.ts.map +1 -1
- package/lib/typescript/src/api/methods/native.d.ts +2 -0
- package/lib/typescript/src/api/methods/native.d.ts.map +1 -1
- package/lib/typescript/src/api/methods/reactNative.d.ts.map +1 -1
- package/lib/typescript/src/channels/threat.d.ts.map +1 -1
- package/lib/typescript/src/models/threat.d.ts +1 -0
- package/lib/typescript/src/models/threat.d.ts.map +1 -1
- package/lib/typescript/src/types/types.d.ts +1 -0
- package/lib/typescript/src/types/types.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/api/listeners/raspExecutionState.ts +39 -11
- package/src/api/listeners/threat.ts +41 -10
- package/src/api/methods/native.ts +8 -0
- package/src/api/methods/reactNative.ts +17 -8
- package/src/channels/threat.ts +1 -3
- package/src/models/threat.ts +2 -0
- package/src/types/types.ts +1 -0
- 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
|
package/android/build.gradle
CHANGED
|
@@ -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
|
-
|
|
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:
|
|
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=
|
|
4
|
-
|
|
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
|
-
|
|
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(
|
|
144
|
-
|
|
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
|
-
//
|
|
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
|
-
|
|
238
|
+
externalId: String, promise: Promise
|
|
215
239
|
) {
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
} catch (e: Exception) {
|
|
240
|
+
try {
|
|
241
|
+
when (val result = Talsec.storeExternalId(reactContext, externalId)) {
|
|
242
|
+
is ExternalIdResult.Error -> {
|
|
220
243
|
promise.reject(
|
|
221
|
-
|
|
222
|
-
|
|
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
|
|
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,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
|
+
}
|
package/example/src/App.tsx
CHANGED
|
@@ -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 = {
|
package/example/src/DemoApp.tsx
CHANGED
|
@@ -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,
|
package/example/src/checks.ts
CHANGED
|
@@ -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}',
|
|
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
|