Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

freeRASP 6.8.0 #137

Open
wants to merge 47 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
47 commits
Select commit Hold shift + click to select a range
df9e2a3
feat: add data classes for malware (Dart)
yardexx Sep 26, 2024
1ace1b8
feat: add data classes for malware (Kotlin)
yardexx Sep 26, 2024
8982a58
feat: update Android configuration
yardexx Sep 26, 2024
5400029
feat: update Talsec configuration
yardexx Sep 26, 2024
7c35e19
feat: add Flutter method invocation
yardexx Sep 26, 2024
d65da87
feat: raise lib version
yardexx Sep 26, 2024
8ef80c6
feat: update example app
yardexx Sep 26, 2024
25cfcc9
feat: update threat callback
yardexx Sep 26, 2024
af634b2
feat: misc
yardexx Sep 26, 2024
fdb5c65
feat: add data classes for malware (Dart)
yardexx Sep 26, 2024
5396bf1
feat: add data classes for malware (Kotlin)
yardexx Sep 26, 2024
fc03c02
feat: update Android configuration
yardexx Sep 26, 2024
fada932
feat: update Talsec configuration
yardexx Sep 26, 2024
3158e81
feat: add Flutter method invocation
yardexx Sep 26, 2024
44a66b4
feat: update example app
yardexx Sep 26, 2024
566599c
feat: update threat callback
yardexx Sep 26, 2024
7831d57
feat: misc
yardexx Sep 26, 2024
2ea6dda
Merge remote-tracking branch 'origin/release/6.8.0' into release/6.8.0
yardexx Sep 27, 2024
587dbb6
feat: so much code
yardexx Sep 30, 2024
d7d190c
feat: add whitelist addition
yardexx Oct 1, 2024
4e592f2
feat!: raise example sdk version
yardexx Oct 1, 2024
49b8262
docs: add documentation
yardexx Oct 1, 2024
b34c534
docs: adjust exports
yardexx Oct 1, 2024
48ffe9e
feat: update example
yardexx Oct 1, 2024
9fb2a5f
style: formatting
yardexx Oct 1, 2024
14f6550
chore: raise android package version
yardexx Oct 1, 2024
0eaaec0
feat: add data classes for malware (Dart)
yardexx Sep 26, 2024
78e4542
feat: add data classes for malware (Kotlin)
yardexx Sep 26, 2024
fade1fb
feat: update Android configuration
yardexx Sep 26, 2024
e4cbbf7
feat: update Talsec configuration
yardexx Sep 26, 2024
ae85a3e
feat: add Flutter method invocation
yardexx Sep 26, 2024
466462a
feat: update example app
yardexx Sep 26, 2024
477aa3b
feat: update threat callback
yardexx Sep 26, 2024
d9cbb3c
feat: misc
yardexx Sep 26, 2024
49cb0c4
feat: so much code
yardexx Sep 30, 2024
4f8f205
feat: add whitelist addition
yardexx Oct 1, 2024
6443d84
feat!: raise example sdk version
yardexx Oct 1, 2024
50afd82
docs: add documentation
yardexx Oct 1, 2024
9bfa6df
docs: adjust exports
yardexx Oct 1, 2024
b07331b
feat: update example
yardexx Oct 1, 2024
4137e8b
style: formatting
yardexx Oct 1, 2024
a4e96f6
Merge remote-tracking branch 'origin/release/6.8.0' into release/6.8.0
yardexx Oct 1, 2024
766d42a
chore: raise version + CHANGELOG
yardexx Oct 1, 2024
7e1d780
fix: typo
yardexx Oct 1, 2024
f44814d
fix: failing tests
yardexx Oct 2, 2024
98da53a
chore: update example
yardexx Oct 2, 2024
445ce0c
feat: add pigeon build script
yardexx Oct 2, 2024
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
14 changes: 14 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,20 @@ 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).

## [6.8.0] - 2024-10-01
- Android SDK version: 11.1.1
- iOS SDK version: 6.6.0

### Android

#### Added
- Implement empty callbacks for malware detection
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you announce it as a new feature?


### Flutter

#### Added
- Application detection and restriction based on configuration

## [6.7.1] - 2024-09-30
- Android SDK version: 11.1.1
- iOS SDK version: 6.6.0
Expand Down
4 changes: 4 additions & 0 deletions analysis_options.yaml
Original file line number Diff line number Diff line change
@@ -1 +1,5 @@
include: package:very_good_analysis/analysis_options.3.1.0.yaml

