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).

## [1.2.0] - 2026-05-07

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

### Breaking

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

### Kotlin Multiplatform

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

## [1.1.0] - 2025-03-25

- Android SDK version: 18.0.4
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import com.freeraspkmp.model.FreeRaspEvent
import com.freeraspkmp.model.SuspiciousAppInfo
import com.freeraspkmp.model.config.AndroidConfig
import com.freeraspkmp.model.config.IOSConfig
import com.freeraspkmp.model.config.MalwareConfig
import com.freeraspkmp.model.config.SuspiciousAppDetectionConfig
import com.freeraspkmp.model.config.freeraspConfig
import com.jetbrains.example.model.initialChecks
import com.jetbrains.example.model.toCheckId
Expand All @@ -33,8 +33,8 @@ fun App() {
androidConfig = AndroidConfig(
packageName = "com.jetbrains.example",
certificateHashes = listOf("K/iFV7+CypnATFWcrUVM6aUIB5gnU2xwzRJOiKJJqPw="),
malwareConfig = MalwareConfig(
blacklistedPackageNames = listOf("com.google.android.youtube")
suspiciousAppDetectionConfig = SuspiciousAppDetectionConfig(
packageNames = listOf("com.google.android.youtube")
)
),
iosConfig = IOSConfig(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ private fun MalwareAppItem(app: SuspiciousAppInfo) {
color = MaterialTheme.colorScheme.onSurfaceVariant,
)
Text(
text = "Reason: ${app.reason}",
text = "Reasons: ${app.reasons.joinToString(", ")}",
style = MaterialTheme.typography.bodySmall,
color = ThreatRed,
)
Expand Down
4 changes: 2 additions & 2 deletions library/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ fun getVariable(name: String): String {


group = "com.aheaditec.talsec.security"
version = "1.1.0"
version = "1.2.0"
kotlin {
targets.all {
compilations.all {
Expand Down Expand Up @@ -77,7 +77,7 @@ kotlin {
languageSettings.optIn("kotlin.ExperimentalMultiplatform")

dependencies{
implementation("com.aheaditec.talsec.security:TalsecSecurity-Community-KMP:18.0.4")
implementation("com.aheaditec.talsec.security:TalsecSecurity-Community-KMP:18.3.0")
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.8.0")
implementation("androidx.startup:startup-runtime:1.2.0")
implementation("androidx.annotation:annotation:1.9.1")
Expand Down
46 changes: 45 additions & 1 deletion library/src/androidMain/kotlin/utils/config_mapper.android.kt
Original file line number Diff line number Diff line change
@@ -1,9 +1,18 @@
package com.freeraspkmp.android.utils

import com.aheaditec.talsec_security.security.api.TalsecConfig
import com.freeraspkmp.model.config.MalwareScanScope
import com.freeraspkmp.model.config.ReasonMode
import com.freeraspkmp.model.config.ScopeType
import com.freeraspkmp.model.config.SuspiciousAppDetectionConfig
import com.freeraspkmp.model.config.freeraspConfig
import com.freeraspkmp.model.exception.FreeraspKMPException
import com.aheaditec.talsec_security.security.api.SuspiciousAppDetectionConfig as NativeSuspiciousAppDetectionConfig
import com.aheaditec.talsec_security.security.api.MalwareScanScope as NativeMalwareScanScope
import com.aheaditec.talsec_security.security.api.ScopeType as NativeScopeType
import com.aheaditec.talsec_security.security.api.ReasonMode as NativeReasonMode

@Suppress("DEPRECATION")
fun freeraspConfig.toNativeConfig(): TalsecConfig {
val androidConfig = this.androidConfig ?: throw FreeraspKMPException("AndroidConfig is required on the Android platform but was null.")

Expand Down Expand Up @@ -43,7 +52,42 @@ fun freeraspConfig.toNativeConfig(): TalsecConfig {
whitelistedInstallationSources(malware.whitelistedInstallationSources.toTypedArray())
}
}

androidConfig.suspiciousAppDetectionConfig?.let {
suspiciousAppDetection(it.toNative())
}
}
return builder.build()

}
}

internal fun SuspiciousAppDetectionConfig.toNative(): NativeSuspiciousAppDetectionConfig =
NativeSuspiciousAppDetectionConfig(
packageNames = packageNames?.toSet(),
hashes = hashes?.toSet(),
requestedPermissions = requestedPermissions?.map { it.toSet() }?.toSet(),
grantedPermissions = grantedPermissions?.map { it.toSet() }?.toSet(),
malwareScanScope = malwareScanScope?.toNative(),
reasonMode = reasonMode?.toNative()
)

internal fun MalwareScanScope.toNative(): NativeMalwareScanScope =
NativeMalwareScanScope(
scanScope = scanScope.toNative(),
trustedInstallSources = trustedInstallSources
)

internal fun ScopeType.toNative(): NativeScopeType =
when (this) {
ScopeType.SIDELOADED_ONLY -> NativeScopeType.SIDELOADED_ONLY
ScopeType.SIDELOADED_AND_SYSTEM_EXCLUDE_OEM -> NativeScopeType.SIDELOADED_AND_SYSTEM_EXCLUDE_OEM
ScopeType.SIDELOADED_AND_OEM -> NativeScopeType.SIDELOADED_AND_OEM
ScopeType.SIDELOADED_AND_SYSTEM_AND_OEM -> NativeScopeType.SIDELOADED_AND_SYSTEM_AND_OEM
ScopeType.ALL -> NativeScopeType.ALL
}

internal fun ReasonMode.toNative(): NativeReasonMode =
when (this) {
ReasonMode.ALL -> NativeReasonMode.ALL
ReasonMode.HIGHEST_CONFIDENCE -> NativeReasonMode.HIGHEST_CONFIDENCE
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ internal fun processMalwareData(
return nativeList.mapNotNull { nativeInfo ->
try {
val pInfo = nativeInfo.packageInfo
val reason = nativeInfo.reason
val reasons = nativeInfo.reasons
val permissions = nativeInfo.permissions

val packageName = pInfo.packageName
Expand All @@ -36,7 +36,7 @@ internal fun processMalwareData(

SuspiciousAppInfo(
packageInfo = commonPackageInfo,
reason = reason,
reasons = reasons,
permissions = permissions ?: emptySet()
)
} catch (e: Exception) {
Expand Down
7 changes: 5 additions & 2 deletions library/src/commonMain/kotlin/model/config/android_config.kt
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,14 @@ package com.freeraspkmp.model.config
* @param packageName The expected package name of the app.
* @param certificateHashes A list of expected certificate hashes in base64.
* @param supportedAlternativeStores A list of package names for supported alternative stores.
* @param malwareConfig Configuration for malware detection.
* @param malwareConfig Deprecated. Use [suspiciousAppDetectionConfig] instead.
* @param suspiciousAppDetectionConfig Configuration for malware detection.
*/
data class AndroidConfig(
val packageName: String,
val certificateHashes: List<String>,
val supportedAlternativeStores: List<String> = emptyList(),
val malwareConfig: MalwareConfig? = null
@Deprecated("Use SuspiciousAppDetectionConfig instead")
val malwareConfig: MalwareConfig? = null,
val suspiciousAppDetectionConfig: SuspiciousAppDetectionConfig? = null
)
5 changes: 5 additions & 0 deletions library/src/commonMain/kotlin/model/config/malware_config.kt
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,14 @@ package com.freeraspkmp.model.config
* @param suspiciousPermissions A list of suspicious permission groups.
* @param whitelistedInstallationSources A list of whitelisted installation sources.
*/
@Deprecated("Use SuspiciousAppDetectionConfig instead")
data class MalwareConfig (
@Deprecated("Use SuspiciousAppDetectionConfig instead")
val blacklistedPackageNames: List<String> = emptyList(),
@Deprecated("Use SuspiciousAppDetectionConfig instead")
val blacklistedHashes: List<String> = emptyList(),
@Deprecated("Use SuspiciousAppDetectionConfig instead")
val suspiciousPermissions: List<List<String>> = emptyList(),
@Deprecated("Use SuspiciousAppDetectionConfig instead")
val whitelistedInstallationSources: List<String> = emptyList()
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package com.freeraspkmp.model.config

/**
* Scope of installed apps included in the malware scan.
*/
enum class ScopeType {
SIDELOADED_ONLY,
SIDELOADED_AND_SYSTEM_EXCLUDE_OEM,
SIDELOADED_AND_OEM,
SIDELOADED_AND_SYSTEM_AND_OEM,
ALL
}

/**
* Controls how detection reasons are reported on a suspicious app.
*/
enum class ReasonMode { ALL, HIGHEST_CONFIDENCE }

/**
* Defines which installed apps should be scanned for malware.
*
* @param scanScope The set of apps to include in the scan.
* @param trustedInstallSources Installation sources whose apps should be excluded from the scan.
*/
data class MalwareScanScope(
val scanScope: ScopeType,
val trustedInstallSources: List<String>? = null
)

/**
* Configuration for malware detection. Replaces [MalwareConfig].
*
* @param packageNames Package names of known malicious apps.
* @param hashes Certificate hashes of known malicious apps.
* @param requestedPermissions Groups of permissions an app must request to be flagged as suspicious.
* @param grantedPermissions Groups of permissions an app must be granted to be flagged as suspicious.
* @param malwareScanScope Defines which apps are scanned.
* @param reasonMode Controls how detection reasons are reported.
*/
data class SuspiciousAppDetectionConfig(
val packageNames: List<String>? = null,
val hashes: List<String>? = null,
val requestedPermissions: List<List<String>>? = null,
val grantedPermissions: List<List<String>>? = null,
val malwareScanScope: MalwareScanScope? = null,
val reasonMode: ReasonMode? = null
)
6 changes: 3 additions & 3 deletions library/src/commonMain/kotlin/model/suspicious_app_info.kt
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,12 @@ package com.freeraspkmp.model
* Contains information about a suspicious app.
*
* @param packageInfo Information about the suspicious package.
* @param reason The reason why the app is considered suspicious.
* @param permissions A set of suspicious permissions held by the app. Populated when reason is `suspiciousPermission`.
* @param reasons The reasons why the app is considered suspicious.
* @param permissions A set of suspicious permissions held by the app. Populated when reasons contain `suspiciousPermission`.
*/
data class SuspiciousAppInfo(
val packageInfo: PackageInfo,
val reason: String,
val reasons: Set<String>,
val permissions: Set<String> = emptySet()
)

Expand Down