Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
6b1d5c8
feat: bump Android SDK to 18.3.0
martinzigrai May 7, 2026
1058b9e
feat!: rename reason to reasons in SuspiciousAppInfo
martinzigrai May 7, 2026
bd7894b
feat!: deprecate old malware config fields
martinzigrai May 7, 2026
56487c3
feat: add SuspiciousAppDetectionConfig to API
martinzigrai May 7, 2026
ba56ae0
refactor: extract SuspiciousAppDetectionConfig parsing to utils
martinzigrai May 7, 2026
6909054
chore: update changelog for 7.6.0
martinzigrai May 7, 2026
be0649a
style: format suspicious_app_detection_config.g.dart
martinzigrai May 11, 2026
e4258fc
fix: resolve lint issues - deprecated_consistency and prefer_const_co…
martinzigrai May 11, 2026
ba45995
style: format android_config.dart
martinzigrai May 11, 2026
cccbd74
fix: use positional args and correct types for MalwareScanScope and S…
martinzigrai May 12, 2026
a545b78
Merge branch 'master' into rc/7.6.0
tompsota May 13, 2026
393fa70
feat!: remove deprecated MalwareConfig API
martinzigrai May 13, 2026
8cf8248
refactor: tighten SuspiciousAppDetectionConfig parsing
martinzigrai May 13, 2026
aafe0d2
test: cover SuspiciousAppDetectionConfig and related models
martinzigrai May 13, 2026
e065364
chore: bump to 8.0.0
martinzigrai May 13, 2026
6d9947b
style: format suspicious_app_detection_config_test.dart
martinzigrai May 13, 2026
44e000a
docs: drop stale TalsecConfig.Builder deprecation note
martinzigrai May 13, 2026
74f7cce
chore: changelog + comments update
martinzigrai May 15, 2026
bd99a7f
Revert "chore: changelog + comments update"
martinzigrai May 15, 2026
e3dcac7
docs: CHANGELOG update
martinzigrai May 15, 2026
11f8e45
refactor: rename MalwareScanScope to ScanScope and malwareScanScope f…
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).

## [8.0.0] - 2026-05-13

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

### Breaking
Comment thread
tompsota marked this conversation as resolved.

- `SuspiciousAppInfo.reason` (String) renamed to `reasons` (List\<String\>)
- Value `"blacklist"` in `reasons` renamed to `"blocklist"`
- Removed `MalwareConfig` and `AndroidConfig.malwareConfig` — use `SuspiciousAppDetectionConfig` instead

### Flutter

#### Removed

- `MalwareConfig` class and `AndroidConfig.malwareConfig` field — 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

## [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
36 changes: 35 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,14 @@ 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.JSONArray
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 +39,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 +89,31 @@ internal fun ExternalIdResult.resolve(result: MethodChannel.Result) {
is ExternalIdResult.Error -> result.error("external-id-failure", this.errorMsg, null)
}
}

internal fun JSONObject.toScanScope(): MalwareScanScope {
val scanScope = ScopeType.valueOf(getString("scanScope"))
val trustedInstallSources = optJSONArray("trustedInstallSources")
?.let { processArray<String>(it).asList() }
return MalwareScanScope(scanScope, trustedInstallSources)
}

internal fun JSONObject.toSuspiciousAppDetectionConfig(): SuspiciousAppDetectionConfig {
val packageNames = optJSONArray("packageNames")
?.let { processArray<String>(it).toMutableSet() }
val hashes = optJSONArray("hashes")
?.let { processArray<String>(it).toMutableSet() }
val requestedPermissions = optJSONArray("requestedPermissions")
?.let { processArray<Array<String>>(it).mapTo(mutableSetOf()) { it.toMutableSet() } }
val grantedPermissions = optJSONArray("grantedPermissions")
?.let { processArray<Array<String>>(it).mapTo(mutableSetOf()) { it.toMutableSet() } }
val scanScope = getJSONObject("scanScope").toScanScope()
val reasonMode = ReasonMode.valueOf(getString("reasonMode"))
return SuspiciousAppDetectionConfig(
packageNames,
hashes,
requestedPermissions,
grantedPermissions,
scanScope,
reasonMode,
)
}
38 changes: 7 additions & 31 deletions android/src/main/kotlin/com/aheaditec/freerasp/Utils.kt
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,6 @@ import org.json.JSONObject
import java.io.ByteArrayOutputStream

