Skip to content

Commit 4cb03c9

Browse files
authored
freeRASP 6.12.0 (#159)
1 parent 9751cfd commit 4cb03c9

File tree

60 files changed

+1624
-760
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

60 files changed

+1624
-760
lines changed

CHANGELOG.md

+144-44
Large diffs are not rendered by default.

android/build.gradle

+8-8
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,15 @@ group 'com.aheaditec.freerasp'
22
version '1.0-SNAPSHOT'
33

44
buildscript {
5-
ext.kotlin_version = '1.7.20'
6-
ext.talsec_version = '13.2.0'
5+
ext.kotlin_version = '1.7.22'
6+
ext.talsec_version = '14.0.1'
77
repositories {
88
google()
99
mavenCentral()
1010
}
1111

1212
dependencies {
13-
classpath 'com.android.tools.build:gradle:7.4.2'
13+
classpath 'com.android.tools.build:gradle:8.3.1'
1414
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
1515
}
1616
}
@@ -32,15 +32,15 @@ android {
3232
namespace("com.aheaditec.freerasp")
3333
}
3434

35-
compileSdkVersion 33
35+
compileSdk 35
3636

3737
compileOptions {
38-
sourceCompatibility JavaVersion.VERSION_1_8
39-
targetCompatibility JavaVersion.VERSION_1_8
38+
sourceCompatibility JavaVersion.VERSION_17
39+
targetCompatibility JavaVersion.VERSION_17
4040
}
4141

4242
kotlinOptions {
43-
jvmTarget = '1.8'
43+
jvmTarget = 17
4444
}
4545

4646
sourceSets {
@@ -58,7 +58,7 @@ android {
5858
}
5959

6060
dependencies {
61-
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
61+
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
6262

6363
// Talsec SDK
6464
implementation "com.aheaditec.talsec.security:TalsecSecurity-Community-Flutter:$talsec_version"
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
distributionBase=GRADLE_USER_HOME
2+
distributionUrl=https\://services.gradle.org/distributions/gradle-8.4-all.zip
23
distributionPath=wrapper/dists
34
zipStoreBase=GRADLE_USER_HOME
45
zipStorePath=wrapper/dists
5-
distributionUrl=https\://services.gradle.org/distributions/gradle-7.6.1-all.zip

android/src/main/kotlin/com/aheaditec/freerasp/FreeraspPlugin.kt

+17-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package com.aheaditec.freerasp
22

33
import android.content.Context
4+
import android.os.Build
45
import androidx.lifecycle.Lifecycle
56
import androidx.lifecycle.LifecycleEventObserver
67
import androidx.lifecycle.LifecycleOwner
@@ -12,19 +13,20 @@ import io.flutter.embedding.engine.plugins.activity.ActivityAware
1213
import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding
1314
import io.flutter.embedding.engine.plugins.lifecycle.FlutterLifecycleAdapter
1415

15-
1616
/** FreeraspPlugin */
1717
class FreeraspPlugin : FlutterPlugin, ActivityAware, LifecycleEventObserver {
1818
private var streamHandler: StreamHandler = StreamHandler()
1919
private var methodCallHandler: MethodCallHandler = MethodCallHandler()
20+
private var screenProtector: ScreenProtector? =
21+
if (Build.VERSION.SDK_INT >= 34) ScreenProtector() else null
2022

2123
private var context: Context? = null
2224
private var lifecycle: Lifecycle? = null
2325

2426
override fun onAttachedToEngine(flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) {
2527
val messenger = flutterPluginBinding.binaryMessenger
2628
context = flutterPluginBinding.applicationContext
27-
29+
screenProtector?.enable()
2830
methodCallHandler.createMethodChannel(messenger, flutterPluginBinding.applicationContext)
2931
streamHandler.createEventChannel(messenger)
3032
}
@@ -39,19 +41,32 @@ class FreeraspPlugin : FlutterPlugin, ActivityAware, LifecycleEventObserver {
3941
lifecycle = FlutterLifecycleAdapter.getActivityLifecycle(binding).also {
4042
it.addObserver(this)
4143
}
44+
methodCallHandler.activity = binding.activity
45+
screenProtector?.activity = binding.activity
46+
screenProtector?.let { lifecycle?.addObserver(it) }
4247
}
4348

4449
override fun onDetachedFromActivity() {
4550
lifecycle?.removeObserver(this)
51+
methodCallHandler.activity = null
52+
screenProtector?.let { lifecycle?.removeObserver(it) }
53+
screenProtector?.activity = null
4654
}
4755

4856
override fun onDetachedFromActivityForConfigChanges() {
4957
lifecycle?.removeObserver(this)
58+
methodCallHandler.activity = null
59+
screenProtector?.let { lifecycle?.removeObserver(it) }
60+
screenProtector?.activity = null
5061
}
5162

5263
override fun onReattachedToActivityForConfigChanges(binding: ActivityPluginBinding) {
5364
lifecycle = FlutterLifecycleAdapter.getActivityLifecycle(binding)
5465
lifecycle?.addObserver(this)
66+
methodCallHandler.activity = binding.activity
67+
screenProtector?.activity = binding.activity
68+
screenProtector?.let { lifecycle?.addObserver(it) }
69+
5570
}
5671

5772
override fun onStateChanged(source: LifecycleOwner, event: Lifecycle.Event) {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
package com.aheaditec.freerasp
2+
3+
import android.annotation.SuppressLint
4+
import android.app.Activity
5+
import android.app.Activity.ScreenCaptureCallback
6+
import android.content.Context
7+
import android.content.pm.PackageManager
8+
import android.os.Build
9+
import android.view.WindowManager.SCREEN_RECORDING_STATE_VISIBLE
10+
import androidx.annotation.RequiresApi
11+
import androidx.core.content.ContextCompat
12+
import androidx.lifecycle.DefaultLifecycleObserver
13+
import androidx.lifecycle.LifecycleOwner
14+
import com.aheaditec.talsec_security.security.api.Talsec
15+
import io.flutter.Log
16+
import java.util.function.Consumer
17+
18+
internal class ScreenProtector : DefaultLifecycleObserver {
19+
companion object {
20+
private const val TAG = "ScreenProtector"
21+
private const val SCREEN_CAPTURE_PERMISSION = "android.permission.DETECT_SCREEN_CAPTURE"
22+
private const val SCREEN_RECORDING_PERMISSION = "android.permission.DETECT_SCREEN_RECORDING"
23+
}
24+
25+
internal var activity: Activity? = null
26+
private var isEnabled = false
27+
28+
private val screenCaptureCallback = ScreenCaptureCallback { Talsec.onScreenshotDetected() }
29+
private val screenRecordCallback: Consumer<Int> = Consumer<Int> { state ->
30+
if (state == SCREEN_RECORDING_STATE_VISIBLE) {
31+
Talsec.onScreenRecordingDetected()
32+
Log.e("ScreenProtector", "Screen recording detected")
33+
}
34+
}
35+
36+
internal fun enable() {
37+
if (isEnabled) return
38+
isEnabled = true
39+
}
40+
41+
override fun onStart(owner: LifecycleOwner) {
42+
super.onStart(owner)
43+
44+
if (isEnabled) activity?.let { register(it) }
45+
}
46+
47+
override fun onStop(owner: LifecycleOwner) {
48+
super.onStop(owner)
49+
50+
if (isEnabled) activity?.let { unregister(it) }
51+
}
52+
53+
private fun register(activity: Activity) {
54+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
55+
registerScreenCapture(activity)
56+
}
57+
58+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.VANILLA_ICE_CREAM) {
59+
registerScreenRecording(activity)
60+
}
61+
}
62+
63+
// Missing permission is suppressed because the decision to use the screen capture API is made
64+
// by developer, and not enforced by the library.
65+
@SuppressLint("MissingPermission")
66+
private fun unregister(currentActivity: Activity) {
67+
val context = currentActivity.applicationContext
68+
69+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE && hasPermission(
70+
context, SCREEN_CAPTURE_PERMISSION
71+
)
72+
) {
73+
currentActivity.unregisterScreenCaptureCallback(screenCaptureCallback)
74+
}
75+
76+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.VANILLA_ICE_CREAM && hasPermission(
77+
context, SCREEN_RECORDING_PERMISSION
78+
)
79+
) {
80+
currentActivity.windowManager?.removeScreenRecordingCallback(screenRecordCallback)
81+
}
82+
}
83+
84+
// Missing permission is suppressed because the decision to use the screen capture API is made
85+
// by developer, and not enforced by the library.
86+
@SuppressLint("MissingPermission")
87+
@RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
88+
private fun registerScreenCapture(currentActivity: Activity) {
89+
val context = currentActivity.applicationContext
90+
91+
if (!hasPermission(context, SCREEN_CAPTURE_PERMISSION)) {
92+
reportMissingPermission("screenshot", SCREEN_CAPTURE_PERMISSION)
93+
return
94+
}
95+
96+
currentActivity.registerScreenCaptureCallback(context.mainExecutor, screenCaptureCallback)
97+
}
98+
99+
// Missing permission is suppressed because the decision to use the screen capture API is made
100+
// by developer, and not enforced by the library.
101+
@SuppressLint("MissingPermission")
102+
@RequiresApi(Build.VERSION_CODES.VANILLA_ICE_CREAM)
103+
private fun registerScreenRecording(currentActivity: Activity) {
104+
val context = currentActivity.applicationContext
105+
106+
if (!hasPermission(context, SCREEN_RECORDING_PERMISSION)) {
107+
reportMissingPermission("screen record", SCREEN_RECORDING_PERMISSION)
108+
return
109+
}
110+
111+
val initialState = currentActivity.windowManager.addScreenRecordingCallback(
112+
context.mainExecutor, screenRecordCallback
113+
)
114+
screenRecordCallback.accept(initialState)
115+
116+
}
117+
118+
private fun hasPermission(context: Context, permission: String): Boolean {
119+
return ContextCompat.checkSelfPermission(
120+
context, permission
121+
) == PackageManager.PERMISSION_GRANTED
122+
}
123+
124+
private fun reportMissingPermission(protectionType: String, permission: String) {
125+
Log.e(
126+
TAG,
127+
"Failed to register $protectionType callback. Check if $permission permission is granted in AndroidManifest.xml"
128+
)
129+
}
130+
}

android/src/main/kotlin/com/aheaditec/freerasp/Threat.kt

+4
Original file line numberDiff line numberDiff line change
@@ -33,4 +33,8 @@ internal sealed class Threat(val value: Int) {
3333
object DevMode : Threat(45291047)
3434

3535
object ADBEnabled : Threat(379769839)
36+
37+
object Screenshot : Threat(705651459)
38+
39+
object ScreenRecording : Threat(64690214)
3640
}

android/src/main/kotlin/com/aheaditec/freerasp/handlers/MethodCallHandler.kt

+30
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package com.aheaditec.freerasp.handlers
22

3+
import android.app.Activity
34
import android.content.Context
45
import android.os.Handler
56
import android.os.HandlerThread
@@ -30,6 +31,8 @@ internal class MethodCallHandler : MethodCallHandler, LifecycleEventObserver {
3031
private val backgroundHandler = Handler(backgroundHandlerThread.looper)
3132
private val mainHandler = Handler(Looper.getMainLooper())
3233

34+
internal var activity: Activity? = null
35+
3336
companion object {
3437
private const val CHANNEL_NAME: String = "talsec.app/freerasp/methods"
3538
}
@@ -116,6 +119,8 @@ internal class MethodCallHandler : MethodCallHandler, LifecycleEventObserver {
116119
"start" -> start(call, result)
117120
"addToWhitelist" -> addToWhitelist(call, result)
118121
"getAppIcon" -> getAppIcon(call, result)
122+
"blockScreenCapture" -> blockScreenCapture(call, result)
123+
"isScreenCaptureBlocked" -> isScreenCaptureBlocked(result)
119124
else -> result.notImplemented()
120125
}
121126
}
@@ -169,4 +174,29 @@ internal class MethodCallHandler : MethodCallHandler, LifecycleEventObserver {
169174

170175
}
171176
}
177+
178+
/**
179+
* Blocks or unblocks screen capture. Sets the window flag to secure the screen.
180+
*
181+
* @param call The method call containing the enable flag.
182+
* @param result The result handler of the method call.
183+
*/
184+
private fun blockScreenCapture(call: MethodCall, result: MethodChannel.Result) {
185+
runResultCatching(result) {
186+
val enable = call.argument<Boolean>("enable") ?: false
187+
Talsec.blockScreenCapture(activity, enable)
188+
result.success(null)
189+
}
190+
}
191+
192+
/**
193+
* Checks if screen capture is blocked.
194+
*
195+
* @param result The result handler of the method call.
196+
*/
197+
private fun isScreenCaptureBlocked(result: MethodChannel.Result) {
198+
runResultCatching(result) {
199+
result.success(Talsec.isScreenCaptureBlocked())
200+
}
201+
}
172202
}

