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.

568 lines (508 loc) 18.7 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 { ScreenshotInfo } from "./model/rasp/ScreenshotInfo"; 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 { SpoofedLocationInfo } from "./model/rasp/SpoofedLocationInfo"; import type { ScreenRecordingDetectionInfo } from "./model/rasp/ScreenRecordingDetectionInfo"; 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"; import { CallDetectionInfo } from "./model/rasp/CallDetectionInfo"; import { AndroidAutoDetectionInfo } from "./model/rasp/AndroidAutoDetectionInfo"; /** * 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 "SCREENSHOT_ANDROID": listener.screenshotDetected(m.payload as ScreenshotInfo) 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; case "SCREEN_RECORDING": if (listener.onScreenRecordingDetected) { listener.onScreenRecordingDetected(m.payload as ScreenRecordingDetectionInfo) } break; // Apple specific case "SCREENSHOT_APPLE": 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 "CALL_DETECTION": listener.callDetectionInfoChanged(m.payload as CallDetectionInfo) 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") } /** * Apple specific: Get information the whole call detection information. * @returns `CallDetectionInfo` */ getCallDetectionInfo(): Promise<CallDetectionInfo> { return this.getRaspAppleInfo("CALL_DETECTION") } // 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") } /** * Android specific: Obtain information about spoofed location apps. */ getSpoofedLocationInfo(): Promise<SpoofedLocationInfo> { return this.getRaspAndroidInfo("SPOOFED_LOCATION_INFO") } /** * Android specific: Check if the screen is being recorded. */ isScreenBeingRecorded(): Promise<boolean> { return this.getRaspAndroidInfo("IS_SCREEN_RECORDING") } /** * Android specific: Get details about screen recording. */ getScreenRecordingDetection(): Promise<ScreenRecordingDetectionInfo> { return this.getRaspAndroidInfo("GET_SCREEN_RECORDING_DETECTION") } /** * Android specific: Get information about Android Auto connection. */ getAndroidAutoDetection(): Promise<AndroidAutoDetectionInfo> { return this.getRaspAndroidInfo("ANDROID_AUTO_DETECTION"); } // 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 a screenshot is taken and when the screenshots are not blocked. * * Platform: Android * * @param info Information about detected screenshot. */ screenshotDetected(info: ScreenshotInfo): 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 /** * Called when a screen recording is detected. * * Platforms: Android 15+ * * @param info Information about detected screen recording. On */ onScreenRecordingDetected?(info: ScreenRecordingDetectionInfo): 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 callDetectionInfo `CallDetectionInfo` the whole call detection data. */ callDetectionInfoChanged(callDetectionInfo: CallDetectionInfo): void }