Skip to content
Merged
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
37 changes: 37 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,43 @@ 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.4.0] - 2026-02-10
- Android SDK version: 18.0.2
- iOS SDK version: 6.13.0

### Flutter

#### Added
- Added `onAutomation` callback to `ThreatCallback` for handling `Threat.automation` threat

### Android

#### Added

- Added support for `KernelSU` to the existing root detection capabilities
- Added support for `HMA` to the existing root detection capabilities
- Added new malware detection capabilities
- Added `onAutomationDetected()` callback to `ThreatDetected` interface
- We are introducing a new capability, detecting whether the device is being automated using tools like Appium
- Added value restrictions to `externalId`
- Method `storeExternalId()` now returns `ExternalIdResult`, which indicates `Success` or `Error` when `externalId` violates restrictions

#### Fixed

- Fixed exception handling for the KeyStore `getEntry` operation
- Fixed issue in `ScreenProtector` concerning the `onScreenRecordingDetected` invocations
- Merged internal shared libraries into a single one, reducing the final APK size
- Fixed bug related to key storing in keystore type detection (hw-backed keystore check)
- Fixed manifest queries merge

#### Changed

- Removed unused library `tmlib`
- Refactoring of signature verification code
- Updated compile and target API to 36
- Improved root detection capabilities
- Detection of wireless ADB added to ADB detections