android/src/main/kotlin/com/aheaditec/freerasp/handlers/PluginThreatHandler.kt

+8
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,14 @@ internal object PluginThreatHandler : ThreatDetected, DeviceState {
8080
notify(Threat.ADBEnabled)
8181
}
8282

83+
override fun onScreenshotDetected() {
84+
notify(Threat.Screenshot)
85+
}
86+
87+
override fun onScreenRecordingDetected() {
88+
notify(Threat.ScreenRecording)
89+
}
90+
8391
override fun onMalwareDetected(suspiciousApps: List<SuspiciousAppInfo>) {
8492
notify(suspiciousApps)
8593
}

android/src/main/kotlin/com/aheaditec/freerasp/handlers/TalsecThreatHandler.kt

+2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
package com.aheaditec.freerasp.handlers
22

33
import android.content.Context
4+
import android.os.Build
5+
import com.aheaditec.freerasp.ScreenProtector
46
import com.aheaditec.freerasp.Threat
57
import com.aheaditec.talsec_security.security.api.SuspiciousAppInfo
68
import com.aheaditec.talsec_security.security.api.Talsec

example/android/app/build.gradle

+9-3
Original file line numberDiff line numberDiff line change
@@ -23,15 +23,17 @@ if (flutterVersionName == null) {
2323
}
2424

