Skip to content

Commit

Permalink
Merge pull request #60 from /issues/53-malwarelytics-for-apple-2.1.1
Browse files Browse the repository at this point in the history
Update Malwarelytics for Apple to 2.1.1
  • Loading branch information
TomasKypta authored May 24, 2024
2 parents f72af8a + 3c4623c commit 4f797a8
Show file tree
Hide file tree
Showing 22 changed files with 503 additions and 53 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -105,9 +105,12 @@ fun ActiveCallDetection.toJs(): ReadableMap {
}

fun AppPresenceDetection.toJs(): ReadableMap {
// AndroidAppPresenceInfo
val androidAppPresenceInfo = Arguments.createMap()
.put("remoteDesktopApps", remoteDesktopApps.toJs { it.toJs() })
// AppPresenceInfo
return Arguments.createMap()
.put("remoteDesktopApps", remoteDesktopApps.toJs { it.toJs() })
.put("androidAppPresenceInfo", androidAppPresenceInfo)
}

fun NamedApkItemInfo.toJs(): ReadableMap {
Expand Down
22 changes: 21 additions & 1 deletion docs/Configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,18 @@ await Malwarelytics.sharedInstance.initialize({
// that prevents the debugger to be connected to the process.
debugger: { action: 'BLOCK' },
// Action defined when device's screen is being captured.
screenCapture: { action: 'NOTIFY' },
screenCapture: {
action: 'HIDE', // Hide the app screen
overlay: {
type: 'COLOR', // Use overlay with the specified color
color: {
red: 128,
blue: 0,
green: 0,
alpha: 255
}
}
},
// Action defined when reverse engineering tools are detected on the device.
reverseEngineeringTools: { action: 'EXIT' },
// Action defined when HTTP proxy is configured on the device.
Expand All @@ -63,6 +74,15 @@ await Malwarelytics.sharedInstance.initialize({
exitUrl: "https://wultra.com/?exit=repackage",
// Array with Base64 encoded certificates that used to sign app package.
trustedCertificates: ["BASE64"]
},
// Action defined when there's an ongoing call
call: {
action: "NOTIFY"
},
// Action defined when a presence of an app from the list is detected.
appPresence: {
action: "NOTIFY",
apps: iosDetectableApps // Array with apps that can be detected. Predefined array can be obtained from the native SDK with await Malwarelytics.sharedInstance.getKnownDetectableApps()
}
},
// Optional configuration of the events collection.
Expand Down
4 changes: 2 additions & 2 deletions docs/Installation.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ Malwarelytics for React Native is distributed as a NPM package. The private Mave
## Platform Support

<!-- begin box info -->
Malwarelytics for React Native __supports iOS 11 and Android 5.1__ (SDK version 22) and above. The React Native 0.71 and above is recommended for your application.
Malwarelytics for React Native __supports iOS 13.4 and Android 6.0__ (SDK version 23) and above. The React Native 0.74 and above is recommended for your application.
<!-- end -->

## Configure Wultra's Artifactory
Expand Down Expand Up @@ -35,7 +35,7 @@ allProjects {
}
```

In the same file, look for `minSdkVersion` and make sure that it's higher or equal to `22`.
In the same file, look for `minSdkVersion` and make sure that it's higher or equal to `23`.

If you don't want to expose your credentials in the gradle file, then use the script to load the credentials from `local.properties`. For example:

Expand Down
1 change: 1 addition & 0 deletions docs/Release-Notes.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

### Release 1.0.3-dev

- Update Malwarelytics for Apple to 2.1.1 (#53)
- Update Malwarelytics for Android to 1.0.2 (#52)
- Update project configuration (#56)
- Fix iOS build (#57)
Expand Down
2 changes: 1 addition & 1 deletion docs/Usage-RASP.md
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,7 @@ console.log(`Screen sharing info: ${JSON.stringify(screenSharing)}`);

console.log(`Is VPN active = ${await rasp.isVpnActive()}`);
console.log(`Phone call = ${await rasp.isOnCall()}`);
console.log(`App presence info = ${JSON.stringify(await rasp.getAppPresenceInfo())}`);

// Apple specific

Expand All @@ -138,7 +139,6 @@ if (Platform.OS == 'android') {
console.log(`Bad Tapjacking capable app is present = ${await rasp.isBadTapjackingCapableAppPresent()}`);
console.log(`Developer options enabled = ${await rasp.isDeveloperOptionsEnabled()}`);
console.log(`Active call info = ${JSON.stringify(await rasp.getActiveCallInfo())}`);
console.log(`App presence info = ${JSON.stringify(await rasp.getAppPresenceInfo())}`);
}
```

Expand Down
2 changes: 1 addition & 1 deletion example/android/gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
# Specifies the JVM arguments used for the daemon process.
# The setting is particularly useful for tweaking memory settings.
# Default value: -Xmx512m -XX:MaxMetaspaceSize=256m
org.gradle.jvmargs=-Xmx2048m -XX:MaxMetaspaceSize=512m
org.gradle.jvmargs=-Xmx4g -XX:MaxMetaspaceSize=512m

# When configured, Gradle will run in incubating parallel mode.
# This option should only be used with decoupled projects. More details, visit
Expand Down
12 changes: 12 additions & 0 deletions example/ios/MalwarelyticsExample/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -51,5 +51,17 @@
</array>
<key>UIViewControllerBasedStatusBarAppearance</key>
<false/>
<key>LSApplicationQueriesSchemes</key>
<array>
<string>cydia</string>
<string>anydesk</string>
<string>tvsqcustomer1</string>
<string>logmein</string>
<string>rdp</string>
<string>jump</string>
<string>prlclient</string>
<string>tuxclient</string>
<string>crd</string>
</array>
</dict>
</plist>
2 changes: 1 addition & 1 deletion example/ios/Podfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -1384,7 +1384,7 @@ SPEC CHECKSUMS:
React-logger: 7e7403a2b14c97f847d90763af76b84b152b6fce
React-Mapbuffer: 11029dcd47c5c9e057a4092ab9c2a8d10a496a33
react-native-config: 86038147314e2e6d10ea9972022aa171e6b1d4d8
react-native-malwarelytics: 9435d99f3a73dc5754c4d7f50417f387fae4db75
react-native-malwarelytics: 847ebaa9964fb1373d3915b62c6ccaa97b7ddb1a
React-nativeconfig: b0073a590774e8b35192fead188a36d1dca23dec
React-NativeModulesApple: df46ff3e3de5b842b30b4ca8a6caae6d7c8ab09f
React-perflogger: 3d31e0d1e8ad891e43a09ac70b7b17a79773003a
Expand Down
6 changes: 3 additions & 3 deletions example/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import * as React from 'react';

import { StyleSheet, View, Text, Appearance, NativeEventSubscription, SafeAreaView, Button, Platform, SectionList } from 'react-native';
import { EmulatorInfo, HttpProxyInfo, Malwarelytics, MalwarelyticsError, MalwarelyticsRaspListener, MalwarelyticsState, MalwarelyticsStateListener, RepackagingInfo, ScreenSharingInfo, SystemIntegrityInfo, TapjackingInfo, ActiveCallInfo, AppPresenceInfo } from 'react-native-malwarelytics';
import { loadMalwarelyticsConfig } from './Config';
import { loadMalwarelyticsConfigAsync } from './Config';

function AppLog(message: string) {
console.log(`${Platform.OS}: ${message}`)
Expand Down Expand Up @@ -113,7 +113,7 @@ class App extends React.Component<{}, AppState> implements MalwarelyticsStateLis
return
}
AppLog("Going to initialize..")
const result = await this.service.initialize(loadMalwarelyticsConfig())
const result = await this.service.initialize(await loadMalwarelyticsConfigAsync())
AppLog(`Module is now initialized with result ${result}`)
} catch (error) {
AppErr(`Exception while init': ${ErrorToString(error)}`)
Expand Down Expand Up @@ -192,6 +192,7 @@ class App extends React.Component<{}, AppState> implements MalwarelyticsStateLis
params.push({key: 'Screen Sharing', value: JSON.stringify(await rasp.getScreenSharingInfo())})
params.push({key: 'VPN', value: `${await rasp.isVpnActive()}`})
params.push({key: 'On Call', value: `${await rasp.isOnCall()}`})
params.push({key: 'App Presence', value: JSON.stringify(await rasp.getAppPresenceInfo())})

if (Platform.OS == 'ios') {
// iOS specific
Expand All @@ -210,7 +211,6 @@ class App extends React.Component<{}, AppState> implements MalwarelyticsStateLis
params.push({key: 'Tapjacking', value: JSON.stringify(await rasp.getTapjackingInfo())})
params.push({key: 'Bad Tapjacking App', value: `${await rasp.isBadTapjackingCapableAppPresent()}`})
params.push({key: 'Active Call', value: JSON.stringify(await rasp.getActiveCallInfo())})
params.push({key: 'App Presence', value: JSON.stringify(await rasp.getAppPresenceInfo())})
}
} catch (error) {
params.push({key: '💣 Exception', value: ErrorToString(error)})
Expand Down
42 changes: 39 additions & 3 deletions example/src/Config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,9 @@
// and limitations under the License.
//

import { MalwarelyticsConfig, MalwarelyticsServiceConfig, MalwarelyticsServiceEnvironment } from "react-native-malwarelytics"
import { MalwarelyticsAppleDetectableApp, MalwarelyticsConfig, MalwarelyticsServiceConfig, MalwarelyticsServiceEnvironment } from "react-native-malwarelytics"
import { Config } from "react-native-config"
import { Malwarelytics } from "react-native-malwarelytics"

const defaultConfig: MalwarelyticsConfig = {

Expand All @@ -40,7 +41,28 @@ const defaultConfig: MalwarelyticsConfig = {
// signaturePublicKey: "your-sign-pk-for-ios-app"
// },
rasp: {
repackage: { action: "NOTIFY" }
repackage: { action: "NOTIFY" },
appPresence: {
action: "NOTIFY",
apps: [] // filled in later
},
screenCapture: {
action: "HIDE",
overlay: {
type: "COLOR",
color: {
red: 128,
blue: 0,
green: 0,
alpha: 255
}
// Example of IMAGE overlay
// type: "IMAGE",
// image: {
// name: "" // The name of the file or image asset.
// }
}
}
}
},
android: {
Expand All @@ -63,12 +85,26 @@ const defaultConfig: MalwarelyticsConfig = {
}
}

/**
* Load default Malwarelytics config. Include constants loaded from the native SDK.
*/
export async function loadMalwarelyticsConfigAsync(): Promise<MalwarelyticsConfig> {
return Malwarelytics.sharedInstance.getKnownDetectableApps()
.catch(() => [] as MalwarelyticsAppleDetectableApp[])
.then((detectableApps) => {
return loadMalwarelyticsConfig(detectableApps)
})
}

/**
* Load default Malwarelytics config. If `.env` file contains service credentials, then mix
* the credentials with the default config.
*/
export function loadMalwarelyticsConfig(): MalwarelyticsConfig {
function loadMalwarelyticsConfig(detectableApps: MalwarelyticsAppleDetectableApp[]): MalwarelyticsConfig {
var config = {...defaultConfig}
if (config?.apple?.rasp?.appPresence) {
config!.apple!.rasp!.appPresence!.apps = detectableApps
}
const envConfig = loadEnvConfig()
if (envConfig?.environment != undefined) {
config.environment = envConfig.environment
Expand Down
47 changes: 47 additions & 0 deletions ios/Malwarelytics+RASP.swift
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,14 @@ extension Malwarelytics: AppProtectionRaspDelegate {
func onCallChanged(isOnCall: Bool) {
sendRaspEvent(RASPMessage(type: "ON_CALL", payload: isOnCall))
}

func installedAppsChanged(installedApps: [DetectableApp]) {
sendRaspEvent(RASPMessage(type: "APP_PRESENCE",
payload: AppPresenceInfo(
appleAppPresenceInfo: AppleAppPresenceInfo(installedApps: installedApps)
)
))
}

/// Send RASP event to JavaScript.
/// - Parameter payload: Message body.
Expand Down Expand Up @@ -101,6 +109,10 @@ extension Malwarelytics {
return rasp.isVpnActive
case "ON_CALL":
return rasp.isOnCall
case "APP_PRESENCE":
return AppPresenceInfo(
appleAppPresenceInfo: AppleAppPresenceInfo(installedApps: rasp.installedApps)
)
default:
throw ModuleError.invalidParam(paramName: "message")
}
Expand Down Expand Up @@ -141,3 +153,38 @@ fileprivate struct EmulatorInfo: Encodable {
let isEmulator: Bool
let detectedEmulatorType: String
}

fileprivate struct AppPresenceInfo: Encodable {
let appleAppPresenceInfo: AppleAppPresenceInfo?
}

fileprivate struct AppleAppPresenceInfo: Encodable {
let installedApps: [DetectableApp]
}

extension DetectableApp : Encodable {
enum CodingKeys: CodingKey {
case deeplinkProtocols
case name
case category
case tag
}

public func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(deeplinkProtocols, forKey: .deeplinkProtocols)
try container.encode(name, forKey: .name)
try container.encode(category, forKey: .category)
try container.encode(tag, forKey: .tag)
}
}

extension DetectableApp.Category : Encodable {
public func encode(to encoder: Encoder) throws {
var container = encoder.singleValueContainer()
switch self {
case .remoteDesktop:
try container.encode("REMOTE_DESKTOP")
}
}
}
1 change: 1 addition & 0 deletions ios/Malwarelytics-Bridge.m
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ @interface RCT_EXTERN_MODULE(Malwarelytics, NSObject)
RCT_EXTERN_METHOD(setClientId:(NSString*)clientId resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject)
RCT_EXTERN_METHOD(setDeviceId:(NSString*)deviceId resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject)
RCT_EXTERN_METHOD(getAvUserId:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject)
RCT_EXTERN_METHOD(getKnownDetectableApps:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject)

+ (BOOL) requiresMainQueueSetup
{
Expand Down
18 changes: 18 additions & 0 deletions ios/Malwarelytics.swift
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,24 @@ class Malwarelytics: RCTEventEmitter {
return try StateWithResult(state: self.state.asString, result: self.initResult?.asString).toJsObject()
}
}

@objc(getKnownDetectableApps:rejecter:)
func getKnownDetectableApps(_ resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) -> Void {
let allKnownApps: [DetectableApp] = [
.KnownApps.anyDesk,
.KnownApps.teamViewer,
.KnownApps.logMeIn,
.KnownApps.msRemoteDekstop,
.KnownApps.jumpDesktop,
.KnownApps.parallelsAccess,
.KnownApps.chromeRemoteDesktop,
]
do {
resolve(try allKnownApps.toJsObject())
} catch {
error.report(to: reject)
}
}

@objc(getSupportedEvents:rejecter:)
func getSupportedEvents(_ resolve: RCTPromiseResolveBlock, reject: RCTPromiseRejectBlock) -> Void {
Expand Down
Loading

0 comments on commit 4f797a8

Please sign in to comment.