analyzer:
exclude:
- '**/*.g.dart'
70 changes: 70 additions & 0 deletions android/src/main/kotlin/com/aheaditec/freerasp/Extensions.kt
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
package com.aheaditec.freerasp

import android.content.Context
import android.content.pm.PackageInfo
import android.os.Build
import com.aheaditec.talsec_security.security.api.SuspiciousAppInfo
import io.flutter.plugin.common.MethodChannel
import com.aheaditec.freerasp.generated.PackageInfo as FlutterPackageInfo
import com.aheaditec.freerasp.generated.SuspiciousAppInfo as FlutterSuspiciousAppInfo

/**
* Executes the provided block of code and catches any exceptions thrown by it, returning the
Expand All @@ -17,3 +23,67 @@ internal inline fun runResultCatching(result: MethodChannel.Result, block: () ->
result.error(err::class.java.name, err.message, null)
}
}

/**
* Converts a [SuspiciousAppInfo] instance to a [com.aheaditec.freerasp.generated.SuspiciousAppInfo]
* instance used by Pigeon package for Flutter.
*
* @return A new [com.aheaditec.freerasp.generated.SuspiciousAppInfo] object with information from
* this [SuspiciousAppInfo].
*/
internal fun SuspiciousAppInfo.toPigeon(context: Context): FlutterSuspiciousAppInfo {
return FlutterSuspiciousAppInfo(this.packageInfo.toPigeon(context), this.reason)
}

/**
* Converts a [PackageInfo] instance to a [com.aheaditec.freerasp.generated.PackageInfo] instance
* used by Pigeon package for Flutter.
*
* @return A new [com.aheaditec.freerasp.generated.PackageInfo] object with information from
* this [PackageInfo].
*/
private fun PackageInfo.toPigeon(context: Context): FlutterPackageInfo {
return FlutterPackageInfo(
packageName = packageName,
appName = applicationInfo?.name,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

applicationInfo?.name returns just package name-style string, to get the app name, we have to use something like this: https://github.com/talsec/Free-RASP-ReactNative/blob/release-3.10.0/android/src/main/java/com/freeraspreactnative/utils/Utils.kt#L26

version = getVersionString(),
appIcon = Utils.parseIconBase64(context, packageName),
installationSource = Utils.getInstallerPackageName(context, packageName),
)
}

/**
* Retrieves the version string of the package.
*
* For devices running on Android P (API 28) and above, this method returns the `longVersionCode`.
* For older versions, it returns the `versionCode` (deprecated).
*
* @return A string representation of the version code.
*/
internal fun PackageInfo.getVersionString(): String {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
return longVersionCode.toString()
}
@Suppress("DEPRECATION")
return versionCode.toString()
}

