import type { OptionalProviderLoadFailures } from "../application/OptionalObservationProviders.js"; import { locateWiresharkTool, selectWiresharkTool, } from "../pcap/PcapConfiguration.js"; import type { AnalysisSettingsReader } from "../domain/analysisSettings.js"; import { CapabilityScope } from "../application/CapabilityScope.js"; import type { CapabilityAvailabilityFacts } from "../application/CapabilityAvailability.js"; import { CAPABILITY_DECLARATIONS, type CapabilityDeclarationId, } from "./capabilityDeclarations.js"; import { isAbsolute } from "node:path"; import { createGpuCodeObjectsService } from "./gpu.js"; import { createPacketCaptureService, tsharkHostSupported } from "./captures.js"; import { createSignalRecordingService, sigrokHostSupported, } from "./signals.js"; import { wasmArtifactModule } from "./wasm.js"; import { apkSignatureModule } from "./apkSignature.js"; import { evmInterfaceModule, evmHostSupported, evmLimiterAvailability, } from "./evm.js"; import { createSqliteDatabaseService } from "./sqlite.js"; import { createDesktopApplicationService } from "./desktop.js"; import { DesktopBuildComputers } from "../application/desktop/DesktopBuildComputers.js"; import { createGoBinaryService } from "./go.js"; import { createRustBinaryService } from "./rust.js"; import { createBeamService } from "./erlang.js"; import { createV8CodeCacheService } from "./v8CodeCache.js"; import { createModelArtifactService } from "./model.js"; import { createIl2cppMetadataService } from "./il2cpp.js"; import { createElectronFuseService } from "./electronFuses.js"; import { createPythonPycService } from "./pythonPyc.js"; import { createWebNetworkCaptureService } from "./webNetworkCaptures.js"; import { createWebSourceLocationService } from "./webSourceLocations.js"; import { createWebModuleTraceService } from "./webModules.js"; import { createWebRuntimeService } from "./webRuntime.js"; import { createJavaScriptRecoveryProvider, wakaruHostSupported, } from "./javascriptRecovery.js"; import { ArtifactResourceScope } from "../artifacts/ArtifactResourceScope.js"; import { createReverseEngineeringService } from "./reverseEngineering.js"; import type { ReverseEngineeringService, CommandProviderLaunchFailures, } from "../application/reverse/ReverseEngineeringService.js"; import { FirmwareAnalysisService } from "../application/firmware/FirmwareAnalysisService.js"; import { createFirmwareAnalysisProvider, firmwareHostSupported, } from "./firmware.js"; import { createAndroidAnalysisProvider } from "./android.js"; import { createPcapAnalysisProvider } from "./pcap.js"; import { createFlutterBuildAnalysisProvider } from "./flutter.js"; import { createApktoolResourceAnalysisProvider } from "./apktool.js"; import { createJebAnalysisProvider } from "./jeb.js"; import { createAdbDeviceAnalysisProvider } from "./adb.js"; import { createFridaInstrumentationProvider } from "./frida.js"; import { createCutterBridgeService } from "./cutter.js"; import { createGdbSessionManager, createRizinDebugSessionManager, } from "./debuggerSessions.js"; import { persistentDebuggerHostSupported } from "../process/PersistentDebuggerHost.js"; import { pcapCaptureHostSupported } from "../domain/pcap/pcapAnalysis.js"; import { createPeLoadConfigService, createBinaryHardeningService, createPeCertificatesService, createPeImportsService, createNativeExportsService, createPeResourcesService, createWindowsShortcutService, createRecordedCrashService, createBinaryLayoutService, pwntoolsHostSupported, createDevicetreeService, createElfNotesService, createNativeInitializersService, createElfUnwindRangesService, createAppleIpsService, createKernelModuleService, createKernelBuildConfigService, } from "./binaryDiagnostics.js"; import type { PacketCaptureService } from "../application/captures/PacketCaptureService.js"; import type { SignalRecordingService } from "../application/signals/SignalRecordingService.js"; import type { WasmArtifactService } from "../application/wasm/WasmArtifactService.js"; import type { ApkSignatureService } from "../application/apkSignature/ApkSignatureService.js"; import type { EvmInterfaceService } from "../application/evm/EvmInterfaceService.js"; import type { SqliteDatabaseService } from "../application/sqlite/SqliteDatabaseService.js"; import type { Il2cppMetadataService } from "../application/il2cpp/Il2cppMetadataService.js"; import type { GoBinaryService } from "../application/go/GoBinaryService.js"; import type { ElectronFuseService } from "../application/electron/ElectronFuseService.js"; import type { RustBinaryService } from "../application/rust/RustBinaryService.js"; import type { PythonPycService } from "../application/python/PythonPycService.js"; import type { BeamService } from "../application/erlang/BeamService.js"; import type { V8CodeCacheService } from "../application/v8CodeCache/V8CodeCacheService.js"; import type { RecordedCrashService } from "../application/binaryDiagnostics/RecordedCrashService.js"; import type { BinaryLayoutService } from "../application/binaryDiagnostics/BinaryLayoutService.js"; import type { BrowserObservationPort } from "../application/BrowserObservationPort.js"; import type { BrowserScenarioCapturePort } from "../application/BrowserScenarioCapturePort.js"; import type { ElectronActiveObservationPort } from "../application/javascript/ElectronActiveObservationPort.js"; import type { ElectronObservationPort } from "../application/javascript/ElectronObservationPort.js"; import type { JavaScriptRuntimeObservationPort } from "../application/javascript/JavaScriptRuntimeObservationPort.js"; import type { FridaInstrumentationPort } from "../application/frida/FridaInstrumentationPort.js"; import type { WebModuleTraceService } from "../application/WebModuleTraceService.js"; import type { WebSourceLocationService } from "../application/WebSourceLocationService.js"; import type { WebRuntimeService } from "../application/WebRuntimeService.js"; import type { WebNetworkCaptureService } from "../application/WebNetworkCaptureService.js"; import type { JavaScriptRecoveryPort } from "../application/javascript/JavaScriptRecoveryPort.js"; import type { FirmwareAnalysisPort } from "../application/firmware/FirmwareAnalysisPort.js"; import type { AndroidAnalysisPort } from "../application/android/AndroidAnalysisPort.js"; import type { ApktoolResourceAnalysisPort } from "../application/apktool/ApktoolResourceAnalysisPort.js"; import type { PcapAnalysisPort } from "../application/pcap/PcapAnalysisPort.js"; import type { FlutterBuildAnalysisPort } from "../application/flutter/FlutterBuildAnalysisPort.js"; import type { JebAnalysisPort } from "../application/jeb/JebAnalysisPort.js"; import type { AdbDeviceAnalysisPort } from "../application/adb/AdbDeviceAnalysisPort.js"; import type { BuildSettle } from "../application/desktop/DesktopBuildSettle.js"; import type { BuildComputerDefaults } from "../desktop/BuildComputerEnvironment.js"; /** Typed injection inputs shared by a capability host, independent of transport. */ export interface CapabilityServiceOptions { readonly packetCapture?: PacketCaptureService; readonly signalRecording?: SignalRecordingService; readonly wasmArtifact?: WasmArtifactService; readonly apkSignature?: ApkSignatureService; readonly evmInterface?: EvmInterfaceService; readonly sqliteDatabase?: SqliteDatabaseService; readonly binaryLayout?: BinaryLayoutService; readonly goBinary?: GoBinaryService; readonly il2cppMetadata?: Il2cppMetadataService; readonly rustBinary?: RustBinaryService; readonly pythonPyc?: PythonPycService; readonly erlangBeam?: BeamService; readonly v8CodeCache?: V8CodeCacheService; readonly electronFuses?: ElectronFuseService; readonly recordedCrash?: RecordedCrashService; readonly firmwareAnalysis?: FirmwareAnalysisPort; readonly javascriptRecovery?: JavaScriptRecoveryPort; readonly webModuleTrace?: WebModuleTraceService; readonly webSourceLocation?: WebSourceLocationService; readonly webRuntime?: WebRuntimeService; readonly webNetworkCapture?: WebNetworkCaptureService; readonly androidAnalysis?: AndroidAnalysisPort; readonly apktoolAnalysis?: ApktoolResourceAnalysisPort; readonly pcapAnalysis?: PcapAnalysisPort; readonly flutterAnalysis?: FlutterBuildAnalysisPort; readonly jebAnalysis?: JebAnalysisPort; readonly adbDeviceAnalysis?: AdbDeviceAnalysisPort; readonly browserObservation?: BrowserObservationPort; readonly browserScenarioCapture?: BrowserScenarioCapturePort; readonly electronObservation?: ElectronObservationPort; readonly optionalObservationProviderOwner?: "server" | "caller"; readonly optionalProviderLoadFailures?: OptionalProviderLoadFailures; readonly electronActiveObservation?: ElectronActiveObservationPort; readonly javascriptRuntimeObservation?: JavaScriptRuntimeObservationPort; readonly fridaInstrumentation?: FridaInstrumentationPort; /** How build computers read an app until its files settle; the shipped timing on real time unless a test shortens or clocks it. */ readonly desktopBuildSettle?: BuildSettle; /** Where build computers look for unset settings; the host's PATH and REA cache unless a test isolates them. */ readonly desktopBuildDefaults?: BuildComputerDefaults; readonly reverseEngineering?: ReverseEngineeringService; } const tsharkLocatable = (environment: Readonly): boolean => { try { return ( locateWiresharkTool( selectWiresharkTool(environment, "tshark"), environment, ) !== undefined ); } catch { // A relative REA_TSHARK_COMMAND is configuration the tool reports at execution. return false; } }; export interface CapabilityHostContext { readonly environment: Readonly; readonly providerEnvironment: Readonly; readonly settings: AnalysisSettingsReader; readonly options: CapabilityServiceOptions; readonly platform: NodeJS.Platform; readonly arch: string; } interface CapabilityInstance { readonly value: Value; readonly close: () => Promise; readonly ownership: "host" | "caller"; readonly availability?: CapabilityAvailabilityFacts; } const capability = ( create: (context: CapabilityHostContext) => Value, close: (value: Value) => Promise, observe?: ( context: CapabilityHostContext, value: Value, ) => CapabilityAvailabilityFacts, ownership: (context: CapabilityHostContext) => "host" | "caller" = () => "host", ) => (context: CapabilityHostContext): CapabilityInstance => { const value = create(context); return { value, close: () => close(value), ownership: ownership(context), ...(observe === undefined ? {} : { availability: observe(context, value) }), }; }; const service = }>( create: (context: CapabilityHostContext) => Value, observe?: ( context: CapabilityHostContext, value: Value, ) => CapabilityAvailabilityFacts, ) => capability(create, (value) => value.close(), observe); const outsideHost = (injected: unknown, supported: boolean): boolean => injected === undefined && !supported; const configuredPwntools = (context: CapabilityHostContext): boolean => pwntoolsHostSupported(context.platform, context.arch) && isAbsolute(context.environment.REA_PWNTOOLS_PYTHON ?? ""); const sharedObservationOwnership = (context: CapabilityHostContext) => context.options.optionalObservationProviderOwner === "caller" ? "caller" : "host"; const closeObservation = (value: { close?: () => Promise } | undefined) => value?.close?.() ?? Promise.resolve(); /** Each entry binds construction, ownership and observed availability once. */ const modules = { wasmArtifact: service( ({ environment, options, settings }) => wasmArtifactModule.create(environment, options.wasmArtifact, settings), ({ environment, options }) => ({ optionalFeatures: { wasmArtifactEnabled: options.wasmArtifact !== undefined || isAbsolute(environment.REA_WABT_BIN_DIRECTORY ?? ""), }, }), ), apkSignature: service(({ environment, options }) => apkSignatureModule.create(environment, options.apkSignature), ), evmInterface: service( ({ environment, options }) => options.evmInterface ?? evmInterfaceModule.create(environment), ({ environment, options, platform, arch }) => ({ hostUnsupported: { evmInterface: outsideHost( options.evmInterface, evmHostSupported(platform, arch), ), }, optionalFeatures: { evmInterfaceEnabled: options.evmInterface !== undefined || evmHostSupported(platform, arch), }, ...(options.evmInterface !== undefined || !evmHostSupported(platform, arch) ? {} : { probes: { evmLimiterAvailability: (signal: AbortSignal) => evmLimiterAvailability(environment, { signal }), }, }), }), ), sqliteDatabase: service( ({ environment, options }) => options.sqliteDatabase ?? createSqliteDatabaseService(environment), ), binaryLayout: service( ({ environment, options }) => options.binaryLayout ?? createBinaryLayoutService(environment), (context) => ({ hostUnsupported: { binaryLayout: outsideHost( context.options.binaryLayout, pwntoolsHostSupported(context.platform, context.arch), ), }, optionalFeatures: { binaryLayoutEnabled: context.options.binaryLayout !== undefined || configuredPwntools(context), }, }), ), recordedCrash: service( ({ environment, options }) => options.recordedCrash ?? createRecordedCrashService(environment), (context) => ({ hostUnsupported: { recordedCrash: outsideHost( context.options.recordedCrash, pwntoolsHostSupported(context.platform, context.arch), ), }, optionalFeatures: { recordedCrashEnabled: context.options.recordedCrash !== undefined || configuredPwntools(context), }, }), ), webSourceLocation: service( ({ environment, options }) => options.webSourceLocation ?? createWebSourceLocationService(environment), ), webModuleTrace: service( ({ environment, options }) => options.webModuleTrace ?? createWebModuleTraceService(environment), ({ environment, options }) => ({ optionalFeatures: { webModuleResolutionEnabled: options.webModuleTrace !== undefined || isAbsolute(environment.REA_BROWSER_EXECUTABLE ?? ""), }, }), ), desktopApplication: service(() => createDesktopApplicationService()), desktopBuildComputers: capability( ({ providerEnvironment, options }) => new DesktopBuildComputers( providerEnvironment, options.desktopBuildSettle, options.desktopBuildDefaults, ), (value) => value.closeAll(), ), goBinary: service( ({ options }) => options.goBinary ?? createGoBinaryService(), ), rustBinary: service( ({ options }) => options.rustBinary ?? createRustBinaryService(), ), erlangBeam: service( ({ options }) => options.erlangBeam ?? createBeamService(), ), v8CodeCache: service( ({ options }) => options.v8CodeCache ?? createV8CodeCacheService(), ), modelArtifact: service(() => createModelArtifactService()), gpuCodeObjects: service(() => createGpuCodeObjectsService()), il2cppMetadata: service( ({ options }) => options.il2cppMetadata ?? createIl2cppMetadataService(), ), peResources: service(() => createPeResourcesService()), nativeExports: service(() => createNativeExportsService()), devicetree: service(() => createDevicetreeService()), elfNotes: service(() => createElfNotesService()), nativeInitializers: service(() => createNativeInitializersService()), elfUnwindRanges: service(() => createElfUnwindRangesService()), appleIps: service(() => createAppleIpsService()), kernelModule: service(() => createKernelModuleService()), kernelBuildConfig: service(() => createKernelBuildConfigService()), peLoadConfig: service(() => createPeLoadConfigService()), binaryHardening: service(() => createBinaryHardeningService()), peCertificates: service(() => createPeCertificatesService()), peImports: service(() => createPeImportsService()), windowsShortcut: service(() => createWindowsShortcutService()), electronFuses: service( ({ options }) => options.electronFuses ?? createElectronFuseService(), ), pythonPyc: service( ({ options }) => options.pythonPyc ?? createPythonPycService(), ), webNetworkCapture: service( ({ environment, options, settings }) => options.webNetworkCapture ?? createWebNetworkCaptureService(environment, settings), ), javascriptRecovery: capability( ({ environment, options }) => options.javascriptRecovery ?? createJavaScriptRecoveryProvider(environment), (value) => value.close?.() ?? Promise.resolve(), ({ environment, options, platform, arch }) => ({ hostUnsupported: { javascriptRecovery: outsideHost( options.javascriptRecovery, wakaruHostSupported(platform, arch), ), }, optionalFeatures: { javascriptRecoveryEnabled: options.javascriptRecovery !== undefined || (wakaruHostSupported(platform, arch) && isAbsolute(environment.REA_WAKARU_COMMAND ?? "")), }, }), ), artifactResources: service(() => new ArtifactResourceScope()), reverseEngineering: service( ({ providerEnvironment, options }) => options.reverseEngineering ?? createReverseEngineeringService(providerEnvironment), (_context, value) => ({ observeCommandProviderFailures: () => value.observedLaunchFailures(), }), ), firmwareAnalysis: service( ({ environment, options }) => new FirmwareAnalysisService( options.firmwareAnalysis ?? createFirmwareAnalysisProvider(environment), ), ({ environment, options, platform }) => ({ hostUnsupported: { firmwareInspection: outsideHost( options.firmwareAnalysis, firmwareHostSupported(platform), ), firmwareExtraction: outsideHost( options.firmwareAnalysis, firmwareHostSupported(platform), ), }, optionalFeatures: { firmwareInspectionEnabled: options.firmwareAnalysis !== undefined || (firmwareHostSupported(platform) && isAbsolute(environment.REA_BINWALK_COMMAND ?? "")), firmwareExtractionEnabled: options.firmwareAnalysis !== undefined || (firmwareHostSupported(platform) && isAbsolute(environment.REA_UNBLOB_COMMAND ?? "")), }, }), ), gdbSessions: capability( ({ providerEnvironment }) => createGdbSessionManager(providerEnvironment), (value) => value.closeAll(), ({ platform }) => ({ hostUnsupported: { persistentDebugger: !persistentDebuggerHostSupported(platform), }, }), ), rizinDebugSessions: capability( ({ providerEnvironment }) => createRizinDebugSessionManager(providerEnvironment), (value) => value.closeAll(), ), android: service( ({ environment, options, settings }) => options.androidAnalysis ?? createAndroidAnalysisProvider(environment, undefined, settings), (_context, value) => ({ probes: { androidAnalysisAvailability: (signal: AbortSignal) => value.inspectAvailability(signal), }, }), ), pcap: service( ({ environment, options }) => options.pcapAnalysis ?? createPcapAnalysisProvider(environment), ({ options, platform }) => ({ hostUnsupported: { pcapCapture: outsideHost( options.pcapAnalysis, pcapCaptureHostSupported(platform), ), }, }), ), flutter: service( ({ options }) => options.flutterAnalysis ?? createFlutterBuildAnalysisProvider(), ), fridaInstrumentation: capability( ({ options }) => options.fridaInstrumentation ?? createFridaInstrumentationProvider(), (value) => value.closeAll(), ({ options }, value) => ({ observeProviderLoadFailures: () => { const failure = options.optionalProviderLoadFailures?.fridaInstrumentation ?? value.availabilityFailure?.(); return failure === undefined ? {} : { fridaInstrumentation: failure }; }, }), ), packetCapture: service( ({ environment, options }) => options.packetCapture ?? createPacketCaptureService(environment), ({ environment, options, platform }) => ({ hostUnsupported: { packetCapture: outsideHost( options.packetCapture, tsharkHostSupported(platform), ), }, optionalFeatures: { packetCaptureEnabled: options.packetCapture !== undefined || (tsharkHostSupported(platform) && tsharkLocatable(environment)), }, }), ), signalRecording: service( ({ environment, options }) => options.signalRecording ?? createSignalRecordingService(environment), ({ environment, options, platform }) => ({ hostUnsupported: { signalRecording: outsideHost( options.signalRecording, sigrokHostSupported(platform), ), }, optionalFeatures: { signalRecordingEnabled: options.signalRecording !== undefined || (sigrokHostSupported(platform) && isAbsolute(environment.REA_SIGROK_COMMAND ?? "")), }, }), ), webRuntime: capability( ({ options }) => options.webRuntime ?? createWebRuntimeService(), (value) => value.provider.close?.() ?? Promise.resolve(), ), apktool: service( ({ environment, options }) => options.apktoolAnalysis ?? createApktoolResourceAnalysisProvider(environment), (_context, value) => ({ probes: { apktoolAnalysisAvailability: (signal: AbortSignal) => value.inspectAvailability(signal), }, }), ), jeb: service( ({ environment, options }) => options.jebAnalysis ?? createJebAnalysisProvider(environment), (_context, value) => ({ probes: { jebAnalysisAvailability: (signal: AbortSignal) => value.inspectAvailability(signal), }, }), ), adbDevice: service( ({ environment, options }) => options.adbDeviceAnalysis ?? createAdbDeviceAnalysisProvider(environment), (_context, value) => ({ probes: { adbAnalysisAvailability: (signal: AbortSignal) => value.inspectAvailability(signal), }, }), ), cutter: service(({ providerEnvironment }) => createCutterBridgeService(providerEnvironment), ), browserObservation: capability( ({ options }) => options.browserObservation, closeObservation, ({ options }) => ({ optionalFeatures: { browserObservationEnabled: options.browserObservation !== undefined, }, }), sharedObservationOwnership, ), browserScenarioCapture: capability( ({ options }) => options.browserScenarioCapture, closeObservation, ({ options }) => ({ optionalFeatures: { browserScenarioEnabled: options.browserScenarioCapture !== undefined, }, }), sharedObservationOwnership, ), electronObservation: capability( ({ options }) => options.electronObservation, closeObservation, ({ options }) => ({ optionalFeatures: { electronObservationEnabled: options.electronObservation !== undefined, }, }), sharedObservationOwnership, ), electronActiveObservation: capability( ({ options }) => options.electronActiveObservation, closeObservation, ({ options }) => ({ optionalFeatures: { electronAutomationEnabled: options.electronActiveObservation !== undefined, }, }), sharedObservationOwnership, ), javascriptRuntimeObservation: capability( ({ options }) => options.javascriptRuntimeObservation, closeObservation, ({ options }) => ({ optionalFeatures: { v8InspectorObservationEnabled: options.javascriptRuntimeObservation !== undefined, }, }), sharedObservationOwnership, ), } satisfies Partial< Record< CapabilityDeclarationId, (context: CapabilityHostContext) => CapabilityInstance > >; export type CapabilityServices = { readonly [Key in keyof typeof modules]: ReturnType< (typeof modules)[Key] >["value"]; }; /** Compose every service and its cleanup together, retaining precise typed ports. */ export const createCapabilityServices = (context: CapabilityHostContext) => { const scope = new CapabilityScope(); const hostUnsupported: NonNullable< CapabilityAvailabilityFacts["hostUnsupported"] > = {}; const optionalFeatures: NonNullable< CapabilityAvailabilityFacts["optionalFeatures"] > = {}; const probes: NonNullable = {}; const failureObservers: (() => OptionalProviderLoadFailures)[] = []; const commandFailureObservers: (() => CommandProviderLaunchFailures)[] = []; const availability = { hostUnsupported, optionalFeatures }; // This is a key-preserving map of a source-owned typed declaration, not an // external value parser or a generic operation dispatcher. const services = Object.fromEntries( Object.entries(modules).map(([id, create]) => { const instance = create(context); const declaration = CAPABILITY_DECLARATIONS[id as keyof typeof modules]; if (declaration === undefined) throw new Error(`Undeclared capability ${id}`); Object.assign( availability.hostUnsupported, instance.availability?.hostUnsupported, ); Object.assign( availability.optionalFeatures, instance.availability?.optionalFeatures, ); Object.assign(probes, instance.availability?.probes); const observeFailure = instance.availability?.observeProviderLoadFailures; if (observeFailure !== undefined) failureObservers.push(observeFailure); const observeCommandFailure = instance.availability?.observeCommandProviderFailures; if (observeCommandFailure !== undefined) commandFailureObservers.push(observeCommandFailure); return [ id, instance.ownership === "host" ? scope.own(id, instance.value, instance.close) : instance.value, ]; }), ) as CapabilityServices; return { services, availability, probes, observeProviderLoadFailures: (): OptionalProviderLoadFailures => Object.assign({}, ...failureObservers.map((observe) => observe())), observeCommandProviderFailures: (): CommandProviderLaunchFailures => Object.assign({}, ...commandFailureObservers.map((observe) => observe())), close: () => scope.close(), }; };