Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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).

## [7.6.0] - 2026-05-07

- Android SDK version: 18.3.0
- iOS SDK version: 6.14.4

### Breaking

- `SuspiciousAppInfo.reason` (String) renamed to `reasons` (List\<String\>)
- Value `"blacklist"` in `reasons` renamed to `"blocklist"`

### Flutter

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

## [7.5.1] - 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 @@ -3,7 +3,7 @@ version '1.0-SNAPSHOT'

buildscript {
ext.kotlin_version = '2.1.0'
ext.talsec_version = '18.0.4'
ext.talsec_version = '18.3.0'
repositories {
google()
mavenCentral()
Expand Down
52 changes: 51 additions & 1 deletion android/src/main/kotlin/com/aheaditec/freerasp/Extensions.kt
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,13 @@ import android.content.Context
import android.content.pm.PackageInfo
import android.os.Build
import com.aheaditec.talsec_security.security.api.ExternalIdResult
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 io.flutter.plugin.common.MethodChannel
import org.json.JSONObject
import com.aheaditec.freerasp.generated.PackageInfo as FlutterPackageInfo
import com.aheaditec.freerasp.generated.SuspiciousAppInfo as FlutterSuspiciousAppInfo

Expand Down Expand Up @@ -33,7 +38,7 @@ internal inline fun runResultCatching(result: MethodChannel.Result, block: () ->
* this [SuspiciousAppInfo].
*/
internal fun SuspiciousAppInfo.toPigeon(context: Context): FlutterSuspiciousAppInfo {
return FlutterSuspiciousAppInfo(this.packageInfo.toPigeon(context), this.reason)
return FlutterSuspiciousAppInfo(this.packageInfo.toPigeon(context), this.reasons.toList())
}

/**
Expand Down Expand Up @@ -83,3 +88,48 @@ internal fun ExternalIdResult.resolve(result: MethodChannel.Result) {
is ExternalIdResult.Error -> result.error("external-id-failure", this.errorMsg, null)
}
}

internal fun JSONObject.toMalwareScanScope(): MalwareScanScope {
val scopeTypeStr = optString("scanScope", "SIDELOADED_ONLY")
val scanScope = runCatching { ScopeType.valueOf(scopeTypeStr) }.getOrDefault(ScopeType.SIDELOADED_ONLY)
val trustedInstallSources = optJSONArray("trustedInstallSources")?.let { arr ->
(0 until arr.length()).map { arr.getString(it) }.toSet()
}
return MalwareScanScope(scanScope = scanScope, trustedInstallSources = trustedInstallSources)
}

internal fun JSONObject.toSuspiciousAppDetectionConfig(): SuspiciousAppDetectionConfig {
val packageNames = optJSONArray("packageNames")?.let { arr ->
(0 until arr.length()).map { arr.getString(it) }.toSet()
}
val hashes = optJSONArray("hashes")?.let { arr ->
(0 until arr.length()).map { arr.getString(it) }.toSet()
}
val requestedPermissions = optJSONArray("requestedPermissions")?.let { outer ->
(0 until outer.length()).map { i ->
val inner = outer.getJSONArray(i)
(0 until inner.length()).map { j -> inner.getString(j) }.toSet()
}.toSet()
}
val grantedPermissions = optJSONArray("grantedPermissions")?.let { outer ->
(0 until outer.length()).map { i ->
val inner = outer.getJSONArray(i)
(0 until inner.length()).map { j -> inner.getString(j) }.toSet()
}.toSet()
}
val malwareScanScope = optJSONObject("malwareScanScope")?.toMalwareScanScope()
val reasonModeStr = optString("reasonMode")
val reasonMode = if (reasonModeStr.isNullOrEmpty()) {
ReasonMode.HIGHEST_CONFIDENCE
} else {
runCatching { ReasonMode.valueOf(reasonModeStr) }.getOrDefault(ReasonMode.HIGHEST_CONFIDENCE)
}
return SuspiciousAppDetectionConfig(
packageNames = packageNames,
hashes = hashes,
requestedPermissions = requestedPermissions,
grantedPermissions = grantedPermissions,
malwareScanScope = malwareScanScope,
reasonMode = reasonMode,
)
}
11 changes: 8 additions & 3 deletions android/src/main/kotlin/com/aheaditec/freerasp/Utils.kt
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ internal object Utils {
val alternativeStores = androidConfig.extractArray<String>("supportedStores")
val malwareConfig = parseMalwareConfig(androidConfig)

return TalsecConfig.Builder(packageName, certificateHashes)
val builder = TalsecConfig.Builder(packageName, certificateHashes)
.watcherMail(watcherMail)
.supportedAlternativeStores(alternativeStores)
.prod(isProd)
Expand All @@ -47,7 +47,12 @@ internal object Utils {
.blacklistedHashes(malwareConfig.blacklistedHashes)
.suspiciousPermissions(malwareConfig.suspiciousPermissions)
.whitelistedInstallationSources(malwareConfig.whitelistedInstallationSources)
.build()

androidConfig.optJSONObject("suspiciousAppDetectionConfig")?.let {
builder.suspiciousAppDetection(it.toSuspiciousAppDetectionConfig())
}

return builder.build()
}

private fun parseMalwareConfig(androidConfig: JSONObject): MalwareConfig {
Expand Down Expand Up @@ -175,4 +180,4 @@ private inline fun <reified T> processArray(jsonArray: JSONArray): Array<T> {
}

return list.toTypedArray()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -62,20 +62,20 @@ data class PackageInfo (
/** Generated class from Pigeon that represents data sent in messages. */
data class SuspiciousAppInfo (
val packageInfo: PackageInfo,
val reason: String
val reasons: List<String>
)
{
companion object {
fun fromList(pigeonVar_list: List<Any?>): SuspiciousAppInfo {
val packageInfo = pigeonVar_list[0] as PackageInfo
val reason = pigeonVar_list[1] as String
return SuspiciousAppInfo(packageInfo, reason)
val reasons = pigeonVar_list[1] as List<String>
return SuspiciousAppInfo(packageInfo, reasons)
}
}
fun toList(): List<Any?> {
return listOf(
packageInfo,
reason,
reasons,
)
}
}
Expand Down
10 changes: 7 additions & 3 deletions example/lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -38,12 +38,16 @@ Future<void> _initializeTalsec() async {
packageName: 'com.aheaditec.freeraspExample',
signingCertHashes: ['AKoRuyLMM91E7lX/Zqp3u4jMmd0A7hH/Iqozu0TMVd0='],
supportedStores: ['com.sec.android.app.samsungapps'],
malwareConfig: MalwareConfig(
blacklistedPackageNames: ['com.aheaditec.freeraspExample'],
suspiciousPermissions: [
suspiciousAppDetectionConfig: SuspiciousAppDetectionConfig(
packageNames: ['com.aheaditec.freeraspExample'],
requestedPermissions: [
['android.permission.CAMERA'],
['android.permission.READ_SMS', 'android.permission.READ_CONTACTS'],
],
malwareScanScope: MalwareScanScope(
scanScope: ScopeType.sideloadedOnly,
),
reasonMode: ReasonMode.highestConfidence,
),
),
iosConfig: IOSConfig(
Expand Down
2 changes: 1 addition & 1 deletion example/lib/widgets/malware_bottom_sheet.dart
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ class MalwareListTile extends StatelessWidget {

return ListTile(
title: Text(malware.packageInfo.packageName),
subtitle: Text('Reason: ${malware.reason}'),
subtitle: Text('Reasons: ${malware.reasons.join(', ')}'),
leading: appIcon,
);
},
Expand Down
8 changes: 4 additions & 4 deletions lib/src/generated/talsec_pigeon_api.g.dart
Original file line number Diff line number Diff line change
Expand Up @@ -63,25 +63,25 @@ class PackageInfo {
class SuspiciousAppInfo {
SuspiciousAppInfo({
required this.packageInfo,
required this.reason,
required this.reasons,
});

PackageInfo packageInfo;

String reason;
List<String> reasons;

Object encode() {
return <Object?>[
packageInfo,
reason,
reasons,
];
}

static SuspiciousAppInfo decode(Object result) {
result as List<Object?>;
return SuspiciousAppInfo(
packageInfo: result[0]! as PackageInfo,
reason: result[1]! as String,
reasons: (result[1] as List<Object?>?)!.cast<String>(),
);
}
}
Expand Down
5 changes: 5 additions & 0 deletions lib/src/models/android_config.dart
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ class AndroidConfig {
required this.signingCertHashes,
this.supportedStores = const [],
this.malwareConfig,
this.suspiciousAppDetectionConfig,
}) {
ConfigVerifier.verifyAndroid(this);
}
Expand All @@ -34,5 +35,9 @@ class AndroidConfig {
final List<String> supportedStores;

/// Malware configuration for Android.
@Deprecated('Use suspiciousAppDetectionConfig instead')
final MalwareConfig? malwareConfig;

/// Suspicious app detection configuration for Android.
final SuspiciousAppDetectionConfig? suspiciousAppDetectionConfig;
}
10 changes: 9 additions & 1 deletion lib/src/models/android_config.g.dart

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 8 additions & 0 deletions lib/src/models/malware_config.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,13 @@ part 'malware_config.g.dart';
class MalwareConfig {
/// Creates a new instance of [MalwareConfig].
MalwareConfig({
@Deprecated('Use SuspiciousAppDetectionConfig instead')
this.blacklistedPackageNames = const [],
@Deprecated('Use SuspiciousAppDetectionConfig instead')
this.blacklistedHashes = const [],
@Deprecated('Use SuspiciousAppDetectionConfig instead')
this.suspiciousPermissions = const [],
@Deprecated('Use SuspiciousAppDetectionConfig instead')
this.whitelistedInstallationSources = const [],
});

Expand All @@ -21,14 +25,18 @@ class MalwareConfig {
Map<String, dynamic> toJson() => _$MalwareConfigToJson(this);

/// List of blocklisted applications with given package name.
@Deprecated('Use SuspiciousAppDetectionConfig instead')
final List<String> blacklistedPackageNames;

/// List of blocklisted applications with given hash.
@Deprecated('Use SuspiciousAppDetectionConfig instead')
final List<String> blacklistedHashes;

/// List of blocklisted applications with given permissions.
@Deprecated('Use SuspiciousAppDetectionConfig instead')
final List<List<String>> suspiciousPermissions;

/// List of whitelisted installation sources.
@Deprecated('Use SuspiciousAppDetectionConfig instead')
final List<String> whitelistedInstallationSources;
}
1 change: 1 addition & 0 deletions lib/src/models/models.dart
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
export 'android_config.dart';
export 'ios_config.dart';
export 'malware_config.dart';
export 'suspicious_app_detection_config.dart';
export 'talsec_config.dart';
Loading
Loading