package com.aheaditec.freerasp.utils import android.content.Context import android.content.pm.PackageInfo import android.util.Base64 import android.util.Log import com.aheaditec.freerasp.models.CapPackageInfo import com.aheaditec.freerasp.models.CapSuspiciousAppInfo import com.aheaditec.talsec_security.security.api.MalwareScanScope import com.aheaditec.talsec_security.security.api.ReasonMode import com.aheaditec.talsec_security.security.api.ScopeType import com.aheaditec.talsec_security.security.api.SuspiciousAppDetectionConfig import com.aheaditec.talsec_security.security.api.SuspiciousAppInfo import com.getcapacitor.JSArray import kotlinx.serialization.encodeToString import kotlinx.serialization.json.Json import org.json.JSONArray import org.json.JSONException import org.json.JSONObject private inline fun JSONArray.toPrimitiveArray(): Array { val output = mutableListOf() for (i in 0 until this.length()) { val element: T = when (T::class) { String::class -> this.getString(i) as T Int::class -> this.getInt(i) as T Double::class -> this.getDouble(i) as T Long::class -> this.getLong(i) as T Boolean::class -> this.getBoolean(i) as T else -> throw JSONException("Cannot parse JSON array - unsupported type") } output.add(element) } return output.toTypedArray() } internal fun JSONObject.getArraySafe(key: String): Array { if (this.has(key)) { val inputArray = this.getJSONArray(key) return inputArray.toPrimitiveArray() } return arrayOf() } internal fun JSONObject.getNestedArraySafe(key: String): Array> { val outArray = mutableListOf>() if (this.has(key)) { val inputArray = this.getJSONArray(key) for (i in 0 until inputArray.length()) { outArray.add(inputArray.getJSONArray(i).toPrimitiveArray()) } } return outArray.toTypedArray() } internal fun JSONObject.toScanScope(): MalwareScanScope { val scopeType = ScopeType.valueOf(getString("scopeType")) val trustedInstallSources = optJSONArray("trustedInstallSources") ?.toPrimitiveArray()?.toList() return MalwareScanScope(scopeType, trustedInstallSources) } internal fun JSONObject.toSuspiciousAppDetectionConfig(): SuspiciousAppDetectionConfig { val scanScope = getJSONObject("scanScope").toScanScope() val reasonMode = ReasonMode.valueOf(getString("reasonMode")) return SuspiciousAppDetectionConfig( getArraySafe("packageNames").toSet().takeIf { it.isNotEmpty() }, getArraySafe("hashes").toSet().takeIf { it.isNotEmpty() }, getNestedArraySafe("requestedPermissions").map { it.toSet() }.toSet().takeIf { it.isNotEmpty() }, getNestedArraySafe("grantedPermissions").map { it.toSet() }.toSet().takeIf { it.isNotEmpty() }, scanScope, reasonMode, ) } /** * Converts the Talsec's SuspiciousAppInfo to Capacitor equivalent */ internal fun SuspiciousAppInfo.toCapSuspiciousAppInfo(context: Context): CapSuspiciousAppInfo { return CapSuspiciousAppInfo( packageInfo = this.packageInfo.toCapPackageInfo(context), reasons = this.reasons, permissions = this.permissions ) } /** * Converts the Android's PackageInfo to Capacitor equivalent */ internal fun PackageInfo.toCapPackageInfo(context: Context): CapPackageInfo { return CapPackageInfo( packageName = this.packageName, appName = Utils.getAppName(context, this.applicationInfo), version = this.versionName, appIcon = null, // this requires heavier computations, so appIcon has to be retrieved separately installerStore = Utils.getInstallationSource(context, this.packageName) ) } /** * Convert the Talsec's SuspiciousAppInfo to base64-encoded JSArray, * which can be then sent to Capacitor */ internal fun MutableList.toEncodedJSArray(context: Context): JSArray { val output = JSArray() this.forEach { suspiciousAppInfo -> val capSuspiciousAppInfo = suspiciousAppInfo.toCapSuspiciousAppInfo(context) try { val encodedAppInfo = Base64.encodeToString( Json.encodeToString(capSuspiciousAppInfo).toByteArray(), Base64.DEFAULT ) output.put(encodedAppInfo) } catch (e: Exception) { Log.e("Talsec", "Could not serialize suspicious app data: ${e.message}") } } return output }