rea-agents 4.0.0 → 4.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +93 -30
- package/bridge/ghidra/ReaGhidraBridge.java +358 -15
- package/bridge/ghidra/ReaGhidraExtensions.java +113 -0
- package/bridge/ghidra/ReaGhidraPrepareCom.java +32 -0
- package/dist/android/AndroidTargetSnapshot.js +34 -0
- package/dist/android/JadxAnalysis.js +191 -0
- package/dist/android/JadxConfiguration.js +41 -0
- package/dist/android/JadxMcpTransport.js +145 -0
- package/dist/android/JadxProtocol.js +101 -0
- package/dist/android/JadxProvider.js +170 -0
- package/dist/android/JadxRelease.js +19 -0
- package/dist/android/JadxSession.js +97 -0
- package/dist/application/AnalysisContextQueries.js +2 -1
- package/dist/application/AnalysisProviderEvaluation.js +5 -3
- package/dist/application/AnalysisProviderRegistry.js +2 -1
- package/dist/application/AnalysisSnapshotCache.js +4 -1
- package/dist/application/AnalysisSnapshotFiles.js +1 -1
- package/dist/application/AndroidAnalysisPort.js +1 -0
- package/dist/application/AndroidAnalysisService.js +37 -0
- package/dist/application/ArtifactExtraction.js +7 -4
- package/dist/application/ArtifactGraphConstruction.js +10 -17
- package/dist/application/ArtifactInventory/scanCanonical.js +8 -7
- package/dist/application/BinarySession.js +16 -3
- package/dist/application/BinarySessionExecution.js +4 -1
- package/dist/application/BinarySessionOpen.js +3 -2
- package/dist/application/BinarySessionRecords.js +9 -3
- package/dist/application/BinaryTargetResolver.js +25 -6
- package/dist/application/BrowserObservationService.js +2 -1
- package/dist/application/BrowserScenarioCaptureService.js +2 -1
- package/dist/application/BrowserScenarioEvidence.js +2 -2
- package/dist/application/CallPathTracing.js +2 -1
- package/dist/application/CapabilityInventory.js +18 -0
- package/dist/application/ClientConfigPath.js +3 -1
- package/dist/application/ClientConfigurationDocument.js +11 -1
- package/dist/application/ClientRegistrationStatus.js +8 -1
- package/dist/application/CompositeProvider.js +1 -1
- package/dist/application/DirectAnalysis.js +23 -7
- package/dist/application/DirectAnalysisStatus.js +3 -3
- package/dist/application/Doctor.js +48 -8
- package/dist/application/DoctorDiagnostics.js +7 -5
- package/dist/application/DoctorScope.js +1 -1
- package/dist/application/ElectronActiveEvidence.js +2 -2
- package/dist/application/ElectronActiveObservationService.js +2 -1
- package/dist/application/ElectronBoundaryAnalysis.js +1 -1
- package/dist/application/ElectronObservationService.js +2 -1
- package/dist/application/EnhancedLiteralTracing.js +2 -1
- package/dist/application/EnhancedToolTypes.js +2 -1
- package/dist/application/EnhancedTools.js +3 -1
- package/dist/application/EvidenceBundleCommands.js +2 -1
- package/dist/application/EvidenceBundleFiles.js +1 -1
- package/dist/application/EvidenceInputResolver.js +30 -0
- package/dist/application/EvidenceLedger.js +2 -1
- package/dist/application/FirmwareAnalysisPort.js +1 -0
- package/dist/application/FirmwareAnalysisService.js +35 -0
- package/dist/application/FunctionAnalysisEvidence.js +32 -0
- package/dist/application/InventoryProjectionEvidence.js +2 -1
- package/dist/application/JavaScriptApplicationService.js +50 -3
- package/dist/application/JavaScriptApplicationWorkflowService.js +2 -1
- package/dist/application/JavaScriptArtifactAnalysis.js +4 -2
- package/dist/application/JavaScriptArtifactFiles.js +4 -2
- package/dist/application/JavaScriptArtifactPathResolution.js +6 -2
- package/dist/application/JavaScriptJsonModules.js +3 -2
- package/dist/application/JavaScriptModuleRelationships.js +4 -2
- package/dist/application/JavaScriptRuntimeObservationService.js +2 -1
- package/dist/application/JavaScriptRuntimeReconciliationService.js +2 -1
- package/dist/application/JavaScriptSemanticGraphProjection.js +1 -1
- package/dist/application/JavaScriptSemanticTraceService.js +2 -1
- package/dist/application/JsonFiles.js +1 -1
- package/dist/application/LazyAnalysisProvider.js +3 -1
- package/dist/application/LinuxHopper.js +12 -4
- package/dist/application/MacHopper.js +20 -7
- package/dist/application/ManagedApplicationGraphService.js +2 -1
- package/dist/application/ManagedMemberComparisonService.js +3 -1
- package/dist/application/ManagedNativeVerificationService.js +2 -1
- package/dist/application/ManagedReconstructionService.js +2 -1
- package/dist/application/NativeCallRoutes.js +1 -1
- package/dist/application/NativeDispatchMetadataInspection.js +1 -1
- package/dist/application/NativeUiActionTrace.js +2 -1
- package/dist/application/NativeValueTrace.js +1 -1
- package/dist/application/ProcessCaptureError.js +1 -1
- package/dist/application/ProcessCaptureJournal.js +3 -1
- package/dist/application/ProcessCaptureLifecycle.js +6 -3
- package/dist/application/ProcessCli.js +9 -5
- package/dist/application/ProcessEvidence.js +2 -6
- package/dist/application/ProcessHarness.js +3 -2
- package/dist/application/ProcessNormalization.js +12 -5
- package/dist/application/ProcessSampling.js +3 -1
- package/dist/application/ReconstructionCoverageService.js +2 -1
- package/dist/application/ReconstructionObligationLedgerService.js +2 -1
- package/dist/application/ReferenceSourceImportEntries.js +2 -0
- package/dist/application/ReferenceSourceImportPolicy.js +4 -2
- package/dist/application/ReferenceSourceVcsAdapter.js +35 -4
- package/dist/application/SessionProviderRouter.js +4 -1
- package/dist/application/Setup.js +1 -128
- package/dist/application/SetupClientConfiguration.js +17 -6
- package/dist/application/SetupHost.js +111 -0
- package/dist/application/SetupRegistrationEnvironment.js +1 -2
- package/dist/application/SetupSkill.js +9 -3
- package/dist/application/SetupTypes.js +17 -0
- package/dist/application/StoreFileLock.js +7 -2
- package/dist/application/SupportedClients.js +8 -0
- package/dist/application/Uninstall.js +9 -4
- package/dist/application/UpdateRuntime.js +5 -2
- package/dist/application/WindowsCapabilities.js +7 -7
- package/dist/application/runtime.js +3 -1
- package/dist/artifacts/ArtifactProvider.js +3 -1
- package/dist/artifacts/DirectoryArtifactReader.js +2 -0
- package/dist/artifacts/NativeDmgArtifactReader.js +10 -3
- package/dist/artifacts/NibArchive.js +4 -2
- package/dist/artifacts/SafeOutputTree.js +26 -10
- package/dist/artifacts/ZipArtifactReader.js +1 -0
- package/dist/browser/BrowserProcessStartup.js +3 -1
- package/dist/browser/CdpAuthorizedMainFrame.js +1 -1
- package/dist/browser/CdpBrowserProvider.js +4 -1
- package/dist/browser/CdpCaptureDocuments.js +3 -1
- package/dist/browser/CdpCaptureEventHandlers.js +6 -2
- package/dist/browser/CdpCaptureStorageFingerprints.js +1 -1
- package/dist/browser/CdpCaptureValues.js +6 -2
- package/dist/browser/CdpConnection.js +8 -6
- package/dist/browser/CdpElectronInspection.js +3 -3
- package/dist/browser/CdpElectronProvider.js +4 -1
- package/dist/browser/CdpElectronScripts.js +2 -2
- package/dist/browser/CdpEndpoint.js +22 -10
- package/dist/browser/CdpObservationSession.js +2 -1
- package/dist/browser/CdpOptionalCommand.js +1 -1
- package/dist/browser/CdpPageCapture.js +2 -1
- package/dist/browser/CdpSafeMetadata.js +53 -8
- package/dist/browser/CdpScreenshot.js +1 -1
- package/dist/browser/CdpTargetSession.js +8 -5
- package/dist/browser/CdpWebMcpDiscovery.js +1 -1
- package/dist/browser/ElectronFileScope.js +9 -3
- package/dist/browser/JavaScriptRuntimeScope.js +3 -1
- package/dist/browser/PlaywrightBrowserScenarioProvider.js +3 -1
- package/dist/browser/PlaywrightElectronActiveActions.js +1 -1
- package/dist/browser/PlaywrightElectronActiveProvider.js +6 -2
- package/dist/browser/PlaywrightExecutionBoundary.js +1 -1
- package/dist/browser/PlaywrightScenarioArtifacts.js +10 -5
- package/dist/browser/PlaywrightScenarioBrowser.js +3 -1
- package/dist/browser/PlaywrightScenarioEvents.js +6 -2
- package/dist/browser/PlaywrightScenarioSession.js +5 -2
- package/dist/browser/V8InspectorEndpoint.js +2 -1
- package/dist/browser/V8InspectorProvider.js +6 -1
- package/dist/browser/WebSourceMapFetcher.js +24 -11
- package/dist/cli/androidCommands.js +81 -0
- package/dist/{cliApplicationCommands.js → cli/applicationCommands.js} +18 -16
- package/dist/{cliBrowserAdvancedCommands.js → cli/browserAdvancedCommands.js} +13 -16
- package/dist/{cliBrowserCommands.js → cli/browserCommands.js} +9 -9
- package/dist/cli/commandCancellation.js +24 -0
- package/dist/cli/coreAnalysisCommands.js +110 -17
- package/dist/{cliElectronCommands.js → cli/electronCommands.js} +14 -13
- package/dist/cli/firmwareCommands.js +75 -0
- package/dist/{cliSetup.js → cli/interactiveSetup.js} +1 -1
- package/dist/cli/javascriptApplicationAnalysis.js +1 -1
- package/dist/cli/managedCommands.js +1 -1
- package/dist/cli/managedProjectGraphCommand.js +1 -1
- package/dist/cli/options.js +7 -1
- package/dist/{cliProcessCommands.js → cli/processCommands.js} +3 -3
- package/dist/cli/referenceSourceImportStatus.js +10 -0
- package/dist/cli/setupCommands.js +4 -2
- package/dist/cli/utilityCommands.js +3 -2
- package/dist/cli.js +10 -15
- package/dist/cliBrowserContext.js +1 -1
- package/dist/cliBrowserScenarioCommands.js +2 -1
- package/dist/cliCommandNames.js +11 -2
- package/dist/cliEvidenceCommands.js +2 -1
- package/dist/cliJavaScriptRuntimeCommands.js +2 -1
- package/dist/cliJsonInput.js +26 -11
- package/dist/cliLogging.js +2 -1
- package/dist/cliOutput.js +15 -16
- package/dist/config/environment.js +11 -1
- package/dist/config/parseConfig.js +7 -1
- package/dist/config/parsers.js +1 -1
- package/dist/contracts/androidToolContracts.js +77 -0
- package/dist/contracts/applicationToolContracts.js +32 -5
- package/dist/contracts/applicationWorkflowInputContracts.js +13 -13
- package/dist/contracts/artifactComparisonExample.js +7 -14
- package/dist/contracts/enhancedToolContracts.js +31 -0
- package/dist/contracts/errorSchemas.js +4 -2
- package/dist/contracts/evidenceInputContracts.js +13 -0
- package/dist/contracts/firmwareToolContracts.js +38 -0
- package/dist/contracts/officialToolContracts.js +66 -0
- package/dist/contracts/promptContracts.js +1 -1
- package/dist/contracts/sessionLifecycleInputs.js +4 -0
- package/dist/contracts/sessionToolContracts.js +43 -0
- package/dist/contracts/sessionToolSchemas.js +1 -1
- package/dist/contracts/toolContractExamples.js +5 -0
- package/dist/contracts/toolContractTypes.js +2 -0
- package/dist/contracts/toolContracts.js +7 -130
- package/dist/contracts/toolEffects.js +43 -0
- package/dist/contracts/toolOutputSchemaGroups.js +4 -0
- package/dist/contracts/toolOutputSchemaPrimitives.js +1 -0
- package/dist/doctorRuntime.js +5 -1
- package/dist/domain/analysisErrorBase.js +33 -0
- package/dist/domain/analysisErrorCore.js +60 -0
- package/dist/domain/analysisErrorPresentation.js +22 -2
- package/dist/domain/analysisErrorProjection.js +24 -3
- package/dist/domain/analysisSnapshot.js +1 -0
- package/dist/domain/androidAnalysis.js +141 -0
- package/dist/domain/appleAssetCatalog.js +1 -1
- package/dist/domain/artifactGraph.js +1 -0
- package/dist/domain/artifactInspection.js +19 -17
- package/dist/domain/artifactInventoryEvidence.js +20 -25
- package/dist/domain/artifactOperationError.js +16 -0
- package/dist/domain/browserObservation.js +12 -4
- package/dist/domain/browserObservationError.js +23 -0
- package/dist/domain/browserScenario.js +1 -1
- package/dist/domain/browserScenarioCapture.js +1 -1
- package/dist/domain/browserScenarioDiff.js +1 -1
- package/dist/domain/browserScenarioNormalization.js +6 -11
- package/dist/domain/browserScenarioValues.js +3 -1
- package/dist/domain/callPath.js +47 -7
- package/dist/domain/canonicalDigest.js +65 -0
- package/dist/domain/canonicalOrdering.js +11 -0
- package/dist/domain/changedBehavior.js +8 -7
- package/dist/domain/comparisonSemantics.js +2 -4
- package/dist/domain/configurationErrors.js +25 -0
- package/dist/domain/dosCom.js +8 -0
- package/dist/domain/electronActiveObservation.js +3 -1
- package/dist/domain/electronStaticAnalysisBrowser.js +13 -23
- package/dist/domain/electronStaticAnalysisNative.js +2 -1
- package/dist/domain/electronStaticAnalysisValues.js +23 -3
- package/dist/domain/evidence.js +3 -11
- package/dist/domain/evidenceErrors.js +30 -0
- package/dist/domain/firmwareAnalysis.js +121 -0
- package/dist/domain/functionComparison.js +4 -2
- package/dist/domain/functionComparisonDimensions.js +6 -4
- package/dist/domain/functionComparisonNormalization.js +8 -1
- package/dist/domain/functionComparisonResults.js +1 -4
- package/dist/domain/hopperErrors.js +78 -0
- package/dist/domain/hopperValues.js +2 -1
- package/dist/domain/interfaceBuilderGraph.js +3 -1
- package/dist/domain/javascriptApplicationChangeGraph.js +2 -2
- package/dist/domain/javascriptApplicationGraph.js +13 -22
- package/dist/domain/javascriptApplicationVersionComparison.js +3 -11
- package/dist/domain/javascriptApplicationVersionItems.js +3 -3
- package/dist/domain/javascriptApplicationVersionKeys.js +1 -1
- package/dist/domain/javascriptAstFingerprint.js +1 -1
- package/dist/domain/javascriptExportShapeComparison.js +1 -2
- package/dist/domain/javascriptExportShapeSelection.js +1 -1
- package/dist/domain/javascriptExportShapeVariants.js +7 -3
- package/dist/domain/javascriptFeatureSeed.js +29 -9
- package/dist/domain/javascriptFeatureTrace.js +9 -13
- package/dist/domain/javascriptFeatureTraversal.js +2 -1
- package/dist/domain/javascriptNativeHandoff.js +2 -1
- package/dist/domain/javascriptRuntimeLoadState.js +2 -2
- package/dist/domain/javascriptRuntimeReconciliationActive.js +3 -10
- package/dist/domain/javascriptRuntimeReconciliationGraph.js +2 -2
- package/dist/domain/javascriptRuntimeReconciliationMatching.js +4 -3
- package/dist/domain/javascriptRuntimeReconciliationParsing.js +8 -14
- package/dist/domain/javascriptRuntimeReconciliationResult.js +4 -4
- package/dist/domain/javascriptRuntimeReconciliationSchemas.js +5 -2
- package/dist/domain/javascriptRuntimeStaticCandidates.js +8 -4
- package/dist/domain/javascriptSemanticAnalysis.js +40 -3
- package/dist/domain/javascriptSemanticCallResolution.js +2 -1
- package/dist/domain/javascriptSemanticChildProcesses.js +2 -1
- package/dist/domain/javascriptSemanticDataEffects.js +2 -1
- package/dist/domain/javascriptSemanticFingerprints.js +1 -1
- package/dist/domain/javascriptSemanticGraph.js +23 -28
- package/dist/domain/javascriptSemanticPrimitives.js +9 -2
- package/dist/domain/javascriptSemanticProjection.js +2 -1
- package/dist/domain/javascriptSemanticPromises.js +2 -1
- package/dist/domain/javascriptSemanticProvenance.js +1 -1
- package/dist/domain/javascriptSemanticQuery.js +7 -8
- package/dist/domain/javascriptSemanticQueryAssessment.js +1 -2
- package/dist/domain/javascriptSemanticQueryIdentity.js +1 -2
- package/dist/domain/javascriptSemanticReturns.js +2 -1
- package/dist/domain/javascriptSemanticValues.js +50 -16
- package/dist/domain/javascriptSourceParser.js +4 -2
- package/dist/domain/javascriptStaticAnalysis.js +1 -1
- package/dist/domain/javascriptStaticAnalysisBundler.js +2 -1
- package/dist/domain/javascriptStaticAnalysisCalls.js +14 -6
- package/dist/domain/javascriptStaticAnalysisHelpers.js +31 -4
- package/dist/domain/jsonShape.js +6 -2
- package/dist/domain/managedMemberComparison.js +1 -0
- package/dist/domain/managedMemberComparisonItems.js +2 -0
- package/dist/domain/managedNativeVerificationMatch.js +5 -3
- package/dist/domain/nativeDataType.js +2 -0
- package/dist/domain/nativeFunctionAnnotations.js +43 -0
- package/dist/domain/nativeLoadImage.js +95 -0
- package/dist/domain/nativeMetadataRecovery.js +55 -0
- package/dist/domain/processCapture.js +0 -1
- package/dist/domain/processEvidenceProvider.js +11 -0
- package/dist/domain/processTraceComparison.js +1 -1
- package/dist/domain/providerAdapterError.js +17 -0
- package/dist/domain/providerCleanupError.js +1 -1
- package/dist/domain/providerSelectionError.js +44 -0
- package/dist/domain/reconstructionUnknowns.js +1 -1
- package/dist/domain/reconstructionVerification.js +22 -8
- package/dist/domain/referenceSourceClassification.js +5 -6
- package/dist/domain/referenceSourceGraph.js +9 -17
- package/dist/domain/referenceSourceImportParsing.js +38 -23
- package/dist/domain/runtimeIdentification.js +1 -1
- package/dist/domain/runtimeVersion.js +4 -7
- package/dist/domain/safeJson.js +13 -0
- package/dist/domain/sourceToBundleComparison.js +5 -7
- package/dist/domain/sourceToBundleSignals.js +6 -6
- package/dist/domain/staticRuntimeCorrelation.js +9 -7
- package/dist/domain/unknownRegistryError.js +10 -0
- package/dist/domain/webBundleAnalyzerAst.js +22 -5
- package/dist/domain/webBundleAnalyzerInspection.js +20 -14
- package/dist/dotnet/ManagedMemberRows.js +1 -1
- package/dist/dotnet/ManagedMetadataHeaps.js +3 -2
- package/dist/dotnet/ManagedMetadataInventoryRows.js +4 -2
- package/dist/dotnet/ManagedMethodBodyReader.js +8 -1
- package/dist/dotnet/ManagedStaticProvider.js +3 -1
- package/dist/firmware/FirmwareCommand.js +163 -0
- package/dist/firmware/FirmwareFiles.js +190 -0
- package/dist/firmware/FirmwareProvider.js +258 -0
- package/dist/firmware/FirmwarePublication.js +84 -0
- package/dist/firmware/FirmwareRelease.js +32 -0
- package/dist/firmware/FirmwareReports.js +223 -0
- package/dist/generatedMcpToolCatalog.js +1 -1
- package/dist/generatedPackageMetadata.js +5 -5
- package/dist/ghidra/GhidraAnalysisProfile.js +37 -8
- package/dist/ghidra/GhidraClient.js +52 -6
- package/dist/ghidra/GhidraDiagnostics.js +3 -0
- package/dist/ghidra/GhidraDoctor.js +38 -14
- package/dist/ghidra/GhidraFunctionValues.js +10 -4
- package/dist/ghidra/GhidraInstallation.js +10 -4
- package/dist/ghidra/GhidraInventoryValues.js +39 -2
- package/dist/ghidra/GhidraLauncher.js +91 -26
- package/dist/ghidra/GhidraLoadImageValues.js +199 -0
- package/dist/ghidra/GhidraProvider.js +129 -13
- package/dist/ghidra/GhidraProviderCapabilities.js +52 -15
- package/dist/ghidra/GhidraRequestQueue.js +2 -2
- package/dist/ghidra/GhidraSessionError.js +31 -4
- package/dist/ghidra/GhidraSessionValues.js +10 -7
- package/dist/ghidra/GhidraTargetSnapshot.js +16 -2
- package/dist/ghidra/GhidraTransport.js +12 -0
- package/dist/ghidra/extensions/GhidraExtensionFailures.js +20 -0
- package/dist/ghidra/extensions/GhidraExtensions.js +170 -0
- package/dist/ghidra/extensions/nativeaot/NativeAotAdapter.js +21 -0
- package/dist/ghidra/extensions/nativeaot/NativeAotReport.js +84 -0
- package/dist/hopper/BridgeLauncher.js +4 -2
- package/dist/hopper/HopperAnalysisProfile.js +7 -2
- package/dist/hopper/HopperCleanup.js +55 -9
- package/dist/hopper/HopperClient.js +14 -4
- package/dist/hopper/HopperProcessDiagnostic.js +1 -1
- package/dist/hopper/HopperProvider.js +8 -4
- package/dist/hopper/HopperRequestQueue.js +7 -3
- package/dist/hopper/HopperSessionValues.js +1 -1
- package/dist/hopper/HopperSocketConnection.js +1 -1
- package/dist/hopper/HopperTargetLease.js +23 -15
- package/dist/hopper/LinuxPrivateDisplayDiagnostic.js +7 -8
- package/dist/hopper/LinuxPrivateDisplayProbe.js +6 -2
- package/dist/hopper/protocol.js +8 -8
- package/dist/ida/IdaConfiguration.js +56 -0
- package/dist/ida/IdaDoctor.js +53 -0
- package/dist/ida/IdaInput.js +62 -0
- package/dist/ida/IdaMcpConnection.js +111 -0
- package/dist/ida/IdaOperationRunner.js +299 -0
- package/dist/ida/IdaProtocolValues.js +178 -0
- package/dist/ida/IdaProvider.js +126 -0
- package/dist/ida/IdaProviderCapabilities.js +60 -0
- package/dist/ida/IdaSessionClient.js +306 -0
- package/dist/ida/IdaWorkspace.js +54 -0
- package/dist/main/shutdown.js +21 -1
- package/dist/main/startup.js +1 -1
- package/dist/main/transport.js +8 -4
- package/dist/main.js +5 -2
- package/dist/native/AppleDispatchMetadata.js +31 -10
- package/dist/native/NativeMacOSProvider.js +87 -15
- package/dist/native/NativeMachoInspection.js +42 -12
- package/dist/native/NativeUiHelperRuntime.js +7 -1
- package/dist/native/NativeUiObservation.js +6 -2
- package/dist/native/parsers/demangle.js +1 -1
- package/dist/native/parsers/dyldInfo.js +3 -3
- package/dist/native/parsers/otool.js +4 -1
- package/dist/native/parsers/plist.js +11 -3
- package/dist/process/PrivateRuntimeRoot.js +13 -3
- package/dist/process/ProcessOwnership.js +5 -73
- package/dist/process/ProcessOwnershipObservation.js +62 -2
- package/dist/process/ProcessOwnershipProcessTree.js +13 -0
- package/dist/process/ProviderProcess.js +24 -7
- package/dist/process/ProviderRunLineage.js +1 -1
- package/dist/process/WindowsAuthority.js +20 -1
- package/dist/reference/ReferenceSourceReaderEntries.js +5 -1
- package/dist/reference/ReferenceSourceReaderFile.js +1 -0
- package/dist/server/createServer.js +25 -2
- package/dist/server/mcpProgress.js +2 -1
- package/dist/server/promptCompletion.js +3 -3
- package/dist/server/registerAndroidTools.js +30 -0
- package/dist/server/registerApplicationTools/compareExportShapes.js +5 -1
- package/dist/server/registerApplicationTools/compareSourceToBundle.js +7 -2
- package/dist/server/registerApplicationTools/compareVersions.js +5 -1
- package/dist/server/registerApplicationTools/traceFeature.js +7 -2
- package/dist/server/registerApplicationTools/traceSemantics.js +7 -2
- package/dist/server/registerEnhancedTools.js +28 -1
- package/dist/server/registerFirmwareTools.js +22 -0
- package/dist/server/registerFunctionComparisonTool.js +1 -1
- package/dist/server/registerInvestigationTools/helpers.js +2 -1
- package/dist/server/registerManagedTools.js +1 -1
- package/dist/server/registerManagedWorkflowTools/evidence.js +1 -1
- package/dist/server/registerOfficialTools.js +4 -1
- package/dist/server/registerProcessComparisonTool.js +5 -2
- package/dist/server/registerSessionTools.js +3 -1
- package/dist/server/runDerivedOperation.js +1 -1
- package/dist/server/sessionAvailabilityPolicy.js +3 -0
- package/dist/server/sessionEvidence.js +1 -1
- package/dist/server/toolResult.js +2 -1
- package/dist/windows/WindowsNativeLoader.js +108 -0
- package/dist/windows/WindowsOwnedProcess.js +168 -0
- package/dist/windows/WindowsPrivateRuntime.js +137 -0
- package/install.sh +4 -6
- package/native/windows/README.md +102 -0
- package/native/windows/build/manifest.json +9 -0
- package/native/windows/build/rea-windows-x64.node +0 -0
- package/package.json +38 -24
- package/scripts/rea.mjs +1 -1
- package/scripts/verify-windows-native-artifact.mjs +39 -0
- package/skills/reverse-engineer-anything/SKILL.md +92 -24
- package/skills/reverse-engineer-anything/references/android-applications.md +31 -0
- package/skills/reverse-engineer-anything/references/javascript-applications.md +19 -2
- package/skills/reverse-engineer-anything/references/native-and-artifacts.md +52 -4
- package/dist/browser/CdpCanonicalJsonDigest.js +0 -9
- package/dist/domain/errors.js +0 -324
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { constants } from "node:fs";
|
|
3
|
+
import { open, realpath, writeFile, mkdir } from "node:fs/promises";
|
|
4
|
+
import { isAbsolute, join } from "node:path";
|
|
5
|
+
import { z } from "zod";
|
|
6
|
+
import { AnalysisCancelledError } from "../../domain/analysisErrorCore.js";
|
|
7
|
+
import { ProviderAdapterError } from "../../domain/providerAdapterError.js";
|
|
8
|
+
import { jsonObjectSchema } from "../../domain/jsonValue.js";
|
|
9
|
+
import { err, ok } from "../../domain/result.js";
|
|
10
|
+
import { nativeAotAdapter } from "./nativeaot/NativeAotAdapter.js";
|
|
11
|
+
const digest = z.string().regex(/^[a-f0-9]{64}$/u);
|
|
12
|
+
/** Exact executable extension artifact committed before the session starts. */
|
|
13
|
+
export const ghidraExtensionSchema = z.strictObject({
|
|
14
|
+
id: z.string().regex(/^[a-z][a-z0-9-]*$/u),
|
|
15
|
+
path: z.string().refine(isAbsolute, "Extension path must be absolute"),
|
|
16
|
+
configured_path: z
|
|
17
|
+
.string()
|
|
18
|
+
.refine(isAbsolute, "Extension path must be absolute"),
|
|
19
|
+
sha256: digest,
|
|
20
|
+
entry_class: z.string().min(1),
|
|
21
|
+
integration_api: z.literal(1),
|
|
22
|
+
});
|
|
23
|
+
/** Shared extension envelope; each adapter validates its own producer result. */
|
|
24
|
+
export const ghidraExtensionResultSchema = z.strictObject({
|
|
25
|
+
id: z.string().min(1),
|
|
26
|
+
sha256: digest,
|
|
27
|
+
status: z.enum([
|
|
28
|
+
"complete",
|
|
29
|
+
"partial",
|
|
30
|
+
"not_applicable",
|
|
31
|
+
"unsupported",
|
|
32
|
+
"failed",
|
|
33
|
+
]),
|
|
34
|
+
reason: z.string().nullable(),
|
|
35
|
+
result: jsonObjectSchema,
|
|
36
|
+
});
|
|
37
|
+
const adapters = [nativeAotAdapter];
|
|
38
|
+
const MAX_EXTENSION_BYTES = 8 * 1024 * 1024;
|
|
39
|
+
/** Read a bounded regular artifact through its open handle, without ambient state changes. */
|
|
40
|
+
const readJar = async (path) => {
|
|
41
|
+
const file = await open(path, constants.O_RDONLY | constants.O_NOFOLLOW | constants.O_NONBLOCK);
|
|
42
|
+
try {
|
|
43
|
+
const stat = await file.stat();
|
|
44
|
+
if (!stat.isFile() || stat.size < 4 || stat.size > MAX_EXTENSION_BYTES)
|
|
45
|
+
throw new Error(`Ghidra extension must be a regular JAR of 4..${MAX_EXTENSION_BYTES} bytes: ${path}`);
|
|
46
|
+
const bytes = Buffer.alloc(stat.size);
|
|
47
|
+
let offset = 0;
|
|
48
|
+
while (offset < bytes.length) {
|
|
49
|
+
const read = await file.read(bytes, offset, bytes.length - offset, offset);
|
|
50
|
+
if (read.bytesRead === 0)
|
|
51
|
+
throw new Error(`Incomplete Ghidra extension read: ${path}`);
|
|
52
|
+
offset += read.bytesRead;
|
|
53
|
+
}
|
|
54
|
+
const extra = Buffer.alloc(1);
|
|
55
|
+
if ((await file.read(extra, 0, 1, offset)).bytesRead !== 0 ||
|
|
56
|
+
bytes.readUInt32LE(0) !== 0x04034b50)
|
|
57
|
+
throw new Error(`Ghidra extension changed during read or is not a JAR: ${path}`);
|
|
58
|
+
return bytes;
|
|
59
|
+
}
|
|
60
|
+
finally {
|
|
61
|
+
await file.close();
|
|
62
|
+
}
|
|
63
|
+
};
|
|
64
|
+
/** Resolve explicitly configured optional extensions into the immutable analysis profile. */
|
|
65
|
+
export const resolveGhidraExtensions = async (config, target, platform, signal) => {
|
|
66
|
+
const extensions = [];
|
|
67
|
+
for (const adapter of adapters) {
|
|
68
|
+
if (signal?.aborted)
|
|
69
|
+
return err(new AnalysisCancelledError("open_binary"));
|
|
70
|
+
const configured = adapter.configuredPath(config);
|
|
71
|
+
if (configured === undefined)
|
|
72
|
+
continue;
|
|
73
|
+
const reason = adapter.unsupportedReason(target, platform);
|
|
74
|
+
if (reason !== null)
|
|
75
|
+
return err(new ProviderAdapterError("ghidra", "resolve_analysis_profile", {
|
|
76
|
+
diagnostics: { extension: adapter.id, reason, path: configured },
|
|
77
|
+
}));
|
|
78
|
+
try {
|
|
79
|
+
const path = await realpath(configured);
|
|
80
|
+
const bytes = await readJar(path);
|
|
81
|
+
extensions.push({
|
|
82
|
+
id: adapter.id,
|
|
83
|
+
path,
|
|
84
|
+
configured_path: configured,
|
|
85
|
+
sha256: createHash("sha256").update(bytes).digest("hex"),
|
|
86
|
+
entry_class: adapter.entryClass,
|
|
87
|
+
integration_api: 1,
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
catch (cause) {
|
|
91
|
+
return err(new ProviderAdapterError("ghidra", "resolve_analysis_profile", {
|
|
92
|
+
cause,
|
|
93
|
+
diagnostics: {
|
|
94
|
+
extension: adapter.id,
|
|
95
|
+
path: configured,
|
|
96
|
+
reason: cause instanceof Error ? cause.message : String(cause),
|
|
97
|
+
},
|
|
98
|
+
}));
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
if (signal?.aborted)
|
|
102
|
+
return err(new AnalysisCancelledError("open_binary"));
|
|
103
|
+
return ok(extensions);
|
|
104
|
+
};
|
|
105
|
+
/** Require explicit local configuration before admitting executable code from a profile. */
|
|
106
|
+
export const validateGhidraExtensionProfile = (extensions, config, target, platform) => {
|
|
107
|
+
const enabled = adapters.filter((adapter) => adapter.configuredPath(config) !== undefined);
|
|
108
|
+
if (enabled.length !== extensions.length ||
|
|
109
|
+
new Set(extensions.map((extension) => extension.id)).size !==
|
|
110
|
+
extensions.length)
|
|
111
|
+
return "Configured Ghidra extension inventory differs from the resolved analysis profile.";
|
|
112
|
+
for (const extension of extensions) {
|
|
113
|
+
const adapter = enabled.find((value) => value.id === extension.id);
|
|
114
|
+
if (adapter === undefined ||
|
|
115
|
+
adapter.entryClass !== extension.entry_class ||
|
|
116
|
+
adapter.configuredPath(config) !== extension.configured_path)
|
|
117
|
+
return `Ghidra extension profile lacks matching explicit local configuration: ${extension.id}`;
|
|
118
|
+
const reason = adapter.unsupportedReason(target, platform);
|
|
119
|
+
if (reason !== null)
|
|
120
|
+
return reason;
|
|
121
|
+
}
|
|
122
|
+
return null;
|
|
123
|
+
};
|
|
124
|
+
/** Snapshot committed extension bytes into the private runtime before Java loads them. */
|
|
125
|
+
export const snapshotGhidraExtensions = async (extensions, runtimeRoot) => {
|
|
126
|
+
if (extensions.length === 0)
|
|
127
|
+
return [];
|
|
128
|
+
for (const extension of extensions) {
|
|
129
|
+
ghidraExtensionSchema.parse(extension);
|
|
130
|
+
const adapter = adapters.find((value) => value.id === extension.id);
|
|
131
|
+
if (adapter === undefined || adapter.entryClass !== extension.entry_class)
|
|
132
|
+
throw new Error(`Unregistered Ghidra extension identity: ${extension.id}`);
|
|
133
|
+
if ((await realpath(extension.configured_path)) !== extension.path)
|
|
134
|
+
throw new Error(`Ghidra extension resolved path differs from explicit configuration: ${extension.id}`);
|
|
135
|
+
}
|
|
136
|
+
const directory = join(runtimeRoot, "extensions");
|
|
137
|
+
await mkdir(directory, { mode: 0o700 });
|
|
138
|
+
const result = [];
|
|
139
|
+
for (const extension of extensions) {
|
|
140
|
+
const bytes = await readJar(extension.path);
|
|
141
|
+
if (createHash("sha256").update(bytes).digest("hex") !== extension.sha256)
|
|
142
|
+
throw new Error(`Ghidra extension digest differs from committed profile: ${extension.id} (${extension.path})`);
|
|
143
|
+
const path = join(directory, `${extension.id}.jar`);
|
|
144
|
+
await writeFile(path, bytes, { flag: "wx", mode: 0o600 });
|
|
145
|
+
result.push({ ...extension, path });
|
|
146
|
+
}
|
|
147
|
+
return result;
|
|
148
|
+
};
|
|
149
|
+
/** Require every configured producer result to match its artifact and registered ABI. */
|
|
150
|
+
export const validateGhidraExtensionResults = (expected, results) => {
|
|
151
|
+
if (results.length !== expected.length ||
|
|
152
|
+
new Set(results.map((result) => result.id)).size !== results.length)
|
|
153
|
+
return "Ghidra extension result inventory differs from its committed profile.";
|
|
154
|
+
for (const extension of expected) {
|
|
155
|
+
const result = results.find((value) => value.id === extension.id);
|
|
156
|
+
const adapter = adapters.find((value) => value.id === extension.id);
|
|
157
|
+
if (result === undefined ||
|
|
158
|
+
adapter === undefined ||
|
|
159
|
+
result.sha256 !== extension.sha256)
|
|
160
|
+
return `Ghidra extension identity mismatch: ${extension.id}`;
|
|
161
|
+
const invalid = adapter.validate(result);
|
|
162
|
+
if (invalid !== null)
|
|
163
|
+
return invalid;
|
|
164
|
+
}
|
|
165
|
+
return null;
|
|
166
|
+
};
|
|
167
|
+
/** Exact adapter limitations for explicitly configured session extensions. */
|
|
168
|
+
export const ghidraExtensionLimitations = (extensions) => adapters
|
|
169
|
+
.filter((adapter) => extensions.some((extension) => extension.id === adapter.id))
|
|
170
|
+
.flatMap((adapter) => adapter.limitations);
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { validateNativeAotReport } from "./NativeAotReport.js";
|
|
2
|
+
/** NativeAOT-specific prerequisites and report interpretation behind the extension boundary. */
|
|
3
|
+
export const nativeAotAdapter = {
|
|
4
|
+
id: "nativeaot",
|
|
5
|
+
entryClass: "rea.extensions.nativeaot.NativeAotExtension",
|
|
6
|
+
configuredPath: (config) => config.ghidraNativeAotJar,
|
|
7
|
+
unsupportedReason: (target, platform) => platform !== "linux"
|
|
8
|
+
? "NativeAOT recovery is verified on Linux only; omit REA_GHIDRA_NATIVEAOT_JAR for ordinary native analysis."
|
|
9
|
+
: !["elf", "pe"].includes(target.format) ||
|
|
10
|
+
target.architecture !== "x86_64" ||
|
|
11
|
+
target.managed === true
|
|
12
|
+
? "NativeAOT recovery requires an x86-64 ELF or native PE target on Linux; PE/CLI and ReadyToRun assemblies use inspect_managed_artifact; omit REA_GHIDRA_NATIVEAOT_JAR for ordinary native analysis."
|
|
13
|
+
: null,
|
|
14
|
+
validate: validateNativeAotReport,
|
|
15
|
+
limitations: [
|
|
16
|
+
"Optional NativeAOT recovery is verified for .NET 8.0.22 RTR 9.1 Linux x64 ELF and Windows x64 PE targets on a Linux host. Other runtime layouts, target architectures and hosts are unsupported.",
|
|
17
|
+
"Type names and relationships use recovery heuristics; original C# source and custom field layouts are not recovered. Rehydrated analysis-memory bytes are derived, without original file offsets or runtime observations.",
|
|
18
|
+
"Method prototypes are Ghidra inferences, not original signatures. Pre-recovery conventions are retained; new functions use the loaded compiler default. Verify parameter roles against instructions and call sites.",
|
|
19
|
+
"The configured extension retains its actual JAR digest. Its reported source revision is not an attestation of caller-supplied build bytes.",
|
|
20
|
+
],
|
|
21
|
+
};
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
const report = z
|
|
3
|
+
.object({
|
|
4
|
+
id: z.literal("nativeaot"),
|
|
5
|
+
integration_api: z.literal(1),
|
|
6
|
+
source_revision: z.literal("effeb734fc570c32650f88b159608979dc7b423e"),
|
|
7
|
+
source_revision_authority: z.literal("build-reported-unattested"),
|
|
8
|
+
status: z.enum([
|
|
9
|
+
"complete",
|
|
10
|
+
"partial",
|
|
11
|
+
"not_applicable",
|
|
12
|
+
"unsupported",
|
|
13
|
+
"failed",
|
|
14
|
+
]),
|
|
15
|
+
reason: z.string().nullable(),
|
|
16
|
+
method_tables: z.number().int().nonnegative(),
|
|
17
|
+
diagnostics: z.array(z.string()),
|
|
18
|
+
header_address: z
|
|
19
|
+
.string()
|
|
20
|
+
.regex(/^0x[0-9a-f]+$/u)
|
|
21
|
+
.optional(),
|
|
22
|
+
discovery: z.enum(["symbol", "signature-heuristic"]).optional(),
|
|
23
|
+
format_major: z.number().int().optional(),
|
|
24
|
+
format_minor: z.number().int().optional(),
|
|
25
|
+
derived_memory: z
|
|
26
|
+
.object({
|
|
27
|
+
address: z.string().regex(/^0x[0-9a-f]+$/u),
|
|
28
|
+
size_bytes: z.number().int().nonnegative(),
|
|
29
|
+
sha256: z.string().regex(/^[a-f0-9]{64}$/u),
|
|
30
|
+
file_offset: z.null(),
|
|
31
|
+
})
|
|
32
|
+
.optional(),
|
|
33
|
+
coverage: z
|
|
34
|
+
.strictObject({
|
|
35
|
+
frozen_object_candidates: z.number().int().nonnegative(),
|
|
36
|
+
frozen_objects_annotated: z.number().int().nonnegative(),
|
|
37
|
+
basis: z.literal("rehydrated-pointer-candidates-and-committed-instance-types"),
|
|
38
|
+
})
|
|
39
|
+
.optional(),
|
|
40
|
+
types: z
|
|
41
|
+
.array(z.object({
|
|
42
|
+
address: z.string().regex(/^0x[0-9a-f]+$/u),
|
|
43
|
+
type: z.string().min(1),
|
|
44
|
+
}))
|
|
45
|
+
.optional(),
|
|
46
|
+
})
|
|
47
|
+
.strict();
|
|
48
|
+
/** Validate the pinned producer representation before its result enters Evidence. */
|
|
49
|
+
export const validateNativeAotReport = (value) => {
|
|
50
|
+
if (value.status === "failed" &&
|
|
51
|
+
typeof value.result.loader_failure === "string" &&
|
|
52
|
+
value.result.loader_failure === value.reason)
|
|
53
|
+
return null;
|
|
54
|
+
const parsed = report.safeParse(value.result);
|
|
55
|
+
if (!parsed.success ||
|
|
56
|
+
parsed.data.status !== value.status ||
|
|
57
|
+
parsed.data.reason !== value.reason)
|
|
58
|
+
return "NativeAOT extension returned a malformed or inconsistent producer report.";
|
|
59
|
+
return validateRecoveredMetadata(parsed.data);
|
|
60
|
+
};
|
|
61
|
+
const validateRecoveredMetadata = (value) => {
|
|
62
|
+
if (["complete", "partial"].includes(value.status) &&
|
|
63
|
+
(value.method_tables === 0 ||
|
|
64
|
+
value.format_major !== 9 ||
|
|
65
|
+
value.format_minor !== 1 ||
|
|
66
|
+
value.types?.length !== value.method_tables ||
|
|
67
|
+
value.derived_memory === undefined ||
|
|
68
|
+
value.header_address === undefined ||
|
|
69
|
+
value.discovery === undefined ||
|
|
70
|
+
value.coverage === undefined ||
|
|
71
|
+
value.reason !== null))
|
|
72
|
+
return "NativeAOT recovery omitted its supported format, method-table inventory or derived-memory identity.";
|
|
73
|
+
const coverage = value.coverage;
|
|
74
|
+
if (coverage !== undefined &&
|
|
75
|
+
(coverage.frozen_objects_annotated > coverage.frozen_object_candidates ||
|
|
76
|
+
(value.status === "complete" &&
|
|
77
|
+
coverage.frozen_objects_annotated !==
|
|
78
|
+
coverage.frozen_object_candidates)))
|
|
79
|
+
return "NativeAOT recovery coverage contradicts its reported completion status.";
|
|
80
|
+
const types = value.types ?? [];
|
|
81
|
+
if (new Set(types.map((type) => type.address)).size !== types.length)
|
|
82
|
+
return "NativeAOT recovery repeated a method-table identity.";
|
|
83
|
+
return null;
|
|
84
|
+
};
|
|
@@ -2,7 +2,7 @@ import { execFile } from "node:child_process";
|
|
|
2
2
|
import { chmod, writeFile } from "node:fs/promises";
|
|
3
3
|
import { basename, dirname, extname, join } from "node:path";
|
|
4
4
|
import { promisify } from "node:util";
|
|
5
|
-
import { HopperCancelledError, HopperProcessError, HopperStartError, } from "../domain/
|
|
5
|
+
import { HopperCancelledError, HopperProcessError, HopperStartError, } from "../domain/hopperErrors.js";
|
|
6
6
|
import { err, ok } from "../domain/result.js";
|
|
7
7
|
import { cleanupOwnedProcessGroup } from "../process/ProcessOwnership.js";
|
|
8
8
|
import { execFileOutput } from "../process/ExecFileOutput.js";
|
|
@@ -275,7 +275,9 @@ const processIsRunning = async (executablePath) => {
|
|
|
275
275
|
.split("\n")
|
|
276
276
|
.some((command) => command.trim() === executablePath);
|
|
277
277
|
}
|
|
278
|
-
catch {
|
|
278
|
+
catch (cause) {
|
|
279
|
+
// best-effort cleanup: optional process probing; failure means not running.
|
|
280
|
+
void cause;
|
|
279
281
|
return false;
|
|
280
282
|
}
|
|
281
283
|
};
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { createHash } from "node:crypto";
|
|
2
2
|
import { createReadStream } from "node:fs";
|
|
3
3
|
import { createAnalysisProfile } from "../domain/analysisProfile.js";
|
|
4
|
-
import { AnalysisCancelledError
|
|
4
|
+
import { AnalysisCancelledError } from "../domain/analysisErrorCore.js";
|
|
5
|
+
import { ProviderAdapterError } from "../domain/providerAdapterError.js";
|
|
6
|
+
import {} from "../domain/analysisErrorBase.js";
|
|
5
7
|
import { err, ok } from "../domain/result.js";
|
|
6
8
|
/** Resolve Hopper open semantics without placing its CLI flags in BinaryTarget. */
|
|
7
9
|
export const resolveHopperAnalysisProfile = async (target, options) => {
|
|
@@ -63,6 +65,7 @@ export const hopperLoaderArgsForTarget = (target) => {
|
|
|
63
65
|
case "pe":
|
|
64
66
|
return ok(["-l", "WinPE", flag]);
|
|
65
67
|
case "dos-mz":
|
|
68
|
+
case "dos-com":
|
|
66
69
|
return err(new ProviderAdapterError("hopper", "resolve_analysis_profile"));
|
|
67
70
|
}
|
|
68
71
|
};
|
|
@@ -94,7 +97,9 @@ const sha256File = async (path, signal) => {
|
|
|
94
97
|
? err(new AnalysisCancelledError("open_binary"))
|
|
95
98
|
: ok(hash.digest("hex"));
|
|
96
99
|
}
|
|
97
|
-
catch {
|
|
100
|
+
catch (cause) {
|
|
101
|
+
// best-effort cleanup: unreadable targets mean no digest; abort still cancels.
|
|
102
|
+
void cause;
|
|
98
103
|
return signalIsAborted(signal)
|
|
99
104
|
? err(new AnalysisCancelledError("open_binary"))
|
|
100
105
|
: ok(undefined);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { HopperProcessError } from "../domain/
|
|
1
|
+
import { HopperProcessError } from "../domain/hopperErrors.js";
|
|
2
2
|
import { ProviderCleanupError } from "../domain/providerCleanupError.js";
|
|
3
3
|
import { err, ok } from "../domain/result.js";
|
|
4
4
|
import { createOwnedHopperShutdownDiagnostic, providerCleanupFailure, } from "./HopperDiagnostics.js";
|
|
@@ -34,11 +34,12 @@ const requestShutdown = async (input, state) => {
|
|
|
34
34
|
}, "Hopper shutdown skipped because a bridge operation remained active");
|
|
35
35
|
return;
|
|
36
36
|
}
|
|
37
|
-
const
|
|
38
|
-
.request(input.retainDocument || input.launch?.shutdownMode === "process-cleanup"
|
|
37
|
+
const method = input.retainDocument || input.launch?.shutdownMode === "process-cleanup"
|
|
39
38
|
? "shutdown"
|
|
40
|
-
: "shutdown_document"
|
|
41
|
-
|
|
39
|
+
: "shutdown_document";
|
|
40
|
+
const shutdown = await input
|
|
41
|
+
.request(method)
|
|
42
|
+
.catch((cause) => err(shutdownRequestFailure(method, cause)));
|
|
42
43
|
if (shutdown.ok &&
|
|
43
44
|
isHopperCleanupRequired(shutdown.value) &&
|
|
44
45
|
input.launch?.shutdownMode === "process-cleanup") {
|
|
@@ -57,17 +58,60 @@ const requestShutdown = async (input, state) => {
|
|
|
57
58
|
}
|
|
58
59
|
if (!shutdown.ok ||
|
|
59
60
|
(!state.shutdownConfirmed && state.cleanupResult === undefined))
|
|
60
|
-
input.logger.warn({
|
|
61
|
+
input.logger.warn({
|
|
62
|
+
status: shutdown.ok ? "invalid-acknowledgement" : "failed",
|
|
63
|
+
...(shutdown.ok ? {} : shutdownFailureDetails(shutdown.error)),
|
|
64
|
+
}, "Hopper document shutdown was not confirmed");
|
|
61
65
|
};
|
|
62
66
|
const fallbackDocumentShutdown = async (input) => {
|
|
63
67
|
const fallback = await input
|
|
64
68
|
.request("shutdown_document")
|
|
65
|
-
.catch(() => err(
|
|
69
|
+
.catch((cause) => err(shutdownRequestFailure("shutdown_document", cause)));
|
|
66
70
|
const confirmed = fallback.ok && isHopperShutdownAcknowledgement(fallback.value);
|
|
67
71
|
if (!confirmed)
|
|
68
|
-
input.logger.warn({
|
|
72
|
+
input.logger.warn({
|
|
73
|
+
status: fallback.ok ? "invalid-acknowledgement" : "failed",
|
|
74
|
+
...(fallback.ok ? {} : shutdownFailureDetails(fallback.error)),
|
|
75
|
+
}, "Hopper document shutdown fallback was not confirmed");
|
|
69
76
|
return confirmed;
|
|
70
77
|
};
|
|
78
|
+
const shutdownRequestFailure = (operation, cause) => {
|
|
79
|
+
const failure = new HopperProcessError(null, undefined, operation);
|
|
80
|
+
failure.cause = cause;
|
|
81
|
+
return failure;
|
|
82
|
+
};
|
|
83
|
+
const shutdownFailureDetails = (error) => ({
|
|
84
|
+
errorTag: error._tag,
|
|
85
|
+
...(error instanceof HopperProcessError && error.operation !== undefined
|
|
86
|
+
? { operation: error.operation }
|
|
87
|
+
: {}),
|
|
88
|
+
...(error instanceof HopperProcessError && error.requestId !== undefined
|
|
89
|
+
? { requestId: error.requestId }
|
|
90
|
+
: {}),
|
|
91
|
+
...(error.cause === undefined
|
|
92
|
+
? {}
|
|
93
|
+
: { failure_cause: describeFailureCause(error.cause) }),
|
|
94
|
+
});
|
|
95
|
+
const describeFailureCause = (cause) => {
|
|
96
|
+
if (cause instanceof Error) {
|
|
97
|
+
const code = "code" in cause ? cause.code : undefined;
|
|
98
|
+
return {
|
|
99
|
+
name: cause.name,
|
|
100
|
+
message: cause.message,
|
|
101
|
+
...(typeof code === "string" ||
|
|
102
|
+
(typeof code === "number" && Number.isFinite(code))
|
|
103
|
+
? { code }
|
|
104
|
+
: {}),
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
if (typeof cause === "string")
|
|
108
|
+
return { type: "string", message: cause };
|
|
109
|
+
if (cause === null ||
|
|
110
|
+
typeof cause === "boolean" ||
|
|
111
|
+
(typeof cause === "number" && Number.isFinite(cause)))
|
|
112
|
+
return { type: cause === null ? "null" : typeof cause, value: cause };
|
|
113
|
+
return { type: typeof cause };
|
|
114
|
+
};
|
|
71
115
|
const stopProcess = async (input, state) => {
|
|
72
116
|
const supervisor = input.processSupervisor;
|
|
73
117
|
if (supervisor === undefined)
|
|
@@ -79,8 +123,9 @@ const stopProcess = async (input, state) => {
|
|
|
79
123
|
try {
|
|
80
124
|
input.onDiagnostic?.(diagnostic);
|
|
81
125
|
}
|
|
82
|
-
catch {
|
|
126
|
+
catch (cause) {
|
|
83
127
|
// Diagnostic consumers cannot change the already-observed cleanup result.
|
|
128
|
+
void cause;
|
|
84
129
|
}
|
|
85
130
|
input.logger.info(diagnostic, "Owned Hopper launcher shutdown completed");
|
|
86
131
|
if (stopped.status !== "incomplete") {
|
|
@@ -149,6 +194,7 @@ const activityDetails = (activity) => ({
|
|
|
149
194
|
queued_requests: activity.queuedRequests,
|
|
150
195
|
});
|
|
151
196
|
const report = async (progress, completed, message) => {
|
|
197
|
+
// best-effort cleanup: progress reporting must not fail session cleanup.
|
|
152
198
|
await progress
|
|
153
199
|
?.report({
|
|
154
200
|
phase: "hopper_cleanup",
|
|
@@ -2,7 +2,10 @@ import { randomBytes, randomUUID } from "node:crypto";
|
|
|
2
2
|
import { access } from "node:fs/promises";
|
|
3
3
|
import { tmpdir } from "node:os";
|
|
4
4
|
import { join } from "node:path";
|
|
5
|
-
import { AnalysisCapabilityUnavailableError
|
|
5
|
+
import { AnalysisCapabilityUnavailableError } from "../domain/analysisErrorCore.js";
|
|
6
|
+
import { HopperCancelledError, HopperProcessError, HopperProtocolError, HopperRemoteError, HopperStartError, HopperTimeoutError, } from "../domain/hopperErrors.js";
|
|
7
|
+
import {} from "../domain/analysisErrorBase.js";
|
|
8
|
+
import { hopperStartupFailure, } from "../domain/hopperStartupFailure.js";
|
|
6
9
|
import { err, ok } from "../domain/result.js";
|
|
7
10
|
import { silentLogger } from "../logger.js";
|
|
8
11
|
import { PrivateRuntimeRoot } from "../process/PrivateRuntimeRoot.js";
|
|
@@ -119,7 +122,8 @@ export class HopperClient {
|
|
|
119
122
|
release();
|
|
120
123
|
if (!result.ok)
|
|
121
124
|
reset();
|
|
122
|
-
}, () => {
|
|
125
|
+
}, (cause) => {
|
|
126
|
+
this.#logger.debug({ error: cause instanceof Error ? cause.message : String(cause) }, "Hopper startup promise rejected during bookkeeping");
|
|
123
127
|
release();
|
|
124
128
|
reset();
|
|
125
129
|
});
|
|
@@ -228,6 +232,8 @@ export class HopperClient {
|
|
|
228
232
|
}
|
|
229
233
|
async #close(starting, controller, options) {
|
|
230
234
|
try {
|
|
235
|
+
// best-effort cleanup: a rejected startup must not mask close/cleanup;
|
|
236
|
+
// #cleanup reports the authoritative close result.
|
|
231
237
|
await starting?.catch(() => undefined);
|
|
232
238
|
return await this.#cleanup(options);
|
|
233
239
|
}
|
|
@@ -286,7 +292,9 @@ export class HopperClient {
|
|
|
286
292
|
try {
|
|
287
293
|
await access(socketPath);
|
|
288
294
|
}
|
|
289
|
-
catch {
|
|
295
|
+
catch (cause) {
|
|
296
|
+
// best-effort cleanup: socket polling; absence means keep waiting.
|
|
297
|
+
void cause;
|
|
290
298
|
if ((await deadline.wait(50)) === "aborted")
|
|
291
299
|
return err(startupInterruption(deadline));
|
|
292
300
|
continue;
|
|
@@ -350,7 +358,9 @@ export class HopperClient {
|
|
|
350
358
|
message: message.event.error.message,
|
|
351
359
|
});
|
|
352
360
|
}
|
|
353
|
-
catch {
|
|
361
|
+
catch (cause) {
|
|
362
|
+
// best-effort cleanup: diagnostic consumers must not break the client.
|
|
363
|
+
void cause;
|
|
354
364
|
this.#logger.warn({ requestId: message.id }, "Hopper bridge diagnostic consumer rejected an event");
|
|
355
365
|
}
|
|
356
366
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { hopperStartupFailure, } from "../domain/
|
|
1
|
+
import { hopperStartupFailure, } from "../domain/hopperStartupFailure.js";
|
|
2
2
|
import { parseLinuxPrivateDisplayDiagnostic } from "./LinuxPrivateDisplayDiagnostic.js";
|
|
3
3
|
/** Extract only a matching Linux adapter failure record. */
|
|
4
4
|
export const hopperLauncherFailureDiagnostic = (event) => {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { fileURLToPath } from "node:url";
|
|
2
2
|
import { accessSync, constants } from "node:fs";
|
|
3
3
|
import { createAnalysisExecution } from "../application/AnalysisProvider.js";
|
|
4
|
-
import { AnalysisCapabilityUnavailableError } from "../domain/
|
|
4
|
+
import { AnalysisCapabilityUnavailableError } from "../domain/analysisErrorCore.js";
|
|
5
5
|
import { err } from "../domain/result.js";
|
|
6
6
|
import { GENERATED_MCP_TOOL_CATALOG } from "../generatedMcpToolCatalog.js";
|
|
7
7
|
import { HopperApplicationLauncher } from "./BridgeLauncher.js";
|
|
@@ -25,6 +25,8 @@ const MUTATING_OPERATIONS = new Set([
|
|
|
25
25
|
/** Tool contracts implemented directly by the Hopper adapter. */
|
|
26
26
|
export const HOPPER_PROVIDER_TOOL_CONTRACTS = Object.freeze([
|
|
27
27
|
...GENERATED_MCP_TOOL_CATALOG.filter(({ kind, name }) => kind === "official-proxy" &&
|
|
28
|
+
name !== "annotate_native_function" &&
|
|
29
|
+
name !== "inspect_native_load_image" &&
|
|
28
30
|
name !== "inspect_native_data_type" &&
|
|
29
31
|
name !== "inspect_native_instruction" &&
|
|
30
32
|
name !== "resolve_native_call_targets"),
|
|
@@ -96,7 +98,9 @@ export class HopperProvider {
|
|
|
96
98
|
diagnostics,
|
|
97
99
|
};
|
|
98
100
|
}
|
|
99
|
-
catch {
|
|
101
|
+
catch (cause) {
|
|
102
|
+
// best-effort cleanup: optional launcher probing; failure means unavailable.
|
|
103
|
+
void cause;
|
|
100
104
|
return {
|
|
101
105
|
status: "unavailable",
|
|
102
106
|
code: "executable_missing",
|
|
@@ -125,11 +129,11 @@ export class HopperProvider {
|
|
|
125
129
|
reason: null,
|
|
126
130
|
diagnostics,
|
|
127
131
|
};
|
|
128
|
-
if (target.format === "dos-mz")
|
|
132
|
+
if (target.format === "dos-mz" || target.format === "dos-com")
|
|
129
133
|
return {
|
|
130
134
|
status: "unsupported",
|
|
131
135
|
code: "target_format_unsupported",
|
|
132
|
-
reason: "DOS MZ analysis requires the Ghidra 16-bit real-mode adapter.",
|
|
136
|
+
reason: "DOS MZ/COM analysis requires the Ghidra 16-bit real-mode adapter.",
|
|
133
137
|
diagnostics,
|
|
134
138
|
};
|
|
135
139
|
return {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { HopperCancelledError, HopperProcessError, HopperProtocolError, HopperRemoteError, } from "../domain/
|
|
1
|
+
import { HopperCancelledError, HopperProcessError, HopperProtocolError, HopperRemoteError, } from "../domain/hopperErrors.js";
|
|
2
2
|
import { err } from "../domain/result.js";
|
|
3
3
|
/** FIFO that keeps the wire serialized until Hopper actually replies. */
|
|
4
4
|
export class HopperRequestQueue {
|
|
@@ -184,10 +184,14 @@ export class HopperRequestQueue {
|
|
|
184
184
|
}
|
|
185
185
|
#reportUpdate(entry, update) {
|
|
186
186
|
try {
|
|
187
|
-
void Promise.resolve(entry.progress?.report(update)).catch(() =>
|
|
187
|
+
void Promise.resolve(entry.progress?.report(update)).catch((cause) => {
|
|
188
|
+
// best-effort cleanup: progress rejection bookkeeping only.
|
|
189
|
+
void cause;
|
|
190
|
+
});
|
|
188
191
|
}
|
|
189
|
-
catch {
|
|
192
|
+
catch (cause) {
|
|
190
193
|
// Progress observation cannot change the request outcome.
|
|
194
|
+
void cause;
|
|
191
195
|
}
|
|
192
196
|
}
|
|
193
197
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { HopperProtocolError } from "../domain/
|
|
1
|
+
import { HopperProtocolError } from "../domain/hopperErrors.js";
|
|
2
2
|
import { err, ok } from "../domain/result.js";
|
|
3
3
|
/** Validate the bridge identity and exact per-launch run identifier. */
|
|
4
4
|
export const parseHopperServerInfo = (value, expectedRunId) => {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { createConnection } from "node:net";
|
|
2
|
-
import { HopperCancelledError, HopperStartError } from "../domain/
|
|
2
|
+
import { HopperCancelledError, HopperStartError, } from "../domain/hopperErrors.js";
|
|
3
3
|
import { err, ok } from "../domain/result.js";
|
|
4
4
|
/** Open one abort-aware Hopper Unix-socket connection attempt. */
|
|
5
5
|
export const connectHopperSocketOnce = (socketPath, signal) => new Promise((resolve) => {
|
|
@@ -2,12 +2,20 @@ import { createHash } from "node:crypto";
|
|
|
2
2
|
import { chmod, lstat, mkdir, realpath, rm } from "node:fs/promises";
|
|
3
3
|
import { createConnection, createServer, } from "node:net";
|
|
4
4
|
import { join, resolve } from "node:path";
|
|
5
|
-
import { HopperStartError } from "../domain/
|
|
5
|
+
import { HopperStartError } from "../domain/hopperErrors.js";
|
|
6
|
+
import { safeParseJson } from "../domain/safeJson.js";
|
|
6
7
|
const leaseDirectory = join("/tmp", `rea-hopper-${process.getuid?.() ?? 0}`);
|
|
7
8
|
/** Keep two REA processes from opening duplicate Hopper documents for one target/profile. */
|
|
8
9
|
export const acquireHopperTargetLease = async (input) => {
|
|
9
10
|
const directory = input.directory ?? leaseDirectory;
|
|
10
11
|
await ensureLeaseDirectory(directory);
|
|
12
|
+
// The canonical path only derives the lease key; it never authorizes access.
|
|
13
|
+
// Common expected `realpath` failures include a missing target or path
|
|
14
|
+
// component (ENOENT, ENOTDIR), an unreadable component (EACCES), or a
|
|
15
|
+
// symlink loop (ELOOP). This catch also falls back for every other error,
|
|
16
|
+
// which may hide a real filesystem problem. The lexical `resolve` fallback
|
|
17
|
+
// still yields a deterministic key, but aliases may take separate leases
|
|
18
|
+
// whenever `realpath` cannot resolve them.
|
|
11
19
|
const targetPath = await realpath(input.targetPath).catch(() => resolve(input.targetPath));
|
|
12
20
|
const key = createHash("sha256")
|
|
13
21
|
.update(JSON.stringify({
|
|
@@ -104,22 +112,22 @@ const readOwner = (socketPath) => new Promise((resolveOwner) => {
|
|
|
104
112
|
const newline = response.indexOf("\n");
|
|
105
113
|
if (newline < 0)
|
|
106
114
|
return;
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
if (typeof value === "object" &&
|
|
110
|
-
value !== null &&
|
|
111
|
-
typeof Reflect.get(value, "runId") === "string" &&
|
|
112
|
-
typeof Reflect.get(value, "processId") === "number")
|
|
113
|
-
finish({
|
|
114
|
-
runId: Reflect.get(value, "runId"),
|
|
115
|
-
processId: Reflect.get(value, "processId"),
|
|
116
|
-
});
|
|
117
|
-
else
|
|
118
|
-
finish(undefined);
|
|
119
|
-
}
|
|
120
|
-
catch {
|
|
115
|
+
const parsed = safeParseJson(response.slice(0, newline));
|
|
116
|
+
if (!parsed.ok) {
|
|
121
117
|
finish(undefined);
|
|
118
|
+
return;
|
|
122
119
|
}
|
|
120
|
+
const value = parsed.value;
|
|
121
|
+
if (typeof value === "object" &&
|
|
122
|
+
value !== null &&
|
|
123
|
+
typeof Reflect.get(value, "runId") === "string" &&
|
|
124
|
+
typeof Reflect.get(value, "processId") === "number")
|
|
125
|
+
finish({
|
|
126
|
+
runId: Reflect.get(value, "runId"),
|
|
127
|
+
processId: Reflect.get(value, "processId"),
|
|
128
|
+
});
|
|
129
|
+
else
|
|
130
|
+
finish(undefined);
|
|
123
131
|
});
|
|
124
132
|
socket.on("error", () => finish(undefined));
|
|
125
133
|
socket.on("end", () => finish(undefined));
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { isHopperStartupFailureCode, } from "../domain/hopperStartupFailure.js";
|
|
3
|
+
import { safeParseJson } from "../domain/safeJson.js";
|
|
3
4
|
export const LINUX_PRIVATE_DISPLAY_DIAGNOSTIC_PREFIX = "REA_X11_DIAGNOSTIC=";
|
|
4
5
|
const diagnosticContextSchema = z.object({
|
|
5
6
|
component: z.literal("hopper_private_display"),
|
|
@@ -53,13 +54,11 @@ export const parseLinuxPrivateDisplayDiagnostic = (stderr) => {
|
|
|
53
54
|
const record = records[0];
|
|
54
55
|
if (record === undefined)
|
|
55
56
|
return { ok: false, reason: "diagnostic_missing" };
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
if (!parsed.success)
|
|
59
|
-
return { ok: false, reason: "diagnostic_malformed" };
|
|
60
|
-
return { ok: true, value: parsed.data };
|
|
61
|
-
}
|
|
62
|
-
catch {
|
|
57
|
+
const decoded = safeParseJson(record.slice(LINUX_PRIVATE_DISPLAY_DIAGNOSTIC_PREFIX.length));
|
|
58
|
+
if (!decoded.ok)
|
|
63
59
|
return { ok: false, reason: "diagnostic_malformed" };
|
|
64
|
-
|
|
60
|
+
const parsed = diagnosticSchema.safeParse(decoded.value);
|
|
61
|
+
if (!parsed.success)
|
|
62
|
+
return { ok: false, reason: "diagnostic_malformed" };
|
|
63
|
+
return { ok: true, value: parsed.data };
|
|
65
64
|
};
|