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.
- package/CHANGELOG.md +50 -1
- package/android/build.gradle +8 -2
- package/android/gradle.properties +2 -2
- package/android/src/main/java/com/freeraspreactnative/FreeraspReactNativeModule.kt +61 -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,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.
|
|
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
|
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,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
|
-
|
|
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(
|
|
151
|
-
|
|
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
|
-
//
|
|
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
|
-
|
|
238
|
+
externalId: String, promise: Promise
|
|
222
239
|
) {
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
} catch (e: Exception) {
|
|
240
|
+
try {
|
|
241
|
+
when (val result = Talsec.storeExternalId(reactContext, externalId)) {
|
|
242
|
+
is ExternalIdResult.Error -> {
|
|
227
243
|
promise.reject(
|
|
228
|
-
|
|
229
|
-
|
|
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
|
|
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,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
|