## [7.3.0] - 2025-10-20
- Android SDK version: 17.0.0
- iOS SDK version: 6.13.0
Expand Down
4 changes: 2 additions & 2 deletions 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 = '17.0.0'
ext.talsec_version = '18.0.2'
repositories {
google()
mavenCentral()
Expand Down Expand Up @@ -32,7 +32,7 @@ android {
namespace("com.aheaditec.freerasp")
}

compileSdk 35
compileSdk 36

compileOptions {
sourceCompatibility JavaVersion.VERSION_17
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import android.os.Build
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleEventObserver
import androidx.lifecycle.LifecycleOwner
import com.aheaditec.freerasp.handlers.ExecutionStateStreamHandler
import com.aheaditec.freerasp.handlers.MethodCallHandler
import com.aheaditec.freerasp.handlers.StreamHandler
import com.aheaditec.freerasp.handlers.TalsecThreatHandler
Expand All @@ -17,6 +18,7 @@ import io.flutter.embedding.engine.plugins.lifecycle.FlutterLifecycleAdapter
/** FreeraspPlugin */
class FreeraspPlugin : FlutterPlugin, ActivityAware, LifecycleEventObserver {
private var streamHandler: StreamHandler = StreamHandler()
private var executionStateStreamHandler: ExecutionStateStreamHandler = ExecutionStateStreamHandler()
private var methodCallHandler: MethodCallHandler = MethodCallHandler()
private var screenProtector: ScreenProtector? =
if (Build.VERSION.SDK_INT >= 34) ScreenProtector else null
Expand All @@ -30,11 +32,13 @@ class FreeraspPlugin : FlutterPlugin, ActivityAware, LifecycleEventObserver {
context = flutterPluginBinding.applicationContext
methodCallHandler.createMethodChannel(messenger, flutterPluginBinding.applicationContext)
streamHandler.createEventChannel(messenger)
executionStateStreamHandler.createEventChannel(messenger)
}

override fun onDetachedFromEngine(binding: FlutterPlugin.FlutterPluginBinding) {
methodCallHandler.destroyMethodChannel()
streamHandler.destroyEventChannel()
executionStateStreamHandler.destroyEventChannel()
TalsecThreatHandler.detachListener(binding.applicationContext)
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package com.aheaditec.freerasp

internal sealed class RaspExecutionStateEvent(val value: Int) {
object AllChecksFinished : RaspExecutionStateEvent(187429)
}
2 changes: 2 additions & 0 deletions android/src/main/kotlin/com/aheaditec/freerasp/Threat.kt
Original file line number Diff line number Diff line change
Expand Up @@ -45,4 +45,6 @@ internal sealed class Threat(val value: Int) {
object TimeSpoofing : Threat(189105221)

object LocationSpoofing : Threat(653273273)

object Automation : Threat(298453120)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package com.aheaditec.freerasp.dispatchers

import com.aheaditec.freerasp.RaspExecutionStateEvent
import io.flutter.plugin.common.EventChannel.EventSink

internal class ExecutionStateDispatcher {
private val eventCache = mutableSetOf<RaspExecutionStateEvent>()

var eventSink: EventSink? = null
set(value) {
field = value
if (value != null) {
flushCache(value)
}
}

fun dispatch(event: RaspExecutionStateEvent) {
val sink = synchronized(eventCache) {
val currentSink = eventSink
if (currentSink != null) {
currentSink
} else {
eventCache.add(event)
null
}
}
sink?.success(event.value)
}

private fun flushCache(sink: EventSink) {
val events = synchronized(eventCache) {
val snapshot = eventCache.toSet()
eventCache.clear()
snapshot
}
events.forEach { sink.success(it.value) }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
package com.aheaditec.freerasp.dispatchers

import com.aheaditec.freerasp.Threat
import com.aheaditec.freerasp.handlers.MethodCallHandler
import com.aheaditec.talsec_security.security.api.SuspiciousAppInfo
import io.flutter.plugin.common.EventChannel.EventSink

internal class ThreatDispatcher {
private val threatCache = mutableSetOf<Threat>()
private val malwareCache = mutableListOf<SuspiciousAppInfo>()

var eventSink: EventSink? = null
set(value) {
field = value
if (value != null) {
flushThreatCache(value)
}
}

var methodSink: MethodCallHandler.MethodSink? = null
set(value) {
field = value
if (value != null) {
flushMalwareCache(value)
}
}

fun dispatchThreat(threat: Threat) {
val sink = synchronized(threatCache) {
val currentSink = eventSink
if (currentSink != null) {
currentSink
} else {
threatCache.add(threat)
null
}
}
sink?.success(threat.value)
}

fun dispatchMalware(apps: List<SuspiciousAppInfo>) {
val sink = synchronized(malwareCache) {
val currentSink = methodSink
if (currentSink != null) {
currentSink
} else {
malwareCache.addAll(apps)
null
}
}
sink?.onMalwareDetected(apps)
}

private fun flushThreatCache(sink: EventSink) {
val threats = synchronized(threatCache) {
val snapshot = threatCache.toSet()
threatCache.clear()
snapshot
}
threats.forEach { sink.success(it.value) }
}

private fun flushMalwareCache(sink: MethodCallHandler.MethodSink) {
val malware = synchronized(malwareCache) {
val snapshot = malwareCache.toMutableList()
malwareCache.clear()
snapshot
}
if (malware.isNotEmpty()) {
sink.onMalwareDetected(malware)
}
}
}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package com.aheaditec.freerasp.handlers

import io.flutter.Log
import io.flutter.plugin.common.BinaryMessenger
import io.flutter.plugin.common.EventChannel

/**
* A stream handler that creates and manages an [EventChannel] for freeRASP execution state events.
*/
internal class ExecutionStateStreamHandler : EventChannel.StreamHandler {

private var eventChannel: EventChannel? = null

companion object {
private const val CHANNEL_NAME: String = "talsec.app/freerasp/execution_state"
}

internal fun createEventChannel(messenger: BinaryMessenger) {
if (eventChannel != null) {
Log.i("ExecStateStreamHandler", "Tried to create channel without disposing old one.")
destroyEventChannel()
}
eventChannel = EventChannel(messenger, CHANNEL_NAME).also {
it.setStreamHandler(this)
}
}

internal fun destroyEventChannel() {
eventChannel?.setStreamHandler(null)
eventChannel = null
TalsecThreatHandler.detachExecutionStateSink()
}

override fun onListen(arguments: Any?, events: EventChannel.EventSink?) {
events?.let {
TalsecThreatHandler.attachExecutionStateSink(it)
}
}

override fun onCancel(arguments: Any?) {
TalsecThreatHandler.detachExecutionStateSink()
}
}
Loading