Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
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.toMalwareScanScope(): 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 malwareScanScope = getMapThrowing("malwareScanScope").toMalwareScanScope()
val reasonMode = ReasonMode.valueOf(getStringThrowing("reasonMode"))
return SuspiciousAppDetectionConfig(packageNames, hashes, requestedPermissions, grantedPermissions, malwareScanScope, 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']],
malwareScanScope: {
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
45 changes: 43 additions & 2 deletions src/api/methods/native.ts
Original file line number Diff line number Diff line change
@@ -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<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 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 = {
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
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;
};