Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
7dd8136
feat: bump Android SDK to 18.3.0
martinzigrai May 7, 2026
9cb8b0f
feat!: rename reason to reasons in SuspiciousAppInfo
martinzigrai May 7, 2026
e53f51f
feat!: deprecate old malware config fields
martinzigrai May 7, 2026
37c9b42
feat: add SuspiciousAppDetectionConfig to API
martinzigrai May 7, 2026
31464c4
refactor: extract SuspiciousAppDetectionConfig parsing to utils
martinzigrai May 7, 2026
1441d28
chore: update changelog for 4.6.0
martinzigrai May 7, 2026
a977d83
refactor: align toMalwareScanScope to operate on scope object directly
martinzigrai May 7, 2026
2391684
fix: add TalsecConfig type annotation to example config
martinzigrai May 11, 2026
1033ca9
fix: use positional arguments for non-Kotlin SDK constructors
martinzigrai May 11, 2026
d0ec516
refactor: simplify enum parsing and map reading in Extensions
martinzigrai May 12, 2026
13abef7
fix: use emptyList() as default trustedInstallSources in MalwareScanS…
martinzigrai May 12, 2026
c1bf5b5
Merge branch 'master' into rc/4.6.0
tompsota May 13, 2026
1eb2289
refactor: tighten SuspiciousAppDetectionConfig parsing
martinzigrai May 15, 2026
aad1926
feat!: remove deprecated MalwareConfig API
martinzigrai May 15, 2026
f6743eb
chore: bump to 5.0.0
martinzigrai May 15, 2026
f4d812a
docs: drop stale MalwareConfig deprecation notes from 4.6.0
martinzigrai May 15, 2026
91370b1
docs: merge unreleased 4.6.0 changelog into 5.0.0
martinzigrai May 15, 2026
344c3fb
refactor: rename MalwareScanScope to ScanScope and extract config hel…
martinzigrai May 18, 2026
e7bb8bd
fix ci
martinzigrai May 18, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 44 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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).

## [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

#### Removed

- `TalsecMalwareConfig` type and `TalsecAndroidConfig.malwareConfig` field

### 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

## [4.5.2] - 2026-03-24

- Android SDK version: 18.0.4
Expand Down
2 changes: 1 addition & 1 deletion android/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -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()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down Expand Up @@ -298,12 +299,9 @@ 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())
}

return talsecBuilder.build()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import kotlinx.serialization.Serializable
@Serializable
data class RNSuspiciousAppInfo(
val packageInfo: RNPackageInfo,
val reason: String,
val reasons: Set<String>,
val permissions: Set<String>?
)

Expand Down
24 changes: 22 additions & 2 deletions android/src/main/java/com/freeraspreactnative/utils/Extensions.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -32,7 +36,7 @@ internal fun ReadableMap.getBooleanSafe(key: String, defaultValue: Boolean = tru
return defaultValue
}

