Skip to content

Commit

Permalink
Merge pull request #67 from /issues/63-malwarelytics-android-1.2.1
Browse files Browse the repository at this point in the history
Update Malwarelytics for Android to 1.2.1
  • Loading branch information
TomasKypta authored Nov 8, 2024
2 parents 7889304 + e7e6d90 commit e6eac63
Show file tree
Hide file tree
Showing 36 changed files with 1,503 additions and 726 deletions.
8 changes: 4 additions & 4 deletions .github/workflows/build-android.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
name: Build
name: "Build Android"

on:
pull_request:
Expand All @@ -21,10 +21,10 @@ jobs:
- name: Prepare Wultra Repo Access
run: touch example/android/local.properties && echo -e "\nwultraArtifactory_username=${{ secrets.JFROG_USERNAME }}\nwultraArtifactory_password=${{ secrets.JFROG_PASSWORD }}" >> example/android/local.properties
- name: Install React Native CLI
run: yarn global add react-native
run: yarn global add "react-native@0.74.6"
- name: Install Library Dependencies
run: yarn install
run: yarn install --frozen-lockfile
- name: Install Example App Dependencies
run: cd example && yarn install
run: cd example && yarn install --frozen-lockfile
- name: Build Android App
run: cd example && react-native build-android
4 changes: 2 additions & 2 deletions .github/workflows/build-ios.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
name: Build
name: "Build iOS"

on:
pull_request:
Expand All @@ -15,7 +15,7 @@ jobs:
- name: Prepare Wultra Repo Access
run: touch ~/.netrc && chmod 0600 ~/.netrc && echo -e "\nmachine wultra.jfrog.io\n login ${{ secrets.JFROG_USERNAME }}\n password ${{ secrets.JFROG_PASSWORD }}" >> ~/.netrc
- name: Install React Native CLI
run: yarn global add react-native
run: yarn global add "react-native@0.74.6"
- name: Install Library Dependencies
run: yarn install
- name: Install Example App Dependencies
Expand Down
10 changes: 5 additions & 5 deletions android/gradle.properties
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
Malwarelytics_kotlinVersion=1.8.22
Malwarelytics_kotlinVersion=1.9.24
Malwarelytics_minSdkVersion=23
Malwarelytics_targetSdkVersion=33
Malwarelytics_compileSdkVersion=33
Malwarelytics_ndkversion=21.4.7075529
Malwarelytics_targetSdkVersion=34
Malwarelytics_compileSdkVersion=34
Malwarelytics_ndkversion=27.0.12077973

Malwarelytics_antivirusVersion=1.1.1
Malwarelytics_antivirusVersion=1.2.1
Malwarelytics_reactNativeVersion=0.71.6

# Required due to processing a massive react-android AAR
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ import com.facebook.react.bridge.ReadableMap
import com.wultra.android.antimalware.AntivirusConfig
import com.wultra.android.antimalware.ThreatIndex
import com.wultra.android.appprotection.AppProtectionConfig
import com.wultra.android.rasp.config.AdbDetectionConfig
import com.wultra.android.rasp.config.AppPresenceDetectionConfig
import com.wultra.android.rasp.config.BlockConfig
import com.wultra.android.rasp.config.DebuggerDetectionConfig
Expand All @@ -30,6 +29,7 @@ import com.wultra.android.rasp.config.ProcessNameConfig
import com.wultra.android.rasp.config.RepackageDetectionConfig
import com.wultra.android.rasp.config.RootDetectionConfig
import com.wultra.android.rasp.config.ScreenReaderBlockConfig
import com.wultra.android.rasp.config.ScreenshotConfig
import com.wultra.android.rasp.config.SimpleDetectionConfig
import com.wultra.android.rasp.config.TapjackingBlockConfig
import com.wultra.android.rasp.DebuggerType
Expand All @@ -45,7 +45,6 @@ fun ReadableMap.toAppProtectionConfig(context: Context, javaPackagePath: String)
val raspBuilder = RaspConfig.Builder()
mapOptAt("android.rasp")?.apply {
val jsObj = "MalwarelyticsAndroidRaspConfig"
val jsScreenReaders = "MalwarelyticsAndroidRaspScreenReadersBlockConfig"
mapOptAt("emulator", jsObj)?.let {
raspBuilder.emulator(it.toDetectionConfig("emulator"))
}
Expand All @@ -62,7 +61,7 @@ fun ReadableMap.toAppProtectionConfig(context: Context, javaPackagePath: String)
raspBuilder.screenSharing(it.toDetectionConfig("screenSharing"))
}
mapOptAt("screenshot", jsObj)?.let {
raspBuilder.screenshot(it.toBlockConfig("screenshot"))
raspBuilder.screenshot(it.toScreenshotConfig())
}
mapOptAt("screenReader", jsObj)?.let {
raspBuilder.screenReader(it.toScreenReaderBlockConfig())
Expand All @@ -80,14 +79,17 @@ fun ReadableMap.toAppProtectionConfig(context: Context, javaPackagePath: String)
raspBuilder.vpn(it.toDetectionConfig("vpn"))
}
mapOptAt("adb", jsObj)?.let {
raspBuilder.adb(it.toAdbDetectionConfig())
raspBuilder.adb(it.toDetectionConfig("adb"))
}
mapOptAt("activeCall", jsObj)?.let {
raspBuilder.activeCall(it.toSimpleDetectionConfig("activeCall"))
}
mapOptAt("appPresence", jsObj)?.let {
raspBuilder.appPresence(it.toAppPresenceDetectionConfig())
}
boolOptAt("sendInfoOutputs", jsObj)?.let {
raspBuilder.sendInfoOutputs(it)
}
}
val raspConfig = raspBuilder.build()

