UNPKG

react-native-malwarelytics

Version:

Malwarelytics for React Native protects your banking or fintech app from a broad range of mobile security threats with an industry-leading mobile threat intelligence solution.

503 lines (448 loc) 16.2 kB
// // Copyright 2023 Wultra s.r.o. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions // and limitations under the License. // import type { EmitterSubscription } from "react-native"; import type { EmulatorInfo } from "./model/rasp/EmulatorInfo"; import type { HttpProxyInfo } from "./model/rasp/HttpProxyInfo"; import type { RepackagingInfo } from "./model/rasp/RepackageInfo"; import type { ScreenSharingInfo } from "./model/rasp/ScreenSharingInfo"; import type { ScreenReaderInfo } from "./model/rasp/ScreenReaderInfo"; import type { SystemIntegrityInfo } from "./model/rasp/SystemIntegrityInfo"; import type { TapjackingInfo } from "./model/rasp/TapjackingInfo"; import type { ActiveCallInfo } from "./model/rasp/ActiveCallInfo"; import type { AppPresenceInfo } from "./model/rasp/AppPresenceInfo"; import type { DebuggerInfo } from "./model/rasp/DebuggerInfo"; import type { BiometryInfo } from "./model/rasp/BiometryInfo"; import type { EventHelper } from "./internal/EventHelper"; import type { RaspEvent, RaspEventType } from "./internal/RaspEvent"; import { MalwarelyticsModuleIfc, wrapNativeCall } from "./internal/MalwarelyticsModule"; import { Platform } from "react-native"; import { MalwarelyticsError } from "./MalwarelyticsError"; /** * Malwarelytics RASP module. */ export class MalwarelyticsRasp { constructor(eventHelper: EventHelper) { this.eventHelper = eventHelper this.module = eventHelper.module } /** * Instance of EventHelper shared with Malwarelytics class. */ private readonly eventHelper: EventHelper /** * Instance of native module interface. */ private readonly module: MalwarelyticsModuleIfc /** * Object representing a subscription to RASP events. */ private raspEventsSubscription: EmitterSubscription | undefined /** * Set listener for RASP events. * @param listener Listener implementation. */ async setRaspListener(listener: MalwarelyticsRaspListener): Promise<void> { this.raspEventsSubscription?.remove() this.raspEventsSubscription = await this.eventHelper.addListener('Malwarelytics.RASP', (data) => { //console.log(`${Platform.OS}: RASP event: ${JSON.stringify(data)}`) const m = data as RaspEvent switch (m.type) { // Apple + Android case "DEBUGGER": listener.debuggerDetected(m.payload as boolean); break case "REPACKAGED": listener.repackagingDetected(m.payload as RepackagingInfo) break case "SYSTEM_INTEGRITY": listener.systemIntegrityCompromised(m.payload as SystemIntegrityInfo) break case "HTTP_PROXY": listener.httpProxyDetected(m.payload as HttpProxyInfo) break case "SCREEN_SHARING": listener.screenSharingDetected(m.payload as ScreenSharingInfo) break case "EMULATOR": listener.emulatorDetected(m.payload as EmulatorInfo) break case "VPN": listener.vpnDetected(m.payload as boolean) break case "APP_PRESENCE": listener.appPresenceChangeDetected(m.payload as AppPresenceInfo) break; // Android specific case "SCREEN_READER": listener.screenReaderDetected(m.payload as ScreenReaderInfo) break case "TAPJACKING": listener.tapjackingDetected(m.payload as TapjackingInfo) break case "ADB_STATUS": listener.adbStatusDetected(m.payload as boolean) break case "ACTIVE_CALL": listener.activeCallDetected(m.payload as ActiveCallInfo) break; // Apple specific case "SCREENSHOT": listener.userScreenshotDetected() break case "REVERSE_TOOLS": listener.reverseEngineeringToolsDetected() break case "DEVICE_PASSCODE": listener.systemPasscodeConfigurationChanged(m.payload as boolean) break case "DEVICE_BIOMETRY": listener.systemBiometryConfigurationChanged(m.payload as boolean) break case "ON_CALL": listener.isOnCallChanged(m.payload as boolean) break default: console.warn(`${Platform.OS}: Unsupported RASP event ${m.type}`) break } }) } /** * Remove RASP listener previously set by `setRaspListener()` method. */ removeRaspListener() { this.raspEventsSubscription?.remove() this.raspEventsSubscription = undefined } // Android + Apple /** * Get information about Jailbreak or Root presence on the device. */ getSystemIntegrityInfo(): Promise<SystemIntegrityInfo> { return this.getRaspInfo("SYSTEM_INTEGRITY") } /** * Get information whether app is running in emulator. You can use `getEmulatorInfo()` method to get more details * about the emulator type. */ async isRunningInEmulator(): Promise<boolean> { return (await this.getEmulatorInfo()).isEmulator } /** * Get information whether debugger is connected. */ isDebuggerConnected(): Promise<boolean> { return this.getRaspInfo("DEBUGGER") } /** * Get detailed information about debugger detection. */ getDebuggerInfo(): Promise<DebuggerInfo> { return this.getRaspAndroidInfo("DEBUGGER_INFO") } /** * Get information about application repackaging. */ getRepackagingInfo(): Promise<RepackagingInfo> { return this.getRaspInfo("REPACKAGED") } /** * Get information about HTTP proxy configured on the system. */ getHttpProxyInfo(): Promise<HttpProxyInfo> { return this.getRaspInfo("HTTP_PROXY") } /** * Get information whether app is running in emulator. */ getEmulatorInfo(): Promise<EmulatorInfo> { return this.getRaspInfo("EMULATOR") } /** * Get information about active screen sharing or screen capturing. */ getScreenSharingInfo(): Promise<ScreenSharingInfo> { return this.getRaspInfo("SCREEN_SHARING") } /** * Get information about active VPN connection. */ isVpnActive(): Promise<boolean> { return this.getRaspInfo("VPN") } /** * Get information about the active phone call. */ isOnCall(): Promise<boolean> { return this.getRaspInfo("ON_CALL") } /** * Obtain information about app presence. */ getAppPresenceInfo(): Promise<AppPresenceInfo> { return this.getRaspInfo("APP_PRESENCE") } // Apple specific /** * Apple specific: Get information whether reverse engineering tools are present on the device. */ isReverseEngineeringToolsPresent(): Promise<boolean> { return this.getRaspAppleInfo("REVERSE_TOOLS") } /** * Apple specific: Get information about enabled passcode in the system (device lock) */ isSystemPasscodeEnabled(): Promise<boolean> { return this.getRaspAppleInfo("DEVICE_PASSCODE") } /** * Apple specific: Get information about biometry enrolled by the user in the system. */ isSystemBiometryEnabled(): Promise<boolean> { return this.getRaspAppleInfo("DEVICE_BIOMETRY") } // Android specific /** * Android specific: Get information about tapjacking. */ getTapjackingInfo(): Promise<TapjackingInfo> { return this.getRaspAndroidInfo("TAPJACKING") } /** * Android specific: Get information about connected ADB. */ getAdbStatus(): Promise<boolean> { return this.getRaspAndroidInfo("ADB_STATUS") } /** * Android specific: Check if system screen lock (PIN or pattern) is being used to prevent * unauthorized usage of the device by other people. It does not check if the device is currently locked. */ isScreenLockEnabled(): Promise<boolean> { return this.getRaspAndroidInfo("SCREEN_LOCK") } /** * Android specific: Check if Play Protect is enabled on the device. `undefined` value indicates that there was * a problem obtaining the information. */ isPlayProtectEnabled(): Promise<boolean | undefined> { return this.getRaspAndroidInfo("PLAY_PROTECT") } /** * Android specific: Get information about screen readers. */ getScreenReaderInfo(): Promise<ScreenReaderInfo> { return this.getRaspAndroidInfo("SCREEN_READER") } /** * Android specific: Check if any not allowed screen reader is enabled on the device. Allowed screen readers are configured * in `MalwarelyticsAndroidRaspScreenReadersConfig.allowedScreenReaders`. */ isNotAllowedScreenReaderEnabled(): Promise<boolean> { return this.getRaspAndroidInfo("NA_SCREEN_READER") } /** * Android specific: Check if there's a bad app that is able to create a system overlay. A bad app is one that * has a treat index same or higher than `MalwarelyticsAndroidRaspTapjackingConfig.blockSensitivity`. */ isBadTapjackingCapableAppPresent(): Promise<boolean> { return this.getRaspAndroidInfo("TAPJACKING_APP_PRESENT") } /** * Android specific: Check if developer options are enabled on the device. */ isDeveloperOptionsEnabled(): Promise<boolean> { return this.getRaspAndroidInfo("DEVELOPER_MODE") } /** * Android specific: Obtain information about biometry on the device. */ getBiometryInfo(): Promise<BiometryInfo> { return this.getRaspAndroidInfo("BIOMETRY") } /** * Android specific: Obtain information about active call. */ getActiveCallInfo(): Promise<ActiveCallInfo> { return this.getRaspAndroidInfo("ACTIVE_CALL") } // Private methods /** * Acquire typed information about RASP detection. * @param messageType RASP message to get. * @returns Value returned from native code. */ private async getRaspInfo<T>(messageType: RaspEventType): Promise<T> { return await wrapNativeCall(this.module, (module) => module.getRaspInfo(messageType)) as T } /** * Acquire typed information about RASP detection. This function fails if called on non-Apple platform. * @param messageType RASP message to get. * @returns Value returned from native code. */ private getRaspAppleInfo<T>(messageType: RaspEventType): Promise<T> { if (Platform.OS != "ios") { return Promise.reject(new MalwarelyticsError("METHOD_NOT_SUPPORTED", "This method is supported only on Apple platforms")) } return this.getRaspInfo(messageType) } /** * Acquire typed information about RASP detection. This function fails if called on non-Android platform. * @param messageType RASP message to get. * @returns Value returned from native code. */ private getRaspAndroidInfo<T>(messageType: RaspEventType): Promise<T> { if (Platform.OS != "android") { return Promise.reject(new MalwarelyticsError("METHOD_NOT_SUPPORTED", "This method is supported only on Android platform")) } return this.getRaspInfo(messageType) } } export interface MalwarelyticsRaspListener { // Android + Apple /** * Called when debuger is attached to the executable. * * Platforms: Apple, Android * * @param detected True is debugger is attached. */ debuggerDetected(detected: boolean): void /** * Called when repackage is detected. * * Platforms: Apple, Android * * @param info Information about repackaging. */ repackagingDetected(info: RepackagingInfo): void /** * Called when device is jailbroken or rooted. * * Platforms: Apple, Android * * @param info Information about compromited system integrity. */ systemIntegrityCompromised(info: SystemIntegrityInfo): void /** * Called when application is running in emulator environment. * * Platforms: Apple, Android * * @param info Information about emulator environment. */ emulatorDetected(info: EmulatorInfo): void /** * Called when HTTP proxy is detected. * * Platforms: Apple (limited), Android * * @param info Information about proxy configuration. */ httpProxyDetected(info: HttpProxyInfo): void /** * Called when screen sharing or capturing is detected. * * Platforms: Apple, Android * * @param info Information about screen sharing. */ screenSharingDetected(info: ScreenSharingInfo): void /** * Called when VPN status is changed * * Platforms: Apple, Android * @param active VPN status */ vpnDetected(active: boolean): void // Android specific /** * Called when application detects a change in enabled screen readers. * * Platforms: Android * * @param info Information about detected screen readers. */ screenReaderDetected(info: ScreenReaderInfo): void /** * Called when tapjacking is detected. * * Platforms: Android * @param info Information about detected tapjacking. */ tapjackingDetected(info: TapjackingInfo): void /** * Called when ADB status is changed. (TODO) * * Platforms: Android * * @param adbStatus ADB status changed */ adbStatusDetected(adbStatus: boolean): void /** * Call when active call detection changes are detected. * * Platforms: Android * * @param info Information about active call. */ activeCallDetected(info: ActiveCallInfo): void /** * Called when app presence changes. * * Platforms: Android * * @param info Information about app presence. */ appPresenceChangeDetected(info: AppPresenceInfo): void // Apple specific /** * Called after user took the screenshot. * * Platforms: Apple */ userScreenshotDetected(): void /** * Called when reverse engineering tools detected on the system. * * Platforms: Apple */ reverseEngineeringToolsDetected(): void /** * Called when device passcode configuration is changed. * * Platforms: Apple * * @param enabled Passcode is enabled or disabled. */ systemPasscodeConfigurationChanged(enabled: boolean): void /** * Called when device's biometry configuration is changed. * * Platforms: Apple * * @param enabled Biometry is enabled or disabled. */ systemBiometryConfigurationChanged(enabled: boolean): void /** * Called when the status of phone call is changed. * * Platforms: Apple * * @param isOnCall `true` if there's active call right now. */ isOnCallChanged(isOnCall: boolean): void }