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 70 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
70 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
d431054
fix: CHANGELOG reformat
yardexx Oct 15, 2024
ffc93dd
style: version break
yardexx Oct 16, 2024
f0d9594
feat: raise SDK version
yardexx Oct 16, 2024
dbd94fe
style: resolve issues
yardexx Oct 16, 2024
9cdde4c
feat: malware sheet scrollable
yardexx Oct 16, 2024
e45da01
feat: update parsing
yardexx Oct 18, 2024
7fee0a9
feat: update models
yardexx Oct 18, 2024
ddb3496
feat: update example app
yardexx Oct 18, 2024
84220bd
style: fix missing comma
yardexx Oct 18, 2024
8e33968
Merge branch 'master' into release/6.8.0
yardexx Oct 22, 2024
5cff44d
fix: update versions to compatible ones
yardexx Oct 22, 2024
0ebab1a
Merge remote-tracking branch 'origin/release/6.8.0' into release/6.8.0
yardexx Oct 22, 2024
cd8471c
fix: CHANGELOG.md
yardexx Oct 22, 2024
0685c4b
fix: extension name
yardexx Oct 22, 2024
1969841
fix: remove function
yardexx Oct 22, 2024
f724346
fix: rename extension name
yardexx Oct 22, 2024
706eb21
fix: version number
yardexx Oct 22, 2024
f5f1980
fix: missing docs
yardexx Oct 22, 2024
0b677f3
fix: style
yardexx Oct 22, 2024
8549784
fix: unused import
yardexx Oct 22, 2024
fdfb193
build: update dependencies
yardexx Oct 23, 2024
05f60fc
chore: update tests
yardexx Oct 23, 2024
2254ab2
feat: exclude null properties from json
yardexx Oct 23, 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.3
- iOS SDK version: 6.6.0

### Flutter

#### Added
- Configuration fields for Malware Detection

### Android

#### Added
- New feature: Malware Detection

## [6.7.2] - 2024-10-18

### Android
Expand Down
6 changes: 5 additions & 1 deletion 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
include: package:very_good_analysis/analysis_options.yaml

analyzer:
exclude:
- '**/*.g.dart'
50 changes: 50 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,47 @@ 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 = context.packageManager.getApplicationLabel(applicationInfo) as String,
Copy link
Member

Choose a reason for hiding this comment

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

applicationInfo is nullable since API34, can you fix this here? (and please validate that gitbook docs take this into accout as well)

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()
}
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
176 changes: 176 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,176 @@
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.JSONArray
import org.json.JSONException
import org.json.JSONObject
import java.io.ByteArrayOutputStream

internal object Utils {
@Suppress("ArrayInDataClass")
data class MalwareConfig(
val blocklistedPackageNames: Array<String>,
val blocklistedHashes: Array<String>,
val blocklistedPermissions: Array<Array<String>>,
val whitelistedInstallationSources: Array<String>
)

fun toTalsecConfigThrowing(configJson: String?): TalsecConfig {
if (configJson == null) {
throw JSONException("Configuration is null")
}

val json = JSONObject(configJson)

val watcherMail = json.getString("watcherMail")
val 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 malwareConfig = parseMalwareConfig(androidConfig)

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

private fun parseMalwareConfig(androidConfig: JSONObject): MalwareConfig {
if (!androidConfig.has("malwareConfig")) {
return MalwareConfig(emptyArray(), emptyArray(), emptyArray(), emptyArray())
}

val malwareConfig = androidConfig.getJSONObject("malwareConfig")

return MalwareConfig(
malwareConfig.extractArray("blocklistedPackageNames"),
malwareConfig.extractArray("blocklistedHashes"),
malwareConfig.extractArray<Array<String>>("blocklistedPermissions"),
malwareConfig.extractArray("whitelistedInstallationSources")
)
}


/**
* 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.NO_WRAP)
}
}

private inline fun <reified T> JSONObject.extractArray(key: String): Array<T> {
return this.optJSONArray(key)?.let { processArray(it) } ?: emptyArray()
}

private inline fun <reified T> processArray(jsonArray: JSONArray): Array<T> {
val list = mutableListOf<T>()

for (i in 0 until jsonArray.length()) {
val element: T = 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
Array<String>::class -> {
// Not universal or ideal solution, but should work for our use case
val nestedArray = jsonArray.getJSONArray(i)
val nestedList = mutableListOf<String>()
for (j in 0 until nestedArray.length()) {
nestedList.add(nestedArray.getString(j))
}
nestedList.toTypedArray() as T
}

else -> throw JSONException("Unsupported type")
}
list.add(element)
}

return list.toTypedArray()
}
Loading