Skip to content

Commit

Permalink
Make malware async (#25)
Browse files Browse the repository at this point in the history
* fix(Android): move malware processing to background thread

* fix(ts): move malware processing to background thread

* feat(example): move malware processing to background thread

* docs: update changelog

* chore(release): freeRASP 1.8.0

* chore: bug fix
  • Loading branch information
tompsota authored Dec 16, 2024
1 parent 86b27cd commit 5db8538
Show file tree
Hide file tree
Showing 14 changed files with 151 additions and 30 deletions.
18 changes: 18 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,24 @@ 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).

## [1.8.0] - 2024-12-06

- iOS SDK version: 6.6.3
- Android SDK version: 13.0.0

### Capacitor

#### Changed

- App icons for detected malware are not fetched automatically anymore, which reduces computation required to retrieve malware data. From now on, app icons have to be retrieved using the `getAppIcon` method
- Parsing of malware data is now async

### Android

#### Changed

- Malware data is now parsed on background thread to improve responsiveness

## [1.7.0] - 2024-11-19

### Capacitor
Expand Down
55 changes: 51 additions & 4 deletions android/src/main/java/com/aheaditec/freerasp/FreeraspPlugin.kt
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
package com.aheaditec.freerasp

import android.os.Handler
import android.os.HandlerThread
import android.os.Looper
import com.aheaditec.freerasp.utils.Utils
import com.aheaditec.freerasp.utils.getArraySafe
import com.aheaditec.freerasp.utils.getNestedArraySafe
import com.aheaditec.freerasp.utils.toEncodedJSArray
Expand Down Expand Up @@ -37,7 +41,11 @@ class FreeraspPlugin : Plugin() {
}
call.resolve(JSObject().put("started", true))
} catch (e: Exception) {
call.reject("Error during Talsec Native plugin initialization - ${e.message}", "TalsecInitializationError", e)
call.reject(
"Error during Talsec Native plugin initialization - ${e.message}",
"TalsecInitializationError",
e
)
}
}

Expand All @@ -57,6 +65,11 @@ class FreeraspPlugin : Plugin() {
}
}

override fun handleOnDestroy() {
super.handleOnDestroy()
backgroundHandlerThread.quitSafely()
}

/**
* Method to get the random identifiers of callbacks
*/
Expand Down Expand Up @@ -107,13 +120,44 @@ class FreeraspPlugin : Plugin() {
call.resolve(JSObject().put("result", true))
}

/**
* Method retrieves app icon for the given parameter
* @param packageName package name of the app we want to retrieve icon for
* @return PNG with app icon encoded as a base64 string
*/
@PluginMethod
fun getAppIcon(call: PluginCall) {
val packageName = call.getString("packageName")
if (packageName.isNullOrEmpty()) {
call.reject(
"Package name argument is missing or empty in the call",
"MissingArgumentError"
)
return
}
// Perform the app icon encoding on a background thread
backgroundHandler.post {
val encodedData = Utils.getAppIconAsBase64String(context, packageName)
mainHandler.post { call.resolve(JSObject().put("result", encodedData)) }
}
}

internal fun notifyListeners(threat: Threat) {
notifyListeners(THREAT_CHANNEL_NAME, JSObject().put(THREAT_CHANNEL_KEY, threat.value), true)
}

internal fun notifyMalware(suspiciousApps: MutableList<SuspiciousAppInfo>) {
notifyListeners(THREAT_CHANNEL_NAME, JSObject().put(THREAT_CHANNEL_KEY, Threat.Malware.value).put(
MALWARE_CHANNEL_KEY, suspiciousApps.toEncodedJSArray(context)), true)
// Perform the malware encoding on a background thread
backgroundHandler.post {

val encodedSuspiciousApps = suspiciousApps.toEncodedJSArray(context)
mainHandler.post {
val params = JSObject()
.put(THREAT_CHANNEL_KEY, Threat.Malware.value)
.put(MALWARE_CHANNEL_KEY, encodedSuspiciousApps)
notifyListeners(THREAT_CHANNEL_NAME, params, true)
}
}
}

private fun buildTalsecConfigThrowing(configJson: JSObject): TalsecConfig {
Expand All @@ -140,7 +184,10 @@ class FreeraspPlugin : Plugin() {
.toString() // name of the channel over which threat callbacks are sent
private val THREAT_CHANNEL_KEY = (10000..999999999).random()
.toString() // key of the argument map under which threats are expected
val MALWARE_CHANNEL_KEY = (10000..999999999).random()
private val MALWARE_CHANNEL_KEY = (10000..999999999).random()
.toString() // key of the argument map under which malware data is expected
private val backgroundHandlerThread = HandlerThread("BackgroundThread").apply { start() }
private val backgroundHandler = Handler(backgroundHandlerThread.looper)
private val mainHandler = Handler(Looper.getMainLooper())
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ internal fun PackageInfo.toCapPackageInfo(context: Context): CapPackageInfo {
packageName = this.packageName,
appName = Utils.getAppName(context, this.applicationInfo),
version = this.versionName,
appIcon = Utils.getAppIconAsBase64String(context, this.packageName),
appIcon = null, // this requires heavier computations, so appIcon has to be retrieved separately
installerStore = Utils.getInstallationSource(context, this.packageName)
)
}
Expand Down
5 changes: 4 additions & 1 deletion android/src/main/java/com/aheaditec/freerasp/utils/Utils.kt
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,10 @@ internal object Utils {
context.packageManager.getInstallerPackageName(packageName)
}
} catch (e: Exception) {
Log.e("Talsec", "Could not retrieve app installation source for ${packageName}: ${e.message}")
Log.e(
"Talsec",
"Could not retrieve app installation source for ${packageName}: ${e.message}"
)
null
}
}
Expand Down
5 changes: 5 additions & 0 deletions dist/esm/definitions.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,11 @@ export interface FreeraspPlugin {
}): Promise<{
result: boolean;
}>;
getAppIcon(options: {
packageName: string;
}): Promise<{
result: string;
}>;
}
export type FreeraspConfig = {
androidConfig?: AndroidConfig;
Expand Down
2 changes: 1 addition & 1 deletion dist/esm/definitions.js.map

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion dist/esm/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,6 @@ declare const setThreatListeners: <T extends NativeEventEmitterActions>(callback
declare const removeThreatListeners: () => void;
declare const startFreeRASP: <T extends NativeEventEmitterActions>(config: FreeraspConfig, reactions: T & Record<Exclude<keyof T, keyof NativeEventEmitterActions>, []>) => Promise<boolean>;
declare const addToWhitelist: (packageName: string) => Promise<boolean>;
declare const getAppIcon: (packageName: string) => Promise<string>;
export * from './definitions';
export { Freerasp, startFreeRASP, setThreatListeners, removeThreatListeners, addToWhitelist, };
export { Freerasp, startFreeRASP, setThreatListeners, removeThreatListeners, addToWhitelist, getAppIcon, };
25 changes: 20 additions & 5 deletions dist/esm/index.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit 5db8538

Please sign in to comment.