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
45 changes: 45 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,51 @@ 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).

## [2.1.0] - 2026-08-07

- Android SDK version: 19.2.1
- iOS SDK version: 7.1.2

### Kotlin Multiplatform

#### Added

- Added `FreeRaspEvent.Bootloader`, reporting an unlocked or compromised bootloader — Android only

### Android

#### Added

- Added bootloader detection (unlocked/compromised) with `onBootloader()` callback
- Added option to fetch JitPack dependencies from our own Talsec repository (`https://europe-west3-maven.pkg.dev/talsec-artifact-repository/common`)

#### Fixed

- Fixed native crash caused by std::terminate() race condition
- Fixed periodic hook and root check overwriting
- Fixed root detection crash in obfuscated release builds
- Fixed hardware-backed keystore detection failing with `NoSuchMethodError` on some Android 12+ devices

#### Changed

- Improved KernelSU detection
- Improved hook detection
- Improved Frida detection
- Improved root detection capabilities

### iOS

#### Added

- Improved jailbreak detection
- Added support for postponed checks, therefore, due to slower execution, some subchecks are run after initial startup checks.
- Improved hook detection.

#### Fixed

- Fixed issue with app's color scheme initialization.
- Fixed bad memory access in jaibreak check.

## [2.0.0] - 2026-05-15

- Android SDK version: 18.3.0
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ enum class CheckId {
HOOKS, DEVICE_BINDING, OBFUSCATION_ISSUES, SCREENSHOT, SCREEN_RECORDING,
PASSCODE, SECURE_HARDWARE, SYSTEM_VPN, DEV_MODE, ADB_ENABLED,
MULTI_INSTANCE, DEVICE_ID, TIME_SPOOFING, UNSECURE_WIFI,
LOCATION_SPOOFING, AUTOMATION, MALWARE
LOCATION_SPOOFING, AUTOMATION, BOOTLOADER, MALWARE
}

data class SecurityCheck(
Expand Down Expand Up @@ -39,6 +39,7 @@ fun FreeRaspEvent.toCheckId(): CheckId = when (this) {
is FreeRaspEvent.UnsecureWifi -> CheckId.UNSECURE_WIFI
is FreeRaspEvent.LocationSpoofing -> CheckId.LOCATION_SPOOFING
is FreeRaspEvent.Automation -> CheckId.AUTOMATION
is FreeRaspEvent.Bootloader -> CheckId.BOOTLOADER
is FreeRaspEvent.Malware -> CheckId.MALWARE
}

Expand All @@ -64,5 +65,6 @@ val initialChecks: List<SecurityCheck> = listOf(
SecurityCheck(CheckId.UNSECURE_WIFI, "Unsecure WiFi", "Detects insecure network connection"),
SecurityCheck(CheckId.LOCATION_SPOOFING, "Location Spoofing", "Detects GPS spoofing"),
SecurityCheck(CheckId.AUTOMATION, "Automation", "Detects automation tools"),
SecurityCheck(CheckId.BOOTLOADER, "Bootloader", "Detects unlocked or compromised bootloader"),
SecurityCheck(CheckId.MALWARE, "Malware", "Detects malicious applications"),
)
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 = "2.0.0"
version = "2.1.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.3.0")
implementation("com.aheaditec.talsec.security:TalsecSecurity-Community-KMP:19.2.1")
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
8 changes: 4 additions & 4 deletions library/src/androidMain/kotlin/api/freeraspKMP.android.kt
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package com.freeraspkmp.api