2525
android {
26+
namespace 'com.aheaditec.freerasp_example'
27+
2628
compileSdkVersion flutter.compileSdkVersion
2729

2830
compileOptions {
29-
sourceCompatibility JavaVersion.VERSION_1_8
30-
targetCompatibility JavaVersion.VERSION_1_8
31+
sourceCompatibility JavaVersion.VERSION_17
32+
targetCompatibility JavaVersion.VERSION_17
3133
}
3234

3335
kotlinOptions {
34-
jvmTarget = '1.8'
36+
jvmTarget = 17
3537
}
3638

3739
sourceSets {
@@ -55,6 +57,10 @@ android {
5557
signingConfig signingConfigs.debug
5658
}
5759
}
60+
61+
lintOptions {
62+
disable 'InvalidPackage'
63+
}
5864
}
5965

6066
flutter {

example/android/app/src/main/AndroidManifest.xml

+5
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
22
package="com.aheaditec.freerasp_example">
3+
4+
<!-- These permissions are needed if want to detect screen capturing and recording. -->
5+
<uses-permission android:name="android.permission.DETECT_SCREEN_CAPTURE" />
6+
<uses-permission android:name="android.permission.DETECT_SCREEN_RECORDING" />
7+
38
<application
49
android:label="freerasp_example"
510
android:icon="@mipmap/ic_launcher">
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
#Fri Jun 23 08:50:38 CEST 2017
22
distributionBase=GRADLE_USER_HOME
3+
distributionUrl=https\://services.gradle.org/distributions/gradle-8.4-bin.zip
34
distributionPath=wrapper/dists
45
zipStoreBase=GRADLE_USER_HOME
5-
zipStorePath=wrapper/dists
6-
distributionUrl=https\://services.gradle.org/distributions/gradle-7.6.1-all.zip
6+
zipStorePath=wrapper/dists

0 commit comments

Comments
 (0)