Expand Down Expand Up @@ -236,21 +238,11 @@ private fun ReadableMap.toRepackageDetectionConfig(): RepackageDetectionConfig {
return builder.build()
}

private fun ReadableMap.toAdbDetectionConfig(): AdbDetectionConfig {
val innerName: String = "MalwarelyticsAndroidRaspAdbDetectionConfig"
val exitUrl = stringOptAt("exitUrl")
return when (stringAt("action", innerName)) {
"NOTIFY" -> AdbDetectionConfig.Notify
"EXIT" -> AdbDetectionConfig.Exit(exitUrl)
else -> throw InvalidConfigException("adb.action", innerName)
}
}

private fun ReadableMap.toBlockConfig(path: String, innerName: String = "MalwarelyticsAndroidRaspBlockConfig"): BlockConfig {
return when (stringAt("action", innerName)) {
private fun ReadableMap.toBlockConfig(path: String, innerName: String = "MalwarelyticsAndroidRaspBlockConfig", actionName: String = "action"): BlockConfig {
return when (stringAt(actionName, innerName)) {
"NO_ACTION" -> BlockConfig.NoAction
"BLOCK" -> BlockConfig.Block
else -> throw InvalidConfigException("$path.action", innerName)
else -> throw InvalidConfigException("$path.$actionName", innerName)
}
}

Expand Down Expand Up @@ -300,6 +292,16 @@ private fun ReadableMap.toScreenReaderBlockConfig(): ScreenReaderBlockConfig {
return builder.build()
}

private fun ReadableMap.toScreenshotConfig(): ScreenshotConfig {
val innerName: String = "MalwarelyticsAndroidRaspScreenshotConfig"
val blockAction = toBlockConfig("screenshot", innerName, "blockAction")
val detectionAction = toSimpleDetectionConfig("screenshot", innerName, "detectionAction")
val builder = ScreenshotConfig.Builder()
.blockAction(blockAction)
.detectionAction(detectionAction)
return builder.build()
}

private fun ReadableMap.toProcessNameConfig(): ProcessNameConfig {
val innerName: String = "MalwarelyticsAndroidRaspProcessNameConfig"
val customProcessName = stringOptAt("customProcessName")
Expand All @@ -310,11 +312,11 @@ private fun ReadableMap.toProcessNameConfig(): ProcessNameConfig {
}
}

private fun ReadableMap.toSimpleDetectionConfig(path: String, innerName: String = "MalwarelyticsAndroidRaspSimpleDetectionConfig"): SimpleDetectionConfig {
return when (stringAt("action", innerName)) {
private fun ReadableMap.toSimpleDetectionConfig(path: String, innerName: String = "MalwarelyticsAndroidRaspSimpleDetectionConfig", actionName: String = "action"): SimpleDetectionConfig {
return when (stringAt(actionName, innerName)) {
"NO_ACTION" -> SimpleDetectionConfig.NoAction
"NOTIFY" -> SimpleDetectionConfig.Notify
else -> throw InvalidConfigException("$path.action", innerName)
else -> throw InvalidConfigException("$path.$actionName", innerName)
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,12 @@ fun ScreenReaderDetection.toJs(): ReadableMap {
.put("installedScreenReaders", installedScreenReaders)
}

fun ScreenshotDetection.toJs(): ReadableMap {
// ScreenshotInfo
return Arguments.createMap()
.put("activityClassName", activityClassName)
}

fun TapjackingDetection.toJs(): ReadableMap {
// TapjackingInfo
return Arguments.createMap()
Expand Down Expand Up @@ -194,5 +200,11 @@ fun DebuggerDetection.toJs(): ReadableMap {
return Arguments.createMap()
.put("isDebuggerAttached", isDebuggerAttached)
.put("isWaitingForDebugger", isWaitingForDebugger)
.put("debbuggerType", debbuggerType.toJs { it.name })
.put("debuggerTypes", debuggerTypes.toJs { it.name })
}

fun SpoofedLocationDetection.toJs(): ReadableMap {
// SpoofedLocationInfo
return Arguments.createMap()
.put("mockLocationApps", mockLocationApps)
}
Original file line number Diff line number Diff line change
Expand Up @@ -290,6 +290,8 @@ class MalwarelyticsModule(reactContext: ReactApplicationContext) :
RaspMessageType.ON_CALL -> rasp.isCallActive()
RaspMessageType.ACTIVE_CALL -> rasp.getActiveCallDetection().toJs()
RaspMessageType.APP_PRESENCE -> rasp.getAppPresenceDetection().toJs()
RaspMessageType.SCREENSHOT_ANDROID -> throw InvalidParameterException("SCREENSHOT_ANDROID")
RaspMessageType.SPOOFED_LOCATION_INFO -> rasp.getSpoofedLocationDetection().toJs()
}
}

Expand Down Expand Up @@ -321,6 +323,8 @@ class MalwarelyticsModule(reactContext: ReactApplicationContext) :
ADB_STATUS,
ACTIVE_CALL,
APP_PRESENCE,
// Following types are only reported by the listener.
SCREENSHOT_ANDROID,
// Following types are accessed only by the getter.
SCREEN_LOCK,
PLAY_PROTECT,
Expand All @@ -329,7 +333,8 @@ class MalwarelyticsModule(reactContext: ReactApplicationContext) :
DEVELOPER_MODE,
ON_CALL,
BIOMETRY,
DEBUGGER_INFO
DEBUGGER_INFO,
SPOOFED_LOCATION_INFO
}

/**
Expand Down Expand Up @@ -368,6 +373,10 @@ class MalwarelyticsModule(reactContext: ReactApplicationContext) :
sendRaspEvent(RaspMessageType.SCREEN_READER, screenReaderDetection.toJs(), screenReaderDetection.isNotAllowedScreenReaderEnabled)
}

override fun onScreenshotDetected(screenshotDetection: ScreenshotDetection) {
sendRaspEvent(RaspMessageType.SCREENSHOT_ANDROID, screenshotDetection.toJs(), true)
}

override fun onTapjackingDetected(tapjackingDetection: TapjackingDetection) {
sendRaspEvent(RaspMessageType.TAPJACKING, tapjackingDetection.toJs(), tapjackingDetection.isTapjackingBlocked)
}
Expand Down Expand Up @@ -446,7 +455,7 @@ class MalwarelyticsModule(reactContext: ReactApplicationContext) :
@ReactMethod
fun isAntivirusEnabled(promise: Promise) {
resolveOnQueue(promise) { service ->
service.getAppProtectionConfig().antivirusConfig.isAntivirusEnabled()
service.getAntivirus().isEnabled()
}
}

Expand Down
69 changes: 37 additions & 32 deletions docs/Configuration-Antivirus-UI.md
Original file line number Diff line number Diff line change
@@ -1,25 +1,26 @@
# Configuration of the Antivirus UI for Android

Antivirus component for the Android platform can display UI to alert the user about the threat. The UI can be styled so it fits the theme of your app. To properly customize the Malwarelytics Antivirus UI, you will need to alter Android resources and gradle scripts in your application.
Antivirus components for the Android platform can display UI to alert the user about the threat. The UI can be styled so it fits the theme of your app. To properly customize the Malwarelytics Antivirus UI, you will need to alter Android resources and Gradle scripts in your application.

## Styling Threat Screen

Threat screen can by styled by setting custom theme for the activity and by changing icons for the image buttons on the screen.
The threat screen can be styled by setting a custom theme for the activity and by changing icons for the image buttons on the screen.

There are three icons. Notification, delete icon and settings icon:
There are three icons. Notification, delete icon, and settings icon:

- Delete icon serves for button requesting app uninstall (for apps that can be uninstalled).
- Settings icon serves for button opening app's detail in system settings (for apps that cannot be uninstalled) where user can disable the app.
- The delete icon serves as a button requesting app uninstall (for apps that can be uninstalled).
- Settings icon serves as a button opening the app's details in system settings (for apps that cannot be uninstalled) where the user can disable the app.
- Notification icon is displayed in the notification.

The theme for the screen should contain standard Android attributes and can contain couple of extra attributes defined by the AV SDK.
The theme for the screen should contain standard Android attributes and can contain a couple of extra attributes defined by the AV SDK.

We recommend to derive the theme from AppCompat themes. The usage of ActionBar in the screen is derived from the theme. Use `NoActionBar` version of the theme for design without the ActionBar (contains the app name). Alternatively you can hide the ActionBar by adding
We recommend deriving the theme from AppCompat themes. The usage of ActionBar on the screen is derived from the theme. Use the `NoActionBar` version of the theme for design without the ActionBar (which contains the app name). Alternatively, you can hide the ActionBar by adding

```xml
<item name="windowActionBar">false</item>
<item name="windowNoTitle">true</item>
```

to your theme.

Following is an example of a theme using Wultra-defined attributes that you can put into your `android/app/src/main/res/values/malwarelytics.xml` file:
Expand Down Expand Up @@ -114,27 +115,31 @@ Following is an example of a theme using Wultra-defined attributes that you can

Once you create your custom theme, then you have to apply it via the custom gradle scripts from the Malwarelytics for React Native library:

1. Open `android/app/build.gradle` file and navigate to the top of the file
1. Open the `android/app/build.gradle` file and navigate to the top of the file

1. Skip initial imports such as:
```gradle
apply plugin: "com.android.application"
apply plugin: "com.facebook.react"
```
and add the following line:
```gradle
apply from: "../../node_modules/react-native-malwarelytics/android/malwarelytics.gradle"
```

```gradle
apply plugin: "com.android.application"
apply plugin: "com.facebook.react"
```

and add the following line:

```gradle
apply from: "../../node_modules/react-native-malwarelytics/android/malwarelytics.gradle"
```

1. Alter `defaultConfig` section in `android` and add the following line:
```gradle
android {
defaultConfig {
// Apply custom SmartProtection's UI configuration
configureMalwarelyticsUI owner, "R.style.CustomAppTheme"
}
}
```

```gradle
android {
defaultConfig {
// Apply custom SmartProtection's UI configuration
configureMalwarelyticsUI owner, "R.style.CustomAppTheme"
}
}
```

1. Rebuild your application

Expand All @@ -143,14 +148,14 @@ Once you create your custom theme, then you have to apply it via the custom grad

The `configureMalwarelyticsUI` function has the following parameters:

- `buildFlavor` is required, you use `owner` variable.
- `theme` is optional theme resource ID for Antivirus UI, for example `"R.style.CustomAppTheme"`
- `notificationSmallIcon` is optional resource ID for notificaiton icon, for example `"R.drawable.av_notification_icon"`
- `notificationChannelId` is optional string with channel ID for notification
- `deleteIcon` is optional resource ID for delete icon, for example `"R.drawable.av_delete_icon"`
- `settingsIcon` is optional resource ID for settings icon, for example `"R.drawable.av_settings_icon"`
- `buildFlavor` is required, you use the `owner` variable.
- `theme` is an optional theme resource ID for Antivirus UI, for example `"R.style.CustomAppTheme"`
- `notificationSmallIcon` is an optional resource ID for the notification icon, for example `"R.drawable.av_notification_icon"`
- `notificationChannelId` is an optional string with channel ID for notification
- `deleteIcon` is an optional resource ID for delete icon, for example `"R.drawable.av_delete_icon"`
- `settingsIcon` is an optional resource ID for the settings icon, for example `"R.drawable.av_settings_icon"`

You have to use `"null"` for each parameter that must be skip, for example:
You have to use `"null"` for each parameter that must be skipped, for example:

```gradle
android {
Expand All @@ -161,7 +166,7 @@ android {
}
```

The script above configure UI with theme, delete and settings icons, but and skip notification icon and channel ID.
The script above configure UI with a theme, delete, and settings icons, but skips the notification icon and channel ID.


## Read Next
Expand Down
Loading

0 comments on commit e6eac63

Please sign in to comment.