From 7dd8136b01ffcefa96c9b1dbdec52c24c1b4cfdd Mon Sep 17 00:00:00 2001 From: martinzigrai Date: Thu, 7 May 2026 15:47:20 +0200 Subject: [PATCH 01/18] feat: bump Android SDK to 18.3.0 --- android/build.gradle | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/android/build.gradle b/android/build.gradle index 7a726b4..83eefc6 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -105,7 +105,7 @@ dependencies { implementation "com.facebook.react:react-native:$react_native_version" implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" implementation "org.jetbrains.kotlinx:kotlinx-serialization-json:1.4.1" - implementation "com.aheaditec.talsec.security:TalsecSecurity-Community-ReactNative:18.0.4" + implementation "com.aheaditec.talsec.security:TalsecSecurity-Community-ReactNative:18.3.0" } if (isNewArchitectureEnabled()) { diff --git a/package.json b/package.json index c3051c3..e74d399 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "freerasp-react-native", - "version": "4.5.2", + "version": "4.6.0", "description": "React Native plugin for improving app security and threat monitoring on Android and iOS mobile devices.", "main": "lib/commonjs/index", "module": "lib/module/index", From 9cb8b0f31ad9b89d66c19deb6041c27778c555e1 Mon Sep 17 00:00:00 2001 From: martinzigrai Date: Thu, 7 May 2026 15:49:56 +0200 Subject: [PATCH 02/18] feat!: rename reason to reasons in SuspiciousAppInfo --- .../com/freeraspreactnative/models/RNSuspiciousAppInfo.kt | 2 +- .../src/main/java/com/freeraspreactnative/utils/Extensions.kt | 2 +- example/src/MalwareItem.tsx | 4 ++-- src/types/types.ts | 2 +- src/utils/malware.ts | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/android/src/main/java/com/freeraspreactnative/models/RNSuspiciousAppInfo.kt b/android/src/main/java/com/freeraspreactnative/models/RNSuspiciousAppInfo.kt index 11dc239..4c6b0e9 100644 --- a/android/src/main/java/com/freeraspreactnative/models/RNSuspiciousAppInfo.kt +++ b/android/src/main/java/com/freeraspreactnative/models/RNSuspiciousAppInfo.kt @@ -9,7 +9,7 @@ import kotlinx.serialization.Serializable @Serializable data class RNSuspiciousAppInfo( val packageInfo: RNPackageInfo, - val reason: String, + val reasons: Set, val permissions: Set? ) diff --git a/android/src/main/java/com/freeraspreactnative/utils/Extensions.kt b/android/src/main/java/com/freeraspreactnative/utils/Extensions.kt index 5feac76..a7a4f53 100644 --- a/android/src/main/java/com/freeraspreactnative/utils/Extensions.kt +++ b/android/src/main/java/com/freeraspreactnative/utils/Extensions.kt @@ -74,7 +74,7 @@ internal fun ReadableMap.getNestedArraySafe(key: String): Array> { internal fun SuspiciousAppInfo.toRNSuspiciousAppInfo(context: ReactContext): RNSuspiciousAppInfo { return RNSuspiciousAppInfo( packageInfo = this.packageInfo.toRNPackageInfo(context), - reason = this.reason, + reasons = this.reasons, permissions = this.permissions ) } diff --git a/example/src/MalwareItem.tsx b/example/src/MalwareItem.tsx index 1b9fe0a..ce7c311 100644 --- a/example/src/MalwareItem.tsx +++ b/example/src/MalwareItem.tsx @@ -90,8 +90,8 @@ export const MalwareItem: React.FC<{ app: SuspiciousAppInfo }> = ({ app }) => { {app.packageInfo.installerStore ?? 'Not specified'} - Detection reason: - {app.reason} + Detection reasons: + {app.reasons.join(', ')} Granted permissions: {app.permissions?.join(', ') ?? 'Not specified'} diff --git a/src/types/types.ts b/src/types/types.ts index 96f4d56..d1dc7c1 100644 --- a/src/types/types.ts +++ b/src/types/types.ts @@ -27,7 +27,7 @@ export type TalsecMalwareConfig = { export type SuspiciousAppInfo = { packageInfo: PackageInfo; - reason: string; + reasons: string[]; permissions?: string[]; }; diff --git a/src/utils/malware.ts b/src/utils/malware.ts index 02929c3..6a6f634 100644 --- a/src/utils/malware.ts +++ b/src/utils/malware.ts @@ -18,7 +18,7 @@ export const toSuspiciousAppInfo = (base64Value: string): SuspiciousAppInfo => { const packageInfo = data.packageInfo as PackageInfo; return { packageInfo, - reason: data.reason, + reasons: data.reasons, permissions: data.permissions, } as SuspiciousAppInfo; }; From e53f51f8bc9ffb08c103f4de82fa6bcd5f5ca565 Mon Sep 17 00:00:00 2001 From: martinzigrai Date: Thu, 7 May 2026 15:50:10 +0200 Subject: [PATCH 03/18] feat!: deprecate old malware config fields --- src/types/types.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/types/types.ts b/src/types/types.ts index d1dc7c1..22706d0 100644 --- a/src/types/types.ts +++ b/src/types/types.ts @@ -19,9 +19,13 @@ export type TalsecIosConfig = { }; export type TalsecMalwareConfig = { + /** @deprecated Use SuspiciousAppDetectionConfig instead */ blacklistedHashes?: string[]; + /** @deprecated Use SuspiciousAppDetectionConfig instead */ blacklistedPackageNames?: string[]; + /** @deprecated Use SuspiciousAppDetectionConfig instead */ suspiciousPermissions?: string[][]; + /** @deprecated Use SuspiciousAppDetectionConfig instead */ whitelistedInstallationSources?: string[]; }; From 37c9b425d7303f81e1de75e340f907806876521a Mon Sep 17 00:00:00 2001 From: martinzigrai Date: Thu, 7 May 2026 15:50:39 +0200 Subject: [PATCH 04/18] feat: add SuspiciousAppDetectionConfig to API --- example/src/App.tsx | 15 ++++++++++----- src/types/types.ts | 24 ++++++++++++++++++++++++ 2 files changed, 34 insertions(+), 5 deletions(-) diff --git a/example/src/App.tsx b/example/src/App.tsx index 1b92aa4..4d95476 100644 --- a/example/src/App.tsx +++ b/example/src/App.tsx @@ -34,10 +34,10 @@ const App = () => { packageName: 'com.freeraspreactnativeexample', certificateHashes: ['AKoRuyLMM91E7lX/Zqp3u4jMmd0A7hH/Iqozu0TMVd0='], // supportedAlternativeStores: ['storeOne', 'storeTwo'], - malwareConfig: { - blacklistedHashes: ['FgvSehLMM91E7lX/Zqp3u4jMmd0A7hH/Iqozu0TMVd0u'], - blacklistedPackageNames: ['com.freeraspreactnativeexample'], - suspiciousPermissions: [ + suspiciousAppDetectionConfig: { + packageNames: ['com.freeraspreactnativeexample'], + hashes: ['FgvSehLMM91E7lX/Zqp3u4jMmd0A7hH/Iqozu0TMVd0u'], + requestedPermissions: [ [ 'android.permission.INTERNET', 'android.permission.ACCESS_COARSE_LOCATION', @@ -45,7 +45,12 @@ const App = () => { ['android.permission.BLUETOOTH'], ['android.permission.BATTERY_STATS'], ], - whitelistedInstallationSources: ['com.apkpure.aegon'], + grantedPermissions: [['android.permission.ACCESS_FINE_LOCATION']], + malwareScanScope: { + scanScope: 'SIDELOADED_AND_SYSTEM_EXCLUDE_OEM', + trustedInstallSources: ['com.apkpure.aegon'], + }, + reasonMode: 'HIGHEST_CONFIDENCE', }, }, iosConfig: { diff --git a/src/types/types.ts b/src/types/types.ts index 22706d0..450ef5b 100644 --- a/src/types/types.ts +++ b/src/types/types.ts @@ -6,11 +6,35 @@ export type TalsecConfig = { killOnBypass?: boolean; }; +export type ScopeType = + | 'SIDELOADED_ONLY' + | 'SIDELOADED_AND_SYSTEM_EXCLUDE_OEM' + | 'SIDELOADED_AND_OEM' + | 'SIDELOADED_AND_SYSTEM_AND_OEM' + | 'ALL'; + +export type ReasonMode = 'ALL' | 'HIGHEST_CONFIDENCE'; + +export type MalwareScanScope = { + scanScope: ScopeType; + trustedInstallSources?: string[]; +}; + +export type SuspiciousAppDetectionConfig = { + packageNames?: string[]; + hashes?: string[]; + requestedPermissions?: string[][]; + grantedPermissions?: string[][]; + malwareScanScope?: MalwareScanScope; + reasonMode?: ReasonMode; +}; + export type TalsecAndroidConfig = { packageName: string; certificateHashes: string[]; supportedAlternativeStores?: string[]; malwareConfig?: TalsecMalwareConfig; + suspiciousAppDetectionConfig?: SuspiciousAppDetectionConfig; }; export type TalsecIosConfig = { From 31464c41a5c588426334c78c2cada6f0a661985a Mon Sep 17 00:00:00 2001 From: martinzigrai Date: Thu, 7 May 2026 15:52:07 +0200 Subject: [PATCH 05/18] refactor: extract SuspiciousAppDetectionConfig parsing to utils --- .../FreeraspReactNativeModule.kt | 6 +++ .../freeraspreactnative/utils/Extensions.kt | 42 +++++++++++++++++++ 2 files changed, 48 insertions(+) diff --git a/android/src/main/java/com/freeraspreactnative/FreeraspReactNativeModule.kt b/android/src/main/java/com/freeraspreactnative/FreeraspReactNativeModule.kt index ba40fdf..dc16106 100644 --- a/android/src/main/java/com/freeraspreactnative/FreeraspReactNativeModule.kt +++ b/android/src/main/java/com/freeraspreactnative/FreeraspReactNativeModule.kt @@ -33,6 +33,7 @@ import com.freeraspreactnative.utils.getMapThrowing import com.freeraspreactnative.utils.getNestedArraySafe import com.freeraspreactnative.utils.getStringThrowing import com.freeraspreactnative.utils.toEncodedWritableArray +import com.freeraspreactnative.utils.toSuspiciousAppDetectionConfig class FreeraspReactNativeModule(private val reactContext: ReactApplicationContext) : ReactContextBaseJavaModule(reactContext) { @@ -306,6 +307,11 @@ class FreeraspReactNativeModule(private val reactContext: ReactApplicationContex talsecBuilder.suspiciousPermissions(malwareConfig.getNestedArraySafe("suspiciousPermissions")) } + if (androidConfig.hasKey("suspiciousAppDetectionConfig")) { + val suspiciousAppConfig = androidConfig.getMapThrowing("suspiciousAppDetectionConfig") + talsecBuilder.suspiciousAppDetection(suspiciousAppConfig.toSuspiciousAppDetectionConfig()) + } + return talsecBuilder.build() } diff --git a/android/src/main/java/com/freeraspreactnative/utils/Extensions.kt b/android/src/main/java/com/freeraspreactnative/utils/Extensions.kt index a7a4f53..9322d72 100644 --- a/android/src/main/java/com/freeraspreactnative/utils/Extensions.kt +++ b/android/src/main/java/com/freeraspreactnative/utils/Extensions.kt @@ -3,6 +3,10 @@ package com.freeraspreactnative.utils import android.content.pm.PackageInfo import android.util.Base64 import android.util.Log +import com.aheaditec.talsec_security.security.api.MalwareScanScope +import com.aheaditec.talsec_security.security.api.ReasonMode +import com.aheaditec.talsec_security.security.api.ScopeType +import com.aheaditec.talsec_security.security.api.SuspiciousAppDetectionConfig import com.aheaditec.talsec_security.security.api.SuspiciousAppInfo import com.facebook.react.bridge.Arguments import com.facebook.react.bridge.ReactContext @@ -92,6 +96,44 @@ internal fun PackageInfo.toRNPackageInfo(context: ReactContext): RNPackageInfo { ) } +internal fun ReadableMap.toMalwareScanScope(): MalwareScanScope? { + if (!this.hasKey("malwareScanScope")) return null + val scanScopeMap = this.getMap("malwareScanScope") ?: return null + val scanScope = try { + ScopeType.valueOf(scanScopeMap.getString("scanScope") ?: "SIDELOADED_ONLY") + } catch (_: IllegalArgumentException) { + ScopeType.SIDELOADED_ONLY + } + val trustedInstallSources = scanScopeMap.getArraySafe("trustedInstallSources").toSet() + return MalwareScanScope( + scanScope = scanScope, + trustedInstallSources = trustedInstallSources.ifEmpty { null } + ) +} + +internal fun ReadableMap.toSuspiciousAppDetectionConfig(): SuspiciousAppDetectionConfig { + val packageNames = this.getArraySafe("packageNames").toSet().ifEmpty { null } + val hashes = this.getArraySafe("hashes").toSet().ifEmpty { null } + val requestedPermissions = this.getNestedArraySafe("requestedPermissions") + .map { it.toSet() }.toSet().ifEmpty { null } + val grantedPermissions = this.getNestedArraySafe("grantedPermissions") + .map { it.toSet() }.toSet().ifEmpty { null } + val malwareScanScope = this.toMalwareScanScope() + val reasonMode = try { + ReasonMode.valueOf(this.getString("reasonMode") ?: "HIGHEST_CONFIDENCE") + } catch (_: IllegalArgumentException) { + ReasonMode.HIGHEST_CONFIDENCE + } + return SuspiciousAppDetectionConfig( + packageNames = packageNames, + hashes = hashes, + requestedPermissions = requestedPermissions, + grantedPermissions = grantedPermissions, + malwareScanScope = malwareScanScope, + reasonMode = reasonMode + ) +} + /** * Convert the Talsec's SuspiciousAppInfo to base64-encoded json array, * which can be then sent to React Native From 1441d280ec6a4da98a8e0bb7d304595d64eb4477 Mon Sep 17 00:00:00 2001 From: martinzigrai Date: Thu, 7 May 2026 15:52:50 +0200 Subject: [PATCH 06/18] chore: update changelog for 4.6.0 --- CHANGELOG.md | 44 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8352d9a..43e28fd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,50 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [4.6.0] - 2026-05-07 + +- Android SDK version: 18.3.0 +- iOS SDK version: 6.14.4 + +### Breaking + +- `SuspiciousAppInfo.reason` (String) renamed to `reasons` (string[]) +- Value `"blacklist"` in `reasons` renamed to `"blocklist"` + +### React Native + +#### Deprecated + +- `blacklistedPackageNames`, `blacklistedHashes`, `suspiciousPermissions`, `whitelistedInstallationSources` are deprecated but remain functional — use `SuspiciousAppDetectionConfig` instead + +### Android + +#### Added + +- Added a new sub-check for `HMA` detection to the root detector +- Added a new sub-check for `KernelSU` detection to the root detector +- Added a new sub-check for `Frida Server` detection to the hook detector +- Added Huawei App Market provider to HMA detection queries +- New API class `SuspiciousAppDetectionConfig` that can be used to configure malware detection +- New API for malware detection configuration in `TalsecConfig`, see `TalsecConfig.Builder#suspiciousAppDetection` + +#### Fixed + +- Fixed `VerifyError` caused by `JaCoCo` bytecode instrumentation +- Fixed a potential cause of crash in the multi-instance detector +- Fixed crash caused by unhandled `SecurityException` thrown by `UsageStatsManager` in root detection +- Fixed manifest merge conflicts in HMA detection providers +- Fixed Java interoperability of `ScreenProtector` methods +- Fixed Kotlin classpath conflicts in SDK dependency resolution (Kotlin 2.0.0) + +#### Changed + +- Fine-tuned `KernelSU` detection +- Fine-tuned hook detection +- Fine-tuned location spoofing detection +- Modified malware incident log structure for better aggregation +- Old malware configuration API methods in `TalsecConfig.Builder` tagged as deprecated (but remain functional): `blacklistedPackageNames`, `blacklistedHashes`, `suspiciousPermissions`, `whitelistedInstallationSources` + ## [4.5.2] - 2026-03-24 - Android SDK version: 18.0.4 From a977d837b64a05410ff411c4ddcf7b35e8f03815 Mon Sep 17 00:00:00 2001 From: martinzigrai Date: Thu, 7 May 2026 15:56:53 +0200 Subject: [PATCH 07/18] refactor: align toMalwareScanScope to operate on scope object directly --- .../java/com/freeraspreactnative/utils/Extensions.kt | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/android/src/main/java/com/freeraspreactnative/utils/Extensions.kt b/android/src/main/java/com/freeraspreactnative/utils/Extensions.kt index 9322d72..e735c0a 100644 --- a/android/src/main/java/com/freeraspreactnative/utils/Extensions.kt +++ b/android/src/main/java/com/freeraspreactnative/utils/Extensions.kt @@ -96,18 +96,15 @@ internal fun PackageInfo.toRNPackageInfo(context: ReactContext): RNPackageInfo { ) } -internal fun ReadableMap.toMalwareScanScope(): MalwareScanScope? { - if (!this.hasKey("malwareScanScope")) return null - val scanScopeMap = this.getMap("malwareScanScope") ?: return null +internal fun ReadableMap.toMalwareScanScope(): MalwareScanScope { val scanScope = try { - ScopeType.valueOf(scanScopeMap.getString("scanScope") ?: "SIDELOADED_ONLY") + ScopeType.valueOf(getString("scanScope") ?: "SIDELOADED_ONLY") } catch (_: IllegalArgumentException) { ScopeType.SIDELOADED_ONLY } - val trustedInstallSources = scanScopeMap.getArraySafe("trustedInstallSources").toSet() return MalwareScanScope( scanScope = scanScope, - trustedInstallSources = trustedInstallSources.ifEmpty { null } + trustedInstallSources = getArraySafe("trustedInstallSources").toSet().ifEmpty { null } ) } @@ -118,7 +115,8 @@ internal fun ReadableMap.toSuspiciousAppDetectionConfig(): SuspiciousAppDetectio .map { it.toSet() }.toSet().ifEmpty { null } val grantedPermissions = this.getNestedArraySafe("grantedPermissions") .map { it.toSet() }.toSet().ifEmpty { null } - val malwareScanScope = this.toMalwareScanScope() + val malwareScanScope = if (this.hasKey("malwareScanScope")) + this.getMap("malwareScanScope")?.toMalwareScanScope() else null val reasonMode = try { ReasonMode.valueOf(this.getString("reasonMode") ?: "HIGHEST_CONFIDENCE") } catch (_: IllegalArgumentException) { From 2391684b3efc4c9cbebf803e6da0dbd731956347 Mon Sep 17 00:00:00 2001 From: martinzigrai Date: Mon, 11 May 2026 14:31:30 +0200 Subject: [PATCH 08/18] fix: add TalsecConfig type annotation to example config --- example/src/App.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/example/src/App.tsx b/example/src/App.tsx index 4d95476..08e7cf6 100644 --- a/example/src/App.tsx +++ b/example/src/App.tsx @@ -5,6 +5,7 @@ import { addToWhitelist, useFreeRasp, type SuspiciousAppInfo, + type TalsecConfig, } from 'freerasp-react-native'; import { DemoApp } from './DemoApp'; import { commonChecks, iosChecks, androidChecks } from './checks'; @@ -29,7 +30,7 @@ const App = () => { })(); }, []); - const config = { + const config: TalsecConfig = { androidConfig: { packageName: 'com.freeraspreactnativeexample', certificateHashes: ['AKoRuyLMM91E7lX/Zqp3u4jMmd0A7hH/Iqozu0TMVd0='], From 1033ca98f019632cfe1e2c5eafe722b6efb79217 Mon Sep 17 00:00:00 2001 From: martinzigrai Date: Mon, 11 May 2026 19:04:01 +0200 Subject: [PATCH 09/18] fix: use positional arguments for non-Kotlin SDK constructors --- .../freeraspreactnative/utils/Extensions.kt | 19 ++++++------------- 1 file changed, 6 insertions(+), 13 deletions(-) diff --git a/android/src/main/java/com/freeraspreactnative/utils/Extensions.kt b/android/src/main/java/com/freeraspreactnative/utils/Extensions.kt index e735c0a..ff74256 100644 --- a/android/src/main/java/com/freeraspreactnative/utils/Extensions.kt +++ b/android/src/main/java/com/freeraspreactnative/utils/Extensions.kt @@ -102,10 +102,8 @@ internal fun ReadableMap.toMalwareScanScope(): MalwareScanScope { } catch (_: IllegalArgumentException) { ScopeType.SIDELOADED_ONLY } - return MalwareScanScope( - scanScope = scanScope, - trustedInstallSources = getArraySafe("trustedInstallSources").toSet().ifEmpty { null } - ) + val trustedInstallSources = getArraySafe("trustedInstallSources").toList().ifEmpty { null } + return MalwareScanScope(scanScope, trustedInstallSources) } internal fun ReadableMap.toSuspiciousAppDetectionConfig(): SuspiciousAppDetectionConfig { @@ -116,20 +114,15 @@ internal fun ReadableMap.toSuspiciousAppDetectionConfig(): SuspiciousAppDetectio val grantedPermissions = this.getNestedArraySafe("grantedPermissions") .map { it.toSet() }.toSet().ifEmpty { null } val malwareScanScope = if (this.hasKey("malwareScanScope")) - this.getMap("malwareScanScope")?.toMalwareScanScope() else null + this.getMap("malwareScanScope")?.toMalwareScanScope() ?: MalwareScanScope(ScopeType.SIDELOADED_ONLY, null) + else + MalwareScanScope(ScopeType.SIDELOADED_ONLY, null) val reasonMode = try { ReasonMode.valueOf(this.getString("reasonMode") ?: "HIGHEST_CONFIDENCE") } catch (_: IllegalArgumentException) { ReasonMode.HIGHEST_CONFIDENCE } - return SuspiciousAppDetectionConfig( - packageNames = packageNames, - hashes = hashes, - requestedPermissions = requestedPermissions, - grantedPermissions = grantedPermissions, - malwareScanScope = malwareScanScope, - reasonMode = reasonMode - ) + return SuspiciousAppDetectionConfig(packageNames, hashes, requestedPermissions, grantedPermissions, malwareScanScope, reasonMode) } /** From d0ec5166cd7750645a84e2e65ea4325f81f3a6ef Mon Sep 17 00:00:00 2001 From: martinzigrai Date: Tue, 12 May 2026 10:18:23 +0200 Subject: [PATCH 10/18] refactor: simplify enum parsing and map reading in Extensions --- .../freeraspreactnative/utils/Extensions.kt | 32 +++++++------------ 1 file changed, 12 insertions(+), 20 deletions(-) diff --git a/android/src/main/java/com/freeraspreactnative/utils/Extensions.kt b/android/src/main/java/com/freeraspreactnative/utils/Extensions.kt index ff74256..a135372 100644 --- a/android/src/main/java/com/freeraspreactnative/utils/Extensions.kt +++ b/android/src/main/java/com/freeraspreactnative/utils/Extensions.kt @@ -96,32 +96,24 @@ internal fun PackageInfo.toRNPackageInfo(context: ReactContext): RNPackageInfo { ) } +private inline fun > String?.toEnumOrDefault(default: T): T = + if (this == null) default + else try { enumValueOf(this) } catch (_: IllegalArgumentException) { default } + internal fun ReadableMap.toMalwareScanScope(): MalwareScanScope { - val scanScope = try { - ScopeType.valueOf(getString("scanScope") ?: "SIDELOADED_ONLY") - } catch (_: IllegalArgumentException) { - ScopeType.SIDELOADED_ONLY - } + val scanScope = getString("scanScope").toEnumOrDefault(ScopeType.SIDELOADED_ONLY) val trustedInstallSources = getArraySafe("trustedInstallSources").toList().ifEmpty { null } return MalwareScanScope(scanScope, trustedInstallSources) } internal fun ReadableMap.toSuspiciousAppDetectionConfig(): SuspiciousAppDetectionConfig { - val packageNames = this.getArraySafe("packageNames").toSet().ifEmpty { null } - val hashes = this.getArraySafe("hashes").toSet().ifEmpty { null } - val requestedPermissions = this.getNestedArraySafe("requestedPermissions") - .map { it.toSet() }.toSet().ifEmpty { null } - val grantedPermissions = this.getNestedArraySafe("grantedPermissions") - .map { it.toSet() }.toSet().ifEmpty { null } - val malwareScanScope = if (this.hasKey("malwareScanScope")) - this.getMap("malwareScanScope")?.toMalwareScanScope() ?: MalwareScanScope(ScopeType.SIDELOADED_ONLY, null) - else - MalwareScanScope(ScopeType.SIDELOADED_ONLY, null) - val reasonMode = try { - ReasonMode.valueOf(this.getString("reasonMode") ?: "HIGHEST_CONFIDENCE") - } catch (_: IllegalArgumentException) { - ReasonMode.HIGHEST_CONFIDENCE - } + val packageNames = getArraySafe("packageNames").toSet().ifEmpty { null } + val hashes = getArraySafe("hashes").toSet().ifEmpty { null } + val requestedPermissions = getNestedArraySafe("requestedPermissions").map { it.toSet() }.toSet().ifEmpty { null } + val grantedPermissions = getNestedArraySafe("grantedPermissions").map { it.toSet() }.toSet().ifEmpty { null } + val malwareScanScope = getMap("malwareScanScope")?.toMalwareScanScope() + ?: MalwareScanScope(ScopeType.SIDELOADED_ONLY, null) + val reasonMode = getString("reasonMode").toEnumOrDefault(ReasonMode.HIGHEST_CONFIDENCE) return SuspiciousAppDetectionConfig(packageNames, hashes, requestedPermissions, grantedPermissions, malwareScanScope, reasonMode) } From 13abef78673d6ba0c6be420268fafb5ae2512c44 Mon Sep 17 00:00:00 2001 From: martinzigrai Date: Tue, 12 May 2026 10:23:14 +0200 Subject: [PATCH 11/18] fix: use emptyList() as default trustedInstallSources in MalwareScanScope --- .../src/main/java/com/freeraspreactnative/utils/Extensions.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/android/src/main/java/com/freeraspreactnative/utils/Extensions.kt b/android/src/main/java/com/freeraspreactnative/utils/Extensions.kt index a135372..54d1391 100644 --- a/android/src/main/java/com/freeraspreactnative/utils/Extensions.kt +++ b/android/src/main/java/com/freeraspreactnative/utils/Extensions.kt @@ -112,7 +112,7 @@ internal fun ReadableMap.toSuspiciousAppDetectionConfig(): SuspiciousAppDetectio val requestedPermissions = getNestedArraySafe("requestedPermissions").map { it.toSet() }.toSet().ifEmpty { null } val grantedPermissions = getNestedArraySafe("grantedPermissions").map { it.toSet() }.toSet().ifEmpty { null } val malwareScanScope = getMap("malwareScanScope")?.toMalwareScanScope() - ?: MalwareScanScope(ScopeType.SIDELOADED_ONLY, null) + ?: MalwareScanScope(ScopeType.SIDELOADED_ONLY, emptyList()) val reasonMode = getString("reasonMode").toEnumOrDefault(ReasonMode.HIGHEST_CONFIDENCE) return SuspiciousAppDetectionConfig(packageNames, hashes, requestedPermissions, grantedPermissions, malwareScanScope, reasonMode) } From 1eb2289feb539ec80ce5a70fdd5900278f9d90fd Mon Sep 17 00:00:00 2001 From: martinzigrai Date: Fri, 15 May 2026 11:42:10 +0200 Subject: [PATCH 12/18] refactor: tighten SuspiciousAppDetectionConfig parsing --- .../freeraspreactnative/utils/Extensions.kt | 13 ++---- src/api/methods/native.ts | 45 ++++++++++++++++++- src/types/types.ts | 4 +- 3 files changed, 49 insertions(+), 13 deletions(-) diff --git a/android/src/main/java/com/freeraspreactnative/utils/Extensions.kt b/android/src/main/java/com/freeraspreactnative/utils/Extensions.kt index 54d1391..0d0df18 100644 --- a/android/src/main/java/com/freeraspreactnative/utils/Extensions.kt +++ b/android/src/main/java/com/freeraspreactnative/utils/Extensions.kt @@ -36,7 +36,7 @@ internal fun ReadableMap.getBooleanSafe(key: String, defaultValue: Boolean = tru return defaultValue } -private inline fun ReadableArray.toPrimitiveArray(): Array { +internal inline fun ReadableArray.toPrimitiveArray(): Array { val output = mutableListOf() for (i in 0 until this.size()) { @@ -96,12 +96,8 @@ internal fun PackageInfo.toRNPackageInfo(context: ReactContext): RNPackageInfo { ) } -private inline fun > String?.toEnumOrDefault(default: T): T = - if (this == null) default - else try { enumValueOf(this) } catch (_: IllegalArgumentException) { default } - internal fun ReadableMap.toMalwareScanScope(): MalwareScanScope { - val scanScope = getString("scanScope").toEnumOrDefault(ScopeType.SIDELOADED_ONLY) + val scanScope = ScopeType.valueOf(getStringThrowing("scanScope")) val trustedInstallSources = getArraySafe("trustedInstallSources").toList().ifEmpty { null } return MalwareScanScope(scanScope, trustedInstallSources) } @@ -111,9 +107,8 @@ internal fun ReadableMap.toSuspiciousAppDetectionConfig(): SuspiciousAppDetectio val hashes = getArraySafe("hashes").toSet().ifEmpty { null } val requestedPermissions = getNestedArraySafe("requestedPermissions").map { it.toSet() }.toSet().ifEmpty { null } val grantedPermissions = getNestedArraySafe("grantedPermissions").map { it.toSet() }.toSet().ifEmpty { null } - val malwareScanScope = getMap("malwareScanScope")?.toMalwareScanScope() - ?: MalwareScanScope(ScopeType.SIDELOADED_ONLY, emptyList()) - val reasonMode = getString("reasonMode").toEnumOrDefault(ReasonMode.HIGHEST_CONFIDENCE) + val malwareScanScope = getMapThrowing("malwareScanScope").toMalwareScanScope() + val reasonMode = ReasonMode.valueOf(getStringThrowing("reasonMode")) return SuspiciousAppDetectionConfig(packageNames, hashes, requestedPermissions, grantedPermissions, malwareScanScope, reasonMode) } diff --git a/src/api/methods/native.ts b/src/api/methods/native.ts index 9d35b52..5c4eb88 100644 --- a/src/api/methods/native.ts +++ b/src/api/methods/native.ts @@ -1,9 +1,50 @@ import { Platform } from 'react-native'; import { FreeraspReactNative } from '../nativeModules'; -import type { TalsecConfig } from '../../types/types'; +import type { + MalwareScanScope, + ReasonMode, + SuspiciousAppDetectionConfig, + TalsecAndroidConfig, + TalsecConfig, +} from '../../types/types'; + +const DEFAULT_MALWARE_SCAN_SCOPE: MalwareScanScope = { + scanScope: 'SIDELOADED_ONLY', +}; + +const DEFAULT_REASON_MODE: ReasonMode = 'HIGHEST_CONFIDENCE'; + +const withSuspiciousAppDetectionDefaults = ( + config: SuspiciousAppDetectionConfig +): SuspiciousAppDetectionConfig => ({ + ...config, + malwareScanScope: config.malwareScanScope ?? DEFAULT_MALWARE_SCAN_SCOPE, + reasonMode: config.reasonMode ?? DEFAULT_REASON_MODE, +}); + +const normalizeAndroidConfig = ( + androidConfig: TalsecAndroidConfig +): TalsecAndroidConfig => { + if (!androidConfig.suspiciousAppDetectionConfig) { + return androidConfig; + } + return { + ...androidConfig, + suspiciousAppDetectionConfig: withSuspiciousAppDetectionDefaults( + androidConfig.suspiciousAppDetectionConfig + ), + }; +}; + +const normalizeConfig = (options: TalsecConfig): TalsecConfig => ({ + ...options, + androidConfig: options.androidConfig + ? normalizeAndroidConfig(options.androidConfig) + : undefined, +}); export const talsecStart = async (options: TalsecConfig): Promise => { - return FreeraspReactNative.talsecStart(options); + return FreeraspReactNative.talsecStart(normalizeConfig(options)); }; export const addToWhitelist = async (packageName: string): Promise => { diff --git a/src/types/types.ts b/src/types/types.ts index 450ef5b..0318102 100644 --- a/src/types/types.ts +++ b/src/types/types.ts @@ -25,8 +25,8 @@ export type SuspiciousAppDetectionConfig = { hashes?: string[]; requestedPermissions?: string[][]; grantedPermissions?: string[][]; - malwareScanScope?: MalwareScanScope; - reasonMode?: ReasonMode; + malwareScanScope: MalwareScanScope; + reasonMode: ReasonMode; }; export type TalsecAndroidConfig = { From aad1926169336375510e714f19e01f9a1de6c0f6 Mon Sep 17 00:00:00 2001 From: martinzigrai Date: Fri, 15 May 2026 11:43:13 +0200 Subject: [PATCH 13/18] feat!: remove deprecated MalwareConfig API --- .../freeraspreactnative/FreeraspReactNativeModule.kt | 8 -------- src/types/types.ts | 12 ------------ 2 files changed, 20 deletions(-) diff --git a/android/src/main/java/com/freeraspreactnative/FreeraspReactNativeModule.kt b/android/src/main/java/com/freeraspreactnative/FreeraspReactNativeModule.kt index dc16106..94eede7 100644 --- a/android/src/main/java/com/freeraspreactnative/FreeraspReactNativeModule.kt +++ b/android/src/main/java/com/freeraspreactnative/FreeraspReactNativeModule.kt @@ -299,14 +299,6 @@ class FreeraspReactNativeModule(private val reactContext: ReactApplicationContex .killOnBypass(config.getBooleanSafe("killOnBypass", false)) .supportedAlternativeStores(androidConfig.getArraySafe("supportedAlternativeStores")) - if (androidConfig.hasKey("malwareConfig")) { - val malwareConfig = androidConfig.getMapThrowing("malwareConfig") - talsecBuilder.whitelistedInstallationSources(malwareConfig.getArraySafe("whitelistedInstallationSources")) - talsecBuilder.blacklistedHashes(malwareConfig.getArraySafe("blacklistedHashes")) - talsecBuilder.blacklistedPackageNames(malwareConfig.getArraySafe("blacklistedPackageNames")) - talsecBuilder.suspiciousPermissions(malwareConfig.getNestedArraySafe("suspiciousPermissions")) - } - if (androidConfig.hasKey("suspiciousAppDetectionConfig")) { val suspiciousAppConfig = androidConfig.getMapThrowing("suspiciousAppDetectionConfig") talsecBuilder.suspiciousAppDetection(suspiciousAppConfig.toSuspiciousAppDetectionConfig()) diff --git a/src/types/types.ts b/src/types/types.ts index 0318102..dea1e51 100644 --- a/src/types/types.ts +++ b/src/types/types.ts @@ -33,7 +33,6 @@ export type TalsecAndroidConfig = { packageName: string; certificateHashes: string[]; supportedAlternativeStores?: string[]; - malwareConfig?: TalsecMalwareConfig; suspiciousAppDetectionConfig?: SuspiciousAppDetectionConfig; }; @@ -42,17 +41,6 @@ export type TalsecIosConfig = { appTeamId: string; }; -export type TalsecMalwareConfig = { - /** @deprecated Use SuspiciousAppDetectionConfig instead */ - blacklistedHashes?: string[]; - /** @deprecated Use SuspiciousAppDetectionConfig instead */ - blacklistedPackageNames?: string[]; - /** @deprecated Use SuspiciousAppDetectionConfig instead */ - suspiciousPermissions?: string[][]; - /** @deprecated Use SuspiciousAppDetectionConfig instead */ - whitelistedInstallationSources?: string[]; -}; - export type SuspiciousAppInfo = { packageInfo: PackageInfo; reasons: string[]; From f6743eb91ff02101dda33bc1197113cab744acaa Mon Sep 17 00:00:00 2001 From: martinzigrai Date: Fri, 15 May 2026 11:48:35 +0200 Subject: [PATCH 14/18] chore: bump to 5.0.0 --- CHANGELOG.md | 12 ++++++++++++ package.json | 2 +- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 43e28fd..4dbbb34 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,18 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [5.0.0] - 2026-05-15 + +### Breaking + +- Removed `MalwareConfig` and `AndroidConfig.malwareConfig` + +### React Native + +#### Removed + +- `TalsecMalwareConfig` type and `TalsecAndroidConfig.malwareConfig` field + ## [4.6.0] - 2026-05-07 - Android SDK version: 18.3.0 diff --git a/package.json b/package.json index e74d399..ba22a47 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "freerasp-react-native", - "version": "4.6.0", + "version": "5.0.0", "description": "React Native plugin for improving app security and threat monitoring on Android and iOS mobile devices.", "main": "lib/commonjs/index", "module": "lib/module/index", From f4d812a1e9f2bb2432b961b07703422caf2b425a Mon Sep 17 00:00:00 2001 From: martinzigrai Date: Fri, 15 May 2026 11:49:27 +0200 Subject: [PATCH 15/18] docs: drop stale MalwareConfig deprecation notes from 4.6.0 --- CHANGELOG.md | 7 ------- 1 file changed, 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4dbbb34..b20349d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -27,12 +27,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - `SuspiciousAppInfo.reason` (String) renamed to `reasons` (string[]) - Value `"blacklist"` in `reasons` renamed to `"blocklist"` -### React Native - -#### Deprecated - -- `blacklistedPackageNames`, `blacklistedHashes`, `suspiciousPermissions`, `whitelistedInstallationSources` are deprecated but remain functional — use `SuspiciousAppDetectionConfig` instead - ### Android #### Added @@ -59,7 +53,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Fine-tuned hook detection - Fine-tuned location spoofing detection - Modified malware incident log structure for better aggregation -- Old malware configuration API methods in `TalsecConfig.Builder` tagged as deprecated (but remain functional): `blacklistedPackageNames`, `blacklistedHashes`, `suspiciousPermissions`, `whitelistedInstallationSources` ## [4.5.2] - 2026-03-24 From 91370b10e3535fe3f75735cce9107d18ebd006a2 Mon Sep 17 00:00:00 2001 From: martinzigrai Date: Fri, 15 May 2026 12:12:46 +0200 Subject: [PATCH 16/18] docs: merge unreleased 4.6.0 changelog into 5.0.0 --- CHANGELOG.md | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b20349d..1fba803 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,8 +7,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [5.0.0] - 2026-05-15 +- Android SDK version: 18.3.0 +- iOS SDK version: 6.14.4 + ### Breaking +- `SuspiciousAppInfo.reason` (String) renamed to `reasons` (string[]) +- Value `"blacklist"` in `reasons` renamed to `"blocklist"` - Removed `MalwareConfig` and `AndroidConfig.malwareConfig` ### React Native @@ -17,16 +22,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - `TalsecMalwareConfig` type and `TalsecAndroidConfig.malwareConfig` field -## [4.6.0] - 2026-05-07 - -- Android SDK version: 18.3.0 -- iOS SDK version: 6.14.4 - -### Breaking - -- `SuspiciousAppInfo.reason` (String) renamed to `reasons` (string[]) -- Value `"blacklist"` in `reasons` renamed to `"blocklist"` - ### Android #### Added From 344c3fb7d8b521bf02a96fafe089016b6dfdea21 Mon Sep 17 00:00:00 2001 From: martinzigrai Date: Mon, 18 May 2026 18:46:10 +0200 Subject: [PATCH 17/18] refactor: rename MalwareScanScope to ScanScope and extract config helpers --- .../freeraspreactnative/utils/Extensions.kt | 6 +-- src/api/methods/native.ts | 44 +------------------ src/types/types.ts | 4 +- src/utils/config.ts | 42 ++++++++++++++++++ 4 files changed, 49 insertions(+), 47 deletions(-) create mode 100644 src/utils/config.ts diff --git a/android/src/main/java/com/freeraspreactnative/utils/Extensions.kt b/android/src/main/java/com/freeraspreactnative/utils/Extensions.kt index 0d0df18..e1b8e3f 100644 --- a/android/src/main/java/com/freeraspreactnative/utils/Extensions.kt +++ b/android/src/main/java/com/freeraspreactnative/utils/Extensions.kt @@ -96,7 +96,7 @@ internal fun PackageInfo.toRNPackageInfo(context: ReactContext): RNPackageInfo { ) } -internal fun ReadableMap.toMalwareScanScope(): MalwareScanScope { +internal fun ReadableMap.toScanScope(): MalwareScanScope { val scanScope = ScopeType.valueOf(getStringThrowing("scanScope")) val trustedInstallSources = getArraySafe("trustedInstallSources").toList().ifEmpty { null } return MalwareScanScope(scanScope, trustedInstallSources) @@ -107,9 +107,9 @@ internal fun ReadableMap.toSuspiciousAppDetectionConfig(): SuspiciousAppDetectio val hashes = getArraySafe("hashes").toSet().ifEmpty { null } val requestedPermissions = getNestedArraySafe("requestedPermissions").map { it.toSet() }.toSet().ifEmpty { null } val grantedPermissions = getNestedArraySafe("grantedPermissions").map { it.toSet() }.toSet().ifEmpty { null } - val malwareScanScope = getMapThrowing("malwareScanScope").toMalwareScanScope() + val scanScope = getMapThrowing("scanScope").toScanScope() val reasonMode = ReasonMode.valueOf(getStringThrowing("reasonMode")) - return SuspiciousAppDetectionConfig(packageNames, hashes, requestedPermissions, grantedPermissions, malwareScanScope, reasonMode) + return SuspiciousAppDetectionConfig(packageNames, hashes, requestedPermissions, grantedPermissions, scanScope, reasonMode) } /** diff --git a/src/api/methods/native.ts b/src/api/methods/native.ts index 5c4eb88..c43a9ca 100644 --- a/src/api/methods/native.ts +++ b/src/api/methods/native.ts @@ -1,47 +1,7 @@ import { Platform } from 'react-native'; import { FreeraspReactNative } from '../nativeModules'; -import type { - MalwareScanScope, - ReasonMode, - SuspiciousAppDetectionConfig, - TalsecAndroidConfig, - TalsecConfig, -} from '../../types/types'; - -const DEFAULT_MALWARE_SCAN_SCOPE: MalwareScanScope = { - scanScope: 'SIDELOADED_ONLY', -}; - -const DEFAULT_REASON_MODE: ReasonMode = 'HIGHEST_CONFIDENCE'; - -const withSuspiciousAppDetectionDefaults = ( - config: SuspiciousAppDetectionConfig -): SuspiciousAppDetectionConfig => ({ - ...config, - malwareScanScope: config.malwareScanScope ?? DEFAULT_MALWARE_SCAN_SCOPE, - reasonMode: config.reasonMode ?? DEFAULT_REASON_MODE, -}); - -const normalizeAndroidConfig = ( - androidConfig: TalsecAndroidConfig -): TalsecAndroidConfig => { - if (!androidConfig.suspiciousAppDetectionConfig) { - return androidConfig; - } - return { - ...androidConfig, - suspiciousAppDetectionConfig: withSuspiciousAppDetectionDefaults( - androidConfig.suspiciousAppDetectionConfig - ), - }; -}; - -const normalizeConfig = (options: TalsecConfig): TalsecConfig => ({ - ...options, - androidConfig: options.androidConfig - ? normalizeAndroidConfig(options.androidConfig) - : undefined, -}); +import type { TalsecConfig } from '../../types/types'; +import { normalizeConfig } from '../../utils/config'; export const talsecStart = async (options: TalsecConfig): Promise => { return FreeraspReactNative.talsecStart(normalizeConfig(options)); diff --git a/src/types/types.ts b/src/types/types.ts index dea1e51..8c5f6a5 100644 --- a/src/types/types.ts +++ b/src/types/types.ts @@ -15,7 +15,7 @@ export type ScopeType = export type ReasonMode = 'ALL' | 'HIGHEST_CONFIDENCE'; -export type MalwareScanScope = { +export type ScanScope = { scanScope: ScopeType; trustedInstallSources?: string[]; }; @@ -25,7 +25,7 @@ export type SuspiciousAppDetectionConfig = { hashes?: string[]; requestedPermissions?: string[][]; grantedPermissions?: string[][]; - malwareScanScope: MalwareScanScope; + scanScope: ScanScope; reasonMode: ReasonMode; }; diff --git a/src/utils/config.ts b/src/utils/config.ts new file mode 100644 index 0000000..f1cf1aa --- /dev/null +++ b/src/utils/config.ts @@ -0,0 +1,42 @@ +import type { + ReasonMode, + ScanScope, + SuspiciousAppDetectionConfig, + TalsecAndroidConfig, + TalsecConfig, +} from '../types/types'; + +const DEFAULT_SCAN_SCOPE: ScanScope = { + scanScope: 'SIDELOADED_ONLY', +}; + +const DEFAULT_REASON_MODE: ReasonMode = 'HIGHEST_CONFIDENCE'; + +export const withDefaults = ( + config: SuspiciousAppDetectionConfig +): SuspiciousAppDetectionConfig => ({ + ...config, + scanScope: config.scanScope ?? DEFAULT_SCAN_SCOPE, + reasonMode: config.reasonMode ?? DEFAULT_REASON_MODE, +}); + +export const normalizeAndroidConfig = ( + androidConfig: TalsecAndroidConfig +): TalsecAndroidConfig => { + if (!androidConfig.suspiciousAppDetectionConfig) { + return androidConfig; + } + return { + ...androidConfig, + suspiciousAppDetectionConfig: withDefaults( + androidConfig.suspiciousAppDetectionConfig + ), + }; +}; + +export const normalizeConfig = (options: TalsecConfig): TalsecConfig => ({ + ...options, + androidConfig: options.androidConfig + ? normalizeAndroidConfig(options.androidConfig) + : undefined, +}); From e7bb8bda4d0f0d6534d92a54b561ea6f130a4d13 Mon Sep 17 00:00:00 2001 From: martinzigrai Date: Mon, 18 May 2026 19:01:09 +0200 Subject: [PATCH 18/18] fix ci --- example/src/App.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/example/src/App.tsx b/example/src/App.tsx index 08e7cf6..9a1fda2 100644 --- a/example/src/App.tsx +++ b/example/src/App.tsx @@ -47,7 +47,7 @@ const App = () => { ['android.permission.BATTERY_STATS'], ], grantedPermissions: [['android.permission.ACCESS_FINE_LOCATION']], - malwareScanScope: { + scanScope: { scanScope: 'SIDELOADED_AND_SYSTEM_EXCLUDE_OEM', trustedInstallSources: ['com.apkpure.aegon'], },