internal object Utils {
@Suppress("ArrayInDataClass")
data class MalwareConfig(
val blacklistedPackageNames: Array<String>,
val blacklistedHashes: Array<String>,
val suspiciousPermissions: Array<Array<String>>,
val whitelistedInstallationSources: Array<String>
)

fun toTalsecConfigThrowing(configJson: String?): TalsecConfig {
if (configJson == null) {
throw JSONException("Configuration is null")
Expand All @@ -36,36 +28,20 @@ internal object Utils {
val packageName = androidConfig.getString("packageName")
val certificateHashes = androidConfig.extractArray<String>("signingCertHashes")
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)
.killOnBypass(killOnBypass)
.blacklistedPackageNames(malwareConfig.blacklistedPackageNames)
.blacklistedHashes(malwareConfig.blacklistedHashes)
.suspiciousPermissions(malwareConfig.suspiciousPermissions)
.whitelistedInstallationSources(malwareConfig.whitelistedInstallationSources)
.build()
}

private fun parseMalwareConfig(androidConfig: JSONObject): MalwareConfig {
if (!androidConfig.has("malwareConfig")) {
return MalwareConfig(emptyArray(), emptyArray(), emptyArray(), emptyArray())
androidConfig.optJSONObject("suspiciousAppDetectionConfig")?.let {
builder.suspiciousAppDetection(it.toSuspiciousAppDetectionConfig())
}

val malwareConfig = androidConfig.getJSONObject("malwareConfig")

return MalwareConfig(
malwareConfig.extractArray("blacklistedPackageNames"),
malwareConfig.extractArray("blacklistedHashes"),
malwareConfig.extractArray<Array<String>>("suspiciousPermissions"),
malwareConfig.extractArray("whitelistedInstallationSources")
)
return builder.build()
}


/**
* Retrieves the package name of the installer for a given app package.
*
Expand Down Expand Up @@ -145,11 +121,11 @@ internal object Utils {
}
}

private inline fun <reified T> JSONObject.extractArray(key: String): Array<T> {
internal inline fun <reified T> JSONObject.extractArray(key: String): Array<T> {
return this.optJSONArray(key)?.let { processArray(it) } ?: emptyArray()
}

private inline fun <reified T> processArray(jsonArray: JSONArray): Array<T> {
internal inline fun <reified T> processArray(jsonArray: JSONArray): Array<T> {
val list = mutableListOf<T>()

for (i in 0 until jsonArray.length()) {
Expand All @@ -175,4 +151,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: const SuspiciousAppDetectionConfig(
packageNames: ['com.aheaditec.freeraspExample'],
requestedPermissions: [
['android.permission.CAMERA'],
['android.permission.READ_SMS', 'android.permission.READ_CONTACTS'],
],
scanScope: ScanScope(
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
6 changes: 3 additions & 3 deletions lib/src/models/android_config.dart
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ class AndroidConfig {
required this.packageName,
required this.signingCertHashes,
this.supportedStores = const [],
this.malwareConfig,
this.suspiciousAppDetectionConfig,
}) {
ConfigVerifier.verifyAndroid(this);
}
Expand All @@ -33,6 +33,6 @@ class AndroidConfig {
/// List of supported sources where application can be installed from.
final List<String> supportedStores;

/// Malware configuration for Android.
final MalwareConfig? malwareConfig;
/// Suspicious app detection configuration for Android.
final SuspiciousAppDetectionConfig? suspiciousAppDetectionConfig;
}
9 changes: 5 additions & 4 deletions lib/src/models/android_config.g.dart
Comment thread
tompsota marked this conversation as resolved.

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

34 changes: 0 additions & 34 deletions lib/src/models/malware_config.dart

This file was deleted.

38 changes: 0 additions & 38 deletions lib/src/models/malware_config.g.dart

This file was deleted.

2 changes: 1 addition & 1 deletion lib/src/models/models.dart
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
export 'android_config.dart';
export 'ios_config.dart';
export 'malware_config.dart';
export 'suspicious_app_detection_config.dart';
export 'talsec_config.dart';
Loading