Skip to content

Commit 093b4c0

Browse files
authored
release: freeRASP 4.4.0 (#139)
* feat: prevent multi registration of listener * chore: update tsconfig * feat: update listeners registration * feat: add threat caching * feat: refactor listener registration * feat: add automation check * chore: update example app * chore: raise version * feat: raise compileSdk version * update the android sdk, implement ios cache + align api * chore: remove import * rename wrapper -> plugin * chore: refactor internal listener * feat: improve context handling on android native
1 parent 3672228 commit 093b4c0

29 files changed

+635
-178
lines changed

CHANGELOG.md

Lines changed: 50 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,56 @@ All notable changes to this project will be documented in this file.
55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
66
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
77

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
958

1059
- Android SDK version: 17.0.1
1160
- iOS SDK version: 6.13.0

android/build.gradle

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,14 @@ def getDefault(name) {
4444
return project.properties["FreeraspReactNative_" + name]
4545
}
4646

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+
4753
android {
48-
compileSdkVersion getIntegerDefault("compileSdkVersion")
54+
compileSdk getMaxVersion("compileSdk")
4955

5056
defaultConfig {
5157
minSdkVersion getIntegerDefault("minSdkVersion")
@@ -99,7 +105,7 @@ dependencies {
99105
implementation "com.facebook.react:react-native:$react_native_version"
100106
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
101107
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"
103109
}
104110

105111
if (isNewArchitectureEnabled()) {

android/gradle.properties

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
FreeraspReactNative_kotlinVersion=2.1.0
22
FreeraspReactNative_minSdkVersion=23
3-
FreeraspReactNative_targetSdkVersion=35
4-
FreeraspReactNative_compileSdkVersion=35
3+
FreeraspReactNative_targetSdkVersion=36
4+
FreeraspReactNative_compileSdk=36
55
FreeraspReactNative_ndkversion=21.4.7075529
66
FreeraspReactNative_reactNativeVersion=+

android/src/main/java/com/freeraspreactnative/FreeraspReactNativeModule.kt

Lines changed: 61 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import android.os.Build
44
import android.os.Handler
55
import android.os.HandlerThread
66
import android.os.Looper
7+
import com.aheaditec.talsec_security.security.api.ExternalIdResult
78
import com.aheaditec.talsec_security.security.api.SuspiciousAppInfo
89
import com.aheaditec.talsec_security.security.api.Talsec
910
import com.aheaditec.talsec_security.security.api.TalsecConfig
@@ -22,6 +23,8 @@ import com.facebook.react.modules.core.DeviceEventManagerModule
2223
import com.freeraspreactnative.events.BaseRaspEvent
2324
import com.freeraspreactnative.events.RaspExecutionStateEvent
2425
import com.freeraspreactnative.events.ThreatEvent
26+
import com.freeraspreactnative.interfaces.PluginExecutionStateListener
27+
import com.freeraspreactnative.interfaces.PluginThreatListener
2528
import com.freeraspreactnative.utils.Utils
2629
import com.freeraspreactnative.utils.getArraySafe
2730
import com.freeraspreactnative.utils.getBooleanSafe
@@ -33,7 +36,6 @@ import com.freeraspreactnative.utils.toEncodedWritableArray
3336
class FreeraspReactNativeModule(private val reactContext: ReactApplicationContext) :
3437
ReactContextBaseJavaModule(reactContext) {
3538

36-
private val listener = ThreatListener(FreeraspThreatHandler, FreeraspThreatHandler, FreeraspThreatHandler)
3739
private val lifecycleListener = object : LifecycleEventListener {
3840
override fun onHostResume() {
3941
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
@@ -57,7 +59,6 @@ class FreeraspReactNativeModule(private val reactContext: ReactApplicationContex
5759
}
5860

5961
init {
60-
appReactContext = reactContext
6162
reactContext.addLifecycleEventListener(lifecycleListener)
6263
initializeEventKeys()
6364
}
@@ -69,8 +70,7 @@ class FreeraspReactNativeModule(private val reactContext: ReactApplicationContex
6970

7071
try {
7172
val config = buildTalsecConfig(options)
72-
FreeraspThreatHandler.listener = ThreatListener
73-
listener.registerListener(reactContext)
73+
PluginThreatHandler.registerListener(reactContext)
7474
runOnUiThread {
7575
Talsec.start(reactContext, config, TalsecMode.BACKGROUND)
7676
mainHandler.post {
@@ -147,13 +147,30 @@ class FreeraspReactNativeModule(private val reactContext: ReactApplicationContex
147147
}
148148

149149
@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+
}
152157
}
153158

154159
@ReactMethod
155160
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")
157174
}
158175

159176
/**
@@ -218,17 +235,41 @@ class FreeraspReactNativeModule(private val reactContext: ReactApplicationContex
218235

219236
@ReactMethod
220237
fun storeExternalId(
221-
externalId: String, promise: Promise
238+
externalId: String, promise: Promise
222239
) {
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 -> {
227243
promise.reject(
228-
"NativePluginError",
229-
"Error during storeExternalId operation in Talsec Native Plugin"
244+
"ExternalIdError",
245+
"Setting up External ID failed - ${result.errorMsg}"
230246
)
247+
}
248+
249+
is ExternalIdResult.Success -> {
250+
promise.resolve("OK - Store external ID")
251+
return
252+
}
231253
}
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+
}
232273
}
233274

234275
private fun buildTalsecConfig(config: ReadableMap): TalsecConfig {
@@ -260,11 +301,9 @@ class FreeraspReactNativeModule(private val reactContext: ReactApplicationContex
260301
private val backgroundHandler = Handler(backgroundHandlerThread.looper)
261302
private val mainHandler = Handler(Looper.getMainLooper())
262303

263-
private lateinit var appReactContext: ReactApplicationContext
264-
265304
internal var talsecStarted = false
266305

267-
private fun notifyEvent(event: BaseRaspEvent) {
306+
private fun notifyEvent(event: BaseRaspEvent, appReactContext: ReactApplicationContext) {
268307
val params = Arguments.createMap()
269308
params.putInt(event.channelKey, event.value)
270309
appReactContext.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java)
@@ -274,7 +313,7 @@ class FreeraspReactNativeModule(private val reactContext: ReactApplicationContex
274313
/**
275314
* Sends malware detected event to React Native
276315
*/
277-
private fun notifyMalware(suspiciousApps: MutableList<SuspiciousAppInfo>) {
316+
private fun notifyMalware(suspiciousApps: MutableList<SuspiciousAppInfo>, appReactContext: ReactApplicationContext) {
278317
// Perform the malware encoding on a background thread
279318
backgroundHandler.post {
280319

@@ -294,17 +333,17 @@ class FreeraspReactNativeModule(private val reactContext: ReactApplicationContex
294333
}
295334
}
296335

297-
internal object ThreatListener : FreeraspThreatHandler.TalsecReactNative {
336+
internal class PluginListener(private val reactContext: ReactApplicationContext) : PluginThreatListener, PluginExecutionStateListener {
298337
override fun threatDetected(threatEventType: ThreatEvent) {
299-
notifyEvent(threatEventType)
338+
notifyEvent(threatEventType, reactContext)
300339
}
301340

302341
override fun malwareDetected(suspiciousApps: MutableList<SuspiciousAppInfo>) {
303-
notifyMalware(suspiciousApps)
342+
notifyMalware(suspiciousApps, reactContext)
304343
}
305344

306345
override fun raspExecutionStateChanged(event: RaspExecutionStateEvent) {
307-
notifyEvent(event)
346+
notifyEvent(event, reactContext)
308347
}
309348
}
310349
}

android/src/main/java/com/freeraspreactnative/FreeraspThreatHandler.kt

Lines changed: 0 additions & 103 deletions
This file was deleted.

0 commit comments

Comments
 (0)