Skip to content

Commit 32f1eed

Browse files
authored
Make malware async (#50)
* fix(Android): make malware parsing async * fix(ts): make malware parsing async * core(example): make malware parsing async * chore(release): freerasp 7.2.0
1 parent 8621a60 commit 32f1eed

File tree

11 files changed

+120
-79
lines changed

11 files changed

+120
-79
lines changed

CHANGELOG.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,24 @@ All notable changes to this project will be documented in this file.
55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
66
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
77

8+
## [7.2.0] - 2024-12-06
9+
10+
- iOS SDK version: 6.6.3
11+
- Android SDK version: 13.0.0
12+
13+
### Cordova
14+
15+
#### Changed
16+
17+
- 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
18+
- Parsing of malware data is now async
19+
20+
### Android
21+
22+
#### Changed
23+
24+
- Malware data is now parsed on background thread to improve responsiveness
25+
826
## [7.1.0] - 2024-11-19
927

1028
### Cordova

example/package-lock.json

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

example/src/app/components/malware-item/malware-item.component.ts

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,26 @@
1-
import { Component, Input } from '@angular/core';
2-
import { SuspiciousAppInfo } from 'cordova-talsec-plugin-freerasp';
3-
declare var talsec: any;
1+
import { Component, Input, OnInit } from '@angular/core';
2+
import { SuspiciousAppInfo, Talsec } from 'cordova-talsec-plugin-freerasp';
3+
declare var talsec: Talsec;
44

55
@Component({
66
selector: 'app-malware-item',
77
templateUrl: './malware-item.component.html',
88
styleUrls: ['./malware-item.component.css', '../../../theme/variables.css'],
99
})
10-
export class MalwareItemComponent {
10+
export class MalwareItemComponent implements OnInit {
1111
@Input() susApp!: SuspiciousAppInfo;
1212
expanded = false;
1313

14+
ngOnInit(): void {
15+
this.loadAppIcon();
16+
}
17+
18+
async loadAppIcon(): Promise<void> {
19+
this.susApp.packageInfo.appIcon = await talsec.getAppIcon(
20+
this.susApp.packageInfo.packageName,
21+
);
22+
}
23+
1424
toggleExpanded() {
1525
this.expanded = !this.expanded;
1626
}

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "cordova-talsec-plugin-freerasp",
3-
"version": "7.1.0",
3+
"version": "7.2.0",
44
"description": "Cordova plugin for improving app security and threat monitoring on Android and iOS mobile devices.",
55
"cordova": {
66
"id": "cordova-talsec-plugin-freerasp",

plugin.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
<plugin xmlns="http://apache.org/cordova/ns/plugins/1.0"
44
id="cordova-talsec-plugin-freerasp"
5-
version="7.1.0">
5+
version="7.2.0">
66

77
<name>freerasp</name>
88
<author>Talsec ([email protected])</author>

src/android/TalsecPlugin.kt

Lines changed: 49 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,11 @@
11
package com.aheaditec.talsec.cordova
22

33
import android.content.Context
4+
import android.os.Handler
5+
import android.os.HandlerThread
6+
import android.os.Looper
47
import android.util.Log
8+
import com.aheaditec.talsec.cordova.utils.Utils
59
import com.aheaditec.talsec.cordova.utils.getArraySafe
610
import com.aheaditec.talsec.cordova.utils.getBooleanSafe
711
import com.aheaditec.talsec.cordova.utils.getNestedArraySafe
@@ -47,6 +51,7 @@ class TalsecPlugin : CordovaPlugin() {
4751
"getThreatIdentifiers" -> getThreatIdentifiers(callbackContext)
4852
"getThreatChannelData" -> getThreatChannelData(callbackContext)
4953
"addToWhitelist" -> addToWhitelist(callbackContext, args)
54+
"getAppIcon" -> getAppIcon(callbackContext, args)
5055
else -> {
5156
callbackContext?.error("Talsec plugin executed with unknown action - $action")
5257
return false
@@ -88,6 +93,24 @@ class TalsecPlugin : CordovaPlugin() {
8893
return true
8994
}
9095

96+
/**
97+
* Method retrieves app icon for the given parameter
98+
* @param packageName package name of the app we want to retrieve icon for
99+
* @return PNG with app icon encoded as a base64 string
100+
*/
101+
fun getAppIcon(callbackContext: CallbackContext?, args: JSONArray?): Boolean {
102+
val packageName = args?.optString(0, null) ?: run {
103+
callbackContext?.error("Missing packageName parameter in Talsec Native Plugin")
104+
return false
105+
}
106+
// Perform the app icon encoding on a background thread
107+
backgroundHandler.post {
108+
val encodedData = Utils.getAppIconAsBase64String(cordova.context, packageName)
109+
mainHandler.post { callbackContext?.success(encodedData) }
110+
}
111+
return true
112+
}
113+
91114
override fun onPause(multitasking: Boolean) {
92115
super.onPause(multitasking)
93116
if (this.cordova.activity.isFinishing) {
@@ -104,6 +127,12 @@ class TalsecPlugin : CordovaPlugin() {
104127
}
105128
}
106129

130+
override fun onDestroy() {
131+
super.onDestroy()
132+
133+
backgroundHandlerThread.quitSafely()
134+
}
135+
107136
/**
108137
* We never send an invalid callback over our channel.
109138
* Therefore, if this happens, we want to kill the app.
@@ -166,23 +195,35 @@ class TalsecPlugin : CordovaPlugin() {
166195
companion object {
167196
private var callback: CallbackContext? = null
168197

169-
val THREAT_CHANNEL_KEY = (10000..999999999).random()
198+
private val THREAT_CHANNEL_KEY = (10000..999999999).random()
170199
.toString() // key of the argument map under which threats are expected
171-
val MALWARE_CHANNEL_KEY = (10000..999999999).random()
200+
private val MALWARE_CHANNEL_KEY = (10000..999999999).random()
172201
.toString() // key of the argument map under which malware data is expected
173202

174203
private lateinit var appContext: Context
204+
private val backgroundHandlerThread = HandlerThread("BackgroundThread").apply { start() }
205+
private val backgroundHandler = Handler(backgroundHandlerThread.looper)
206+
private val mainHandler = Handler(Looper.getMainLooper())
207+
175208

176209
/**
177210
* Sends malware detected event to Cordova
178211
*/
179212
private fun notifyMalware(suspiciousApps: MutableList<SuspiciousAppInfo>) {
180-
val response = JSONObject()
181-
response.put(THREAT_CHANNEL_KEY, Threat.Malware.value)
182-
response.put(MALWARE_CHANNEL_KEY, suspiciousApps.toEncodedJsonArray(appContext))
183-
val result = PluginResult(PluginResult.Status.OK, response)
184-
result.keepCallback = true
185-
callback?.sendPluginResult(result) ?: Log.w("TalsecPlugin", "Listener not registered.")
213+
// Perform the malware encoding on a background thread
214+
backgroundHandler.post {
215+
216+
val encodedSuspiciousApps = suspiciousApps.toEncodedJsonArray(appContext)
217+
218+
mainHandler.post {
219+
val response = JSONObject()
220+
response.put(THREAT_CHANNEL_KEY, Threat.Malware.value)
221+
response.put(MALWARE_CHANNEL_KEY, encodedSuspiciousApps)
222+
val result = PluginResult(PluginResult.Status.OK, response)
223+
result.keepCallback = true
224+
callback?.sendPluginResult(result) ?: Log.w("TalsecPlugin", "Listener not registered.")
225+
}
226+
}
186227
}
187228

188229
private fun notifyThreat(threat: Threat) {

src/android/utils/Extensions.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ internal fun PackageInfo.toCordovaPackageInfo(context: Context): CordovaPackageI
8181
packageName = this.packageName,
8282
appName = Utils.getAppName(context, this.applicationInfo),
8383
version = this.versionName,
84-
appIcon = Utils.getAppIconAsBase64String(context, this.packageName),
84+
appIcon = null, // this requires heavier computations, so appIcon has to be retrieved separately
8585
installerStore = Utils.getInstallationSource(context, this.packageName)
8686
)
8787
}

src/android/utils/Utils.kt

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,10 @@ internal object Utils {
7474
context.packageManager.getInstallerPackageName(packageName)
7575
}
7676
} catch (e: Exception) {
77-
Log.e("Talsec", "Could not retrieve app installation source for ${packageName}: ${e.message}")
77+
Log.e(
78+
"Talsec",
79+
"Could not retrieve app installation source for ${packageName}: ${e.message}"
80+
)
7881
null
7982
}
8083
}

www/talsec.d.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ export interface Talsec {
44
eventListenerConfig: NativeEventEmitterActions,
55
) => Promise<void>;
66
addToWhitelist: (packageName: string) => Promise<string>;
7+
getAppIcon: (packageName: string) => Promise<string>;
78
}
89
export type SuspiciousAppInfo = {
910
packageInfo: PackageInfo;

www/talsec.js

Lines changed: 14 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -58,16 +58,7 @@ const getThreatCount = () => {
5858
const getThreatChannelData = async () => {
5959
const dataLength = cordova.platformId === 'ios' ? 1 : 2;
6060
const data = await new Promise((resolve, reject) => {
61-
cordova.exec(
62-
(data) => {
63-
resolve(data);
64-
},
65-
(error) => {
66-
reject(error);
67-
},
68-
'TalsecPlugin',
69-
'getThreatChannelData',
70-
);
61+
cordova.exec(resolve, reject, 'TalsecPlugin', 'getThreatChannelData');
7162
});
7263
if (data.length !== dataLength || !itemsHaveType(data, 'string')) {
7364
onInvalidCallback();
@@ -80,16 +71,7 @@ const itemsHaveType = (data, desidedType) => {
8071
};
8172
const getThreatIdentifiers = async () => {
8273
const identifiers = await new Promise((resolve, reject) => {
83-
cordova.exec(
84-
(data) => {
85-
resolve(data);
86-
},
87-
(error) => {
88-
reject(error);
89-
},
90-
'TalsecPlugin',
91-
'getThreatIdentifiers',
92-
);
74+
cordova.exec(resolve, reject, 'TalsecPlugin', 'getThreatIdentifiers');
9375
});
9476
if (
9577
identifiers.length !== getThreatCount() ||
@@ -203,21 +185,22 @@ const addToWhitelist = (packageName) => {
203185
return Promise.reject('Malware detection not available on iOS');
204186
}
205187
return new Promise((resolve, reject) => {
206-
cordova.exec(
207-
(response) => {
208-
resolve(response);
209-
},
210-
(error) => {
211-
reject(error);
212-
},
213-
'TalsecPlugin',
214-
'addToWhitelist',
215-
[packageName],
216-
);
188+
cordova.exec(resolve, reject, 'TalsecPlugin', 'addToWhitelist', [
189+
packageName,
190+
]);
191+
});
192+
};
193+
const getAppIcon = (packageName) => {
194+
if (cordova.platformId === 'ios') {
195+
return Promise.reject('Malware detection not available on iOS');
196+
}
197+
return new Promise((resolve, reject) => {
198+
cordova.exec(resolve, reject, 'TalsecPlugin', 'getAppIcon', [packageName]);
217199
});
218200
};
219201
// @ts-ignore
220202
module.exports = {
221203
start,
222204
addToWhitelist,
205+
getAppIcon,
223206
};

0 commit comments

Comments
 (0)