private inline fun <reified T> ReadableArray.toPrimitiveArray(): Array<T> {
internal inline fun <reified T> ReadableArray.toPrimitiveArray(): Array<T> {
val output = mutableListOf<T>()

for (i in 0 until this.size()) {
Expand Down Expand Up @@ -74,7 +78,7 @@ internal fun ReadableMap.getNestedArraySafe(key: String): Array<Array<String>> {
internal fun SuspiciousAppInfo.toRNSuspiciousAppInfo(context: ReactContext): RNSuspiciousAppInfo {
return RNSuspiciousAppInfo(
packageInfo = this.packageInfo.toRNPackageInfo(context),
reason = this.reason,
reasons = this.reasons,
permissions = this.permissions
)
}
Expand All @@ -92,6 +96,22 @@ internal fun PackageInfo.toRNPackageInfo(context: ReactContext): RNPackageInfo {
)
}

internal fun ReadableMap.toScanScope(): MalwareScanScope {
val scanScope = ScopeType.valueOf(getStringThrowing("scanScope"))
val trustedInstallSources = getArraySafe("trustedInstallSources").toList().ifEmpty { null }
return MalwareScanScope(scanScope, trustedInstallSources)
}

internal fun ReadableMap.toSuspiciousAppDetectionConfig(): SuspiciousAppDetectionConfig {
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 scanScope = getMapThrowing("scanScope").toScanScope()
val reasonMode = ReasonMode.valueOf(getStringThrowing("reasonMode"))
return SuspiciousAppDetectionConfig(packageNames, hashes, requestedPermissions, grantedPermissions, scanScope, reasonMode)
}

/**
* Convert the Talsec's SuspiciousAppInfo to base64-encoded json array,
* which can be then sent to React Native
Expand Down
18 changes: 12 additions & 6 deletions example/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -29,23 +30,28 @@ const App = () => {
})();
}, []);

const config = {
const config: TalsecConfig = {
androidConfig: {
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',
],
['android.permission.BLUETOOTH'],
['android.permission.BATTERY_STATS'],
],
whitelistedInstallationSources: ['com.apkpure.aegon'],
grantedPermissions: [['android.permission.ACCESS_FINE_LOCATION']],
scanScope: {
scanScope: 'SIDELOADED_AND_SYSTEM_EXCLUDE_OEM',
trustedInstallSources: ['com.apkpure.aegon'],
},
reasonMode: 'HIGHEST_CONFIDENCE',
},
},
iosConfig: {
Expand Down
4 changes: 2 additions & 2 deletions example/src/MalwareItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -90,8 +90,8 @@ export const MalwareItem: React.FC<{ app: SuspiciousAppInfo }> = ({ app }) => {
<Text style={styles.listItem}>
{app.packageInfo.installerStore ?? 'Not specified'}
</Text>
<Text style={styles.listItemTitle}>Detection reason:</Text>
<Text style={styles.listItem}>{app.reason}</Text>
<Text style={styles.listItemTitle}>Detection reasons:</Text>
<Text style={styles.listItem}>{app.reasons.join(', ')}</Text>
<Text style={styles.listItemTitle}>Granted permissions:</Text>
<Text style={styles.listItem}>
{app.permissions?.join(', ') ?? 'Not specified'}
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "freerasp-react-native",
"version": "4.5.2",
"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",
Expand Down
3 changes: 2 additions & 1 deletion src/api/methods/native.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import { Platform } from 'react-native';
import { FreeraspReactNative } from '../nativeModules';
import type { TalsecConfig } from '../../types/types';
import { normalizeConfig } from '../../utils/config';

export const talsecStart = async (options: TalsecConfig): Promise<string> => {
return FreeraspReactNative.talsecStart(options);
return FreeraspReactNative.talsecStart(normalizeConfig(options));
};

export const addToWhitelist = async (packageName: string): Promise<boolean> => {
Expand Down
34 changes: 25 additions & 9 deletions src/types/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,28 +6,44 @@ 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 ScanScope = {
scanScope: ScopeType;
trustedInstallSources?: string[];
};

export type SuspiciousAppDetectionConfig = {
packageNames?: string[];
hashes?: string[];
requestedPermissions?: string[][];
grantedPermissions?: string[][];
scanScope: ScanScope;
reasonMode: ReasonMode;
};

export type TalsecAndroidConfig = {
packageName: string;
certificateHashes: string[];
supportedAlternativeStores?: string[];
malwareConfig?: TalsecMalwareConfig;
suspiciousAppDetectionConfig?: SuspiciousAppDetectionConfig;
};

export type TalsecIosConfig = {
appBundleId: string;
appTeamId: string;
};

export type TalsecMalwareConfig = {
blacklistedHashes?: string[];
blacklistedPackageNames?: string[];
suspiciousPermissions?: string[][];
whitelistedInstallationSources?: string[];
};

export type SuspiciousAppInfo = {
packageInfo: PackageInfo;
reason: string;
reasons: string[];
permissions?: string[];
};

Expand Down
42 changes: 42 additions & 0 deletions src/utils/config.ts
Original file line number Diff line number Diff line change
@@ -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,
});
2 changes: 1 addition & 1 deletion src/utils/malware.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
};