/**
* Returns the encapsulated value if this instance represents success or throws the encapsulated exception
* if it is a failure, executing the given action before throwing.
*
* This function is similar to `Result.getOrThrow()`, but with the added functionality of performing
* an action before throwing the exception.
*
* @param action The action to be executed if the result is a failure. This action should not throw an exception.
* @return The encapsulated value if the result is a success.
* @throws Throwable The encapsulated exception if the result is a failure.
*
* @see Result.getOrThrow
*/
inline fun <T> Result<T>.getOrElseThenThrow(action: () -> Unit): T {
return getOrElse {
action()
throw it
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import io.flutter.embedding.engine.plugins.lifecycle.FlutterLifecycleAdapter
class FreeraspPlugin : FlutterPlugin, ActivityAware, LifecycleEventObserver {
private var streamHandler: StreamHandler = StreamHandler()
private var methodCallHandler: MethodCallHandler = MethodCallHandler()

private var context: Context? = null
private var lifecycle: Lifecycle? = null

Expand Down
158 changes: 158 additions & 0 deletions android/src/main/kotlin/com/aheaditec/freerasp/Utils.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
package com.aheaditec.freerasp

import android.content.Context
import android.graphics.Bitmap
import android.graphics.Canvas
import android.graphics.drawable.BitmapDrawable
import android.graphics.drawable.Drawable
import android.os.Build
import android.util.Base64
import com.aheaditec.talsec_security.security.api.TalsecConfig
import org.json.JSONException
import org.json.JSONObject
import java.io.ByteArrayOutputStream

internal object Utils {
fun toTalsecConfigThrowing(configJson: String?): TalsecConfig {
if (configJson == null) {
throw JSONException("Configuration is null")
}
val json = JSONObject(configJson)

val watcherMail = json.getString("watcherMail")
var isProd = true
if (json.has("isProd")) {
isProd = json.getBoolean("isProd")
}
val androidConfig = json.getJSONObject("androidConfig")

val packageName = androidConfig.getString("packageName")
val certificateHashes = androidConfig.extractArray<String>("signingCertHashes")
val alternativeStores = androidConfig.extractArray<String>("supportedStores")
val blocklistedPackageNames =
androidConfig.extractArray<String>("blocklistedPackageNames")
val blocklistedHashes = androidConfig.extractArray<String>("blocklistedHashes")
val whitelistedInstallationSources =
androidConfig.extractArray<String>("whitelistedInstallationSources")

val blocklistedPermissions = mutableListOf<Array<String>>()
if (androidConfig.has("blocklistedPermissions")) {
val permissions = androidConfig.getJSONArray("blocklistedPermissions")
for (i in 0 until permissions.length()) {
val permission = permissions.getJSONArray(i)
val permissionList = mutableListOf<String>()
for (j in 0 until permission.length()) {
permissionList.add(permission.getString(j))
}
Comment on lines +38 to +46
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you make it an extension method?

blocklistedPermissions.add(permissionList.toTypedArray())
}
}

return TalsecConfig.Builder(packageName, certificateHashes)
.watcherMail(watcherMail)
.supportedAlternativeStores(alternativeStores)
.prod(isProd)
.blocklistedPackageNames(blocklistedPackageNames)
.blocklistedHashes(blocklistedHashes)
.blocklistedPermissions(blocklistedPermissions.toTypedArray())
.whitelistedInstallationSources(whitelistedInstallationSources)
.build()
}

/**
* Retrieves the package name of the installer for a given app package.
*
* @param context The context of the application.
* @param packageName The package name of the app whose installer package name is to be retrieved.
* @return The package name of the installer if available, or `null` if not.
*/
fun getInstallerPackageName(context: Context, packageName: String): String? {
runCatching {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R)
return context.packageManager.getInstallSourceInfo(packageName).installingPackageName
@Suppress("DEPRECATION")
return context.packageManager.getInstallerPackageName(packageName)
}
return null
}

/**
* Converts the application icon of the specified package into a Base64 encoded string.
*
* @param context The context of the application.
* @param packageName The package name of the app whose icon is to be converted.
* @return A Base64 encoded string representing the app icon.
*/
fun parseIconBase64(context: Context, packageName: String): String? {
val result = runCatching {
val drawable = context.packageManager.getApplicationIcon(packageName)
val bitmap = drawable.toBitmap()
bitmap.toBase64()
}

return result.getOrNull()
}

/**
* Creates a Bitmap from a Drawable object.
*
* @param drawable The Drawable to be converted.
* @return A Bitmap representing the drawable.
*/
private fun createBitmapFromDrawable(drawable: Drawable): Bitmap {
val width = if (drawable.intrinsicWidth > 0) drawable.intrinsicWidth else 1
val height = if (drawable.intrinsicHeight > 0) drawable.intrinsicHeight else 1
val bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888)
val canvas = Canvas(bitmap)

drawable.setBounds(0, 0, canvas.width, canvas.height)
drawable.draw(canvas)

return bitmap
}

/**
* Converts a Drawable into a Bitmap.
*
* @receiver The Drawable to be converted.
* @return A Bitmap representing the drawable.
*/
private fun Drawable.toBitmap(): Bitmap {
return when (this) {
is BitmapDrawable -> bitmap
else -> createBitmapFromDrawable(this)
}
}

/**
* Converts a Bitmap into a Base64 encoded string.
*
* @receiver The Bitmap to be converted.
* @return A Base64 encoded string representing the bitmap.
*/
private fun Bitmap.toBase64(): String {
val byteArrayOutputStream = ByteArrayOutputStream()
compress(Bitmap.CompressFormat.PNG, 10, byteArrayOutputStream)
val byteArray = byteArrayOutputStream.toByteArray()
return Base64.encodeToString(byteArray, Base64.DEFAULT)
}
}

inline fun <reified T> JSONObject.extractArray(key: String): Array<T> {
val list = mutableListOf<T>()
if (this.has(key)) {
val jsonArray = this.getJSONArray(key)
for (i in 0 until jsonArray.length()) {
val element = when (T::class) {
String::class -> jsonArray.getString(i) as T
Int::class -> jsonArray.getInt(i) as T
Double::class -> jsonArray.getDouble(i) as T
Boolean::class -> jsonArray.getBoolean(i) as T
Long::class -> jsonArray.getLong(i) as T
else -> throw IllegalArgumentException("Unsupported type")
}
list.add(element)
}
}
return list.toTypedArray()
}
Loading