import com.aheaditec.talsec_security.security.api.Talsec
import com.aheaditec.talsec_security.security.api.ThreatListener
import app.talsec.rasp.security.api.Talsec
import app.talsec.rasp.security.api.ThreatListener
import android.util.Log
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
Expand All @@ -17,8 +17,8 @@ import com.freeraspkmp.model.config.freeraspConfig
import com.freeraspkmp.android.utils.AppIconUtil
import com.freeraspkmp.android.utils.toNativeConfig
import com.freeraspkmp.android.providers.ContextProvider
import com.aheaditec.talsec_security.security.api.ExternalIdResult
import com.aheaditec.talsec_security.security.api.TalsecMode
import app.talsec.rasp.security.api.ExternalIdResult
import app.talsec.rasp.security.api.TalsecMode
import com.freeraspkmp.android.handlers.ThreatDetectedHandler
import com.freeraspkmp.android.handlers.DeviceStateHandler
import com.freeraspkmp.android.handlers.RaspExecutionStateHandler
Expand Down
48 changes: 25 additions & 23 deletions library/src/androidMain/kotlin/handlers/threat_handler.kt
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package com.freeraspkmp.android.handlers

import com.aheaditec.talsec_security.security.api.SuspiciousAppInfo
import com.aheaditec.talsec_security.security.api.ThreatListener
import app.talsec.rasp.security.api.SuspiciousAppInfo
import app.talsec.rasp.security.api.ThreatListener
import com.freeraspkmp.model.FreeRaspEvent
import com.freeraspkmp.android.providers.ContextProvider
import com.freeraspkmp.android.utils.processMalwareData
Expand All @@ -10,23 +10,23 @@ internal class ThreatDetectedHandler(
private val onEvent: (FreeRaspEvent) -> Unit
) : ThreatListener.ThreatDetected() {

override fun onRootDetected() = onEvent(FreeRaspEvent.PrivilegedAccess)
override fun onPrivilegedAccess() = onEvent(FreeRaspEvent.PrivilegedAccess)

override fun onDebuggerDetected() = onEvent(FreeRaspEvent.Debug)
override fun onDebug() = onEvent(FreeRaspEvent.Debug)

override fun onEmulatorDetected() = onEvent(FreeRaspEvent.Simulator)
override fun onSimulator() = onEvent(FreeRaspEvent.Simulator)

override fun onTamperDetected() = onEvent(FreeRaspEvent.AppIntegrity)
override fun onAppIntegrity() = onEvent(FreeRaspEvent.AppIntegrity)

override fun onUntrustedInstallationSourceDetected() = onEvent(FreeRaspEvent.UnofficialStore)
override fun onUnofficialStore() = onEvent(FreeRaspEvent.UnofficialStore)

override fun onHookDetected() = onEvent(FreeRaspEvent.Hooks)
override fun onHooks() = onEvent(FreeRaspEvent.Hooks)

override fun onDeviceBindingDetected() = onEvent(FreeRaspEvent.DeviceBinding)
override fun onDeviceBinding() = onEvent(FreeRaspEvent.DeviceBinding)

override fun onObfuscationIssuesDetected() = onEvent(FreeRaspEvent.ObfuscationIssues)
override fun onObfuscationIssues() = onEvent(FreeRaspEvent.ObfuscationIssues)

override fun onMalwareDetected(suspiciousApps: List<SuspiciousAppInfo>) {
override fun onMalware(suspiciousApps: List<SuspiciousAppInfo>) {
if (suspiciousApps.isEmpty()) return

val context = ContextProvider.getApplicationContext()
Expand All @@ -37,34 +37,36 @@ internal class ThreatDetectedHandler(
}
}

override fun onScreenshotDetected() = onEvent(FreeRaspEvent.Screenshot)
override fun onScreenshot() = onEvent(FreeRaspEvent.Screenshot)

override fun onScreenRecordingDetected() = onEvent(FreeRaspEvent.ScreenRecording)
override fun onScreenRecording() = onEvent(FreeRaspEvent.ScreenRecording)

override fun onMultiInstanceDetected() = onEvent(FreeRaspEvent.MultiInstance)
override fun onMultiInstance() = onEvent(FreeRaspEvent.MultiInstance)

override fun onUnsecureWifiDetected() = onEvent(FreeRaspEvent.UnsecureWifi)
override fun onUnsecureWifi() = onEvent(FreeRaspEvent.UnsecureWifi)

override fun onTimeSpoofingDetected() = onEvent(FreeRaspEvent.TimeSpoofing)
override fun onTimeSpoofing() = onEvent(FreeRaspEvent.TimeSpoofing)

override fun onLocationSpoofingDetected() = onEvent(FreeRaspEvent.LocationSpoofing)
override fun onLocationSpoofing() = onEvent(FreeRaspEvent.LocationSpoofing)

override fun onAutomationDetected() = onEvent(FreeRaspEvent.Automation)
override fun onAutomation() = onEvent(FreeRaspEvent.Automation)

override fun onBootloader() = onEvent(FreeRaspEvent.Bootloader)
}

internal class DeviceStateHandler(
private val onEvent: (FreeRaspEvent) -> Unit
) : ThreatListener.DeviceState() {

override fun onUnlockedDeviceDetected() = onEvent(FreeRaspEvent.Passcode)
override fun onPasscode() = onEvent(FreeRaspEvent.Passcode)

override fun onHardwareBackedKeystoreNotAvailableDetected() = onEvent(FreeRaspEvent.SecureHardwareNotAvailable)
override fun onSecureHardwareNotAvailable() = onEvent(FreeRaspEvent.SecureHardwareNotAvailable)

override fun onDeveloperModeDetected() = onEvent(FreeRaspEvent.DevMode)
override fun onDevMode() = onEvent(FreeRaspEvent.DevMode)

override fun onADBEnabledDetected() = onEvent(FreeRaspEvent.AdbEnabled)
override fun onAdbEnabled() = onEvent(FreeRaspEvent.AdbEnabled)

override fun onSystemVPNDetected() = onEvent(FreeRaspEvent.SystemVPN)
override fun onSystemVpn() = onEvent(FreeRaspEvent.SystemVPN)
}

internal class RaspExecutionStateHandler(
Expand Down
10 changes: 5 additions & 5 deletions library/src/androidMain/kotlin/utils/config_mapper.android.kt
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
package com.freeraspkmp.android.utils

import com.aheaditec.talsec_security.security.api.TalsecConfig
import app.talsec.rasp.security.api.TalsecConfig
import com.freeraspkmp.model.config.ScanScope
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
import app.talsec.rasp.security.api.SuspiciousAppDetectionConfig as NativeSuspiciousAppDetectionConfig
import app.talsec.rasp.security.api.MalwareScanScope as NativeMalwareScanScope
import app.talsec.rasp.security.api.ScopeType as NativeScopeType
import app.talsec.rasp.security.api.ReasonMode as NativeReasonMode

fun freeraspConfig.toNativeConfig(): TalsecConfig {
val androidConfig = this.androidConfig ?: throw FreeraspKMPException("AndroidConfig is required on the Android platform but was null.")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import android.content.pm.PackageManager
import android.util.Log
import com.freeraspkmp.model.PackageInfo
import com.freeraspkmp.model.SuspiciousAppInfo
import com.aheaditec.talsec_security.security.api.SuspiciousAppInfo as NativeSuspiciousAppInfo
import app.talsec.rasp.security.api.SuspiciousAppInfo as NativeSuspiciousAppInfo
import android.os.Build

internal fun processMalwareData(
Expand Down
5 changes: 5 additions & 0 deletions library/src/commonMain/kotlin/model/freerasp_event.kt
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,11 @@ sealed class FreeRaspEvent {
*/
data object Automation : FreeRaspEvent()

/**
* Indicates that the device bootloader is unlocked or otherwise compromised. Android only.
*/
data object Bootloader : FreeRaspEvent()

/**
* Indicates that malware was detected on the device.
*
Expand Down
Empty file.
Binary file not shown.
Binary file not shown.
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
</data>
<key>Info.plist</key>
<data>
4pI5j8NGLDqEd/OZJnyc+ocjgxM=
yqe7Yu658nnrvaC3u0vV+gffjZ8=
</data>
<key>PrivacyInfo.xcprivacy</key>
<data>
Expand Down
Binary file not shown.
Original file line number Diff line number Diff line change
Expand Up @@ -10,19 +10,19 @@
</data>
<key>Frameworks/TalsecRuntime.framework/Info.plist</key>
<data>
4pI5j8NGLDqEd/OZJnyc+ocjgxM=
yqe7Yu658nnrvaC3u0vV+gffjZ8=
</data>
<key>Frameworks/TalsecRuntime.framework/PrivacyInfo.xcprivacy</key>
<data>
EoDY5rl3F+/NSgSM7d7epUYDscA=
</data>
<key>Frameworks/TalsecRuntime.framework/TalsecRuntime</key>
<data>
Y213lKskCJqPv32DGE9vCE0Hz9s=
M3TnkWKQXnST5iVIoW/nkWEyMVc=
</data>
<key>Frameworks/TalsecRuntime.framework/_CodeSignature/CodeResources</key>
<data>
2SjUFO1Z8sHwuPhm8hjVl0KJDAs=
7R2qp2ofudE5edHsRzCx7NduT1o=
</data>
<key>Frameworks/TalsecRuntime.framework/cacert.pem</key>
<data>
Expand Down Expand Up @@ -56,10 +56,6 @@
<data>
md++BGDsQJ81SM8LwiM8Bb6iV1k=
</data>
<key>Modules/TalsecBridge.swiftmodule/arm64-apple-ios.swiftmodule</key>
<data>
pHF/nPXIx4k6Wv+E+ING2kfSB1E=
</data>
<key>Modules/module.modulemap</key>
<data>
3g/15mHdzVcR2cnGC0101BR6/TI=
Expand All @@ -78,7 +74,7 @@
<dict>
<key>hash2</key>
<data>
TCi1uJQPaYbeJInZZipnibqIrlDrm40pP/Uu1cxF1+8=
85yldaf+2IQRaUvolNvjHU4jOg+3vJX3oCChs1sdYRc=
</data>
</dict>
<key>Frameworks/TalsecRuntime.framework/PrivacyInfo.xcprivacy</key>
Expand All @@ -92,14 +88,14 @@
<dict>
<key>hash2</key>
<data>
EMMeVcnA/Aldsp7GNnWGu5ZwKeG2L1i1S5bYVz8N+R8=
IPfp8XVM9T4enKh4W2Oshjb9q7UxhTIEtElz79+pkrs=
</data>
</dict>
<key>Frameworks/TalsecRuntime.framework/_CodeSignature/CodeResources</key>
<dict>
<key>hash2</key>
<data>
99JpehTLlntlHQEgkxb+nhmMOQfhcENGX7Kt+e6OynM=
MoCXnjEkwQDK9pcHtnR+M1xhgyHr/9WYMyGTffAw2bo=
</data>
</dict>
<key>Frameworks/TalsecRuntime.framework/cacert.pem</key>
Expand Down Expand Up @@ -151,13 +147,6 @@
Vx7FpUpbiMJ62u9a1sSV7EYLbCGCEvQfITEq4AX94As=
</data>
</dict>
<key>Modules/TalsecBridge.swiftmodule/arm64-apple-ios.swiftmodule</key>
<dict>
<key>hash2</key>
<data>
94DimUDw0MH1mDSjKY24csyAVHrngOnbYoAlf3w6n3E=
</data>
</dict>
<key>Modules/module.modulemap</key>
<dict>
<key>hash2</key>
Expand Down
Empty file.
Binary file not shown.
Binary file not shown.
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
</data>
<key>Info.plist</key>
<data>
eL5oFlB6wq1n2HB8CtBm1T2ZgRw=
IvCrrT1H+e5K5B//aMp429REroU=
</data>
<key>PrivacyInfo.xcprivacy</key>
<data>
Expand Down
Binary file not shown.
Loading