rea-agents 6.0.0 → 6.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 +23 -17
- package/bridge/android/ReaJadxBridge.java +14 -5
- package/bridge/ghidra/ReaGhidraBridge.java +173 -47
- package/bridge/hopper_bridge.py +310 -128
- package/bridge/mitmproxy/capture.py +0 -2
- package/bridge/native/rea_lldb_tracer.py +630 -94
- package/bridge/process/ProcessRunTokenReader.swift +39 -5
- package/bridge/pwntools/recorded_crash_impl.py +4 -0
- package/dist/android/JadxConfiguration.js +196 -33
- package/dist/android/JadxMcpTransport.js +4 -0
- package/dist/android/JadxProtocol.js +6 -0
- package/dist/android/JadxProvider.js +19 -2
- package/dist/android/JadxSession.js +23 -4
- package/dist/application/AnalysisContextQueries.js +8 -35
- package/dist/application/AnalysisDocument.js +32 -0
- package/dist/application/AppBundleExecutable.js +67 -21
- package/dist/application/CallPathTracing.js +19 -3
- package/dist/application/CapabilityInventory.js +6 -3
- package/dist/application/ClientConfigurationDocument.js +871 -6
- package/dist/application/ClientRegistrationIdentity.js +26 -5
- package/dist/application/ClientRegistrationStatus.js +29 -14
- package/dist/application/DirectAnalysis.js +27 -37
- package/dist/application/Doctor.js +4 -2
- package/dist/application/DoctorScope.js +25 -13
- package/dist/application/EnhancedLiteralTracing.js +22 -25
- package/dist/application/EnhancedTools.js +13 -11
- package/dist/application/EvidenceBundleFiles.js +23 -11
- package/dist/application/FunctionAnalysisEvidence.js +2 -3
- package/dist/application/InventoryProjectionEvidence.js +4 -3
- package/dist/application/InvestigationProviders.js +3 -2
- package/dist/application/JsonFiles.js +50 -28
- package/dist/application/ProcedureAddressResolution.js +11 -0
- package/dist/application/ReconstructionCoverageService.js +7 -4
- package/dist/application/ReconstructionObligationLedgerService.js +4 -3
- package/dist/application/Setup.js +13 -1
- package/dist/application/SetupClientConfiguration.js +10 -15
- package/dist/application/SetupHost.js +5 -2
- package/dist/application/SetupPlan.js +5 -3
- package/dist/application/SupportedClients.js +34 -1
- package/dist/application/Uninstall.js +4 -3
- package/dist/application/Update.js +18 -3
- package/dist/application/UpdateRuntime.js +46 -25
- package/dist/application/WebModuleTraceService.js +4 -2
- package/dist/application/WebNetworkCaptureService.js +4 -2
- package/dist/application/WebRuntimeService.js +5 -3
- package/dist/application/WebScriptExportService.js +2 -2
- package/dist/application/WebSourceLocationService.js +4 -2
- package/dist/application/WorkflowEvidence.js +82 -0
- package/dist/application/binary/AnalysisProviderEvaluation.js +21 -5
- package/dist/application/binary/AnalysisProviderRegistry.js +2 -0
- package/dist/application/binary/AnalysisSnapshotCache.js +3 -6
- package/dist/application/binary/BinarySession.js +95 -68
- package/dist/application/binary/BinarySessionOpen.js +3 -3
- package/dist/application/binary/BinarySessionRecords.js +25 -3
- package/dist/application/binary/BinarySessionStatus.js +17 -1
- package/dist/application/binary/CompositeProvider.js +13 -0
- package/dist/application/binary/LazyAnalysisProvider.js +3 -0
- package/dist/application/binaryDiagnostics/BinaryLayoutService.js +4 -2
- package/dist/application/binaryDiagnostics/RecordedCrashService.js +4 -2
- package/dist/application/evm/EvmInterfaceService.js +4 -2
- package/dist/application/investigation/EvidenceLedger.js +21 -7
- package/dist/application/investigation/InvestigationRecords.js +8 -0
- package/dist/application/javascript/JavaScriptAnalysisControl.js +34 -0
- package/dist/application/javascript/JavaScriptApplicationEvidence.js +24 -3
- package/dist/application/javascript/JavaScriptApplicationEvidenceGraph.js +19 -2
- package/dist/application/javascript/JavaScriptApplicationService.js +22 -15
- package/dist/application/javascript/JavaScriptApplicationWorkflowService.js +5 -5
- package/dist/application/javascript/JavaScriptArtifactAnalysis.js +5 -4
- package/dist/application/javascript/JavaScriptArtifactGraphBuilder.js +39 -7
- package/dist/application/javascript/JavaScriptArtifactPathResolution.js +6 -3
- package/dist/application/javascript/JavaScriptArtifactReconstruction.js +11 -3
- package/dist/application/javascript/JavaScriptModuleRelationships.js +37 -8
- package/dist/application/javascript/JavaScriptRecoveryService.js +4 -2
- package/dist/application/javascript/JavaScriptRuntimeObservationService.js +3 -1
- package/dist/application/javascript/JavaScriptRuntimeReconciliationService.js +3 -3
- package/dist/application/javascript/JavaScriptSemanticGraphBuilder.js +63 -61
- package/dist/application/javascript/JavaScriptSemanticGraphValueProjection.js +23 -1
- package/dist/application/managed/ManagedApplicationGraphService.js +4 -3
- package/dist/application/managed/ManagedMemberComparisonService.js +7 -4
- package/dist/application/managed/ManagedNativeVerificationService.js +4 -3
- package/dist/application/managed/ManagedReconstructionService.js +4 -3
- package/dist/application/process/ProcessCli.js +14 -6
- package/dist/application/process/ProcessEvidence.js +4 -3
- package/dist/application/workflowInputError.js +18 -0
- package/dist/artifacts/ArtifactProvider.js +12 -2
- package/dist/artifacts/MachOSliceArtifactReader.js +98 -14
- package/dist/artifacts/NativeDmgArtifactReader.js +13 -6
- package/dist/artifacts/apple/AppleAssetCatalogAnalysis.js +22 -3
- package/dist/artifacts/apple/InterfaceBuilderAnalysis.js +176 -56
- package/dist/artifacts/apple/InterfaceBuilderDecodeBudget.js +195 -0
- package/dist/artifacts/apple/KeyedArchiveReader.js +36 -22
- package/dist/artifacts/apple/MachoLoadCommandReader.js +92 -31
- package/dist/artifacts/apple/NibArchive.js +81 -14
- package/dist/artifacts/apple/NibViewHierarchy.js +10 -3
- package/dist/artifacts/apple/PlistNumberLiterals.js +164 -0
- package/dist/artifacts/inventory/ArtifactGraphConstruction.js +13 -8
- package/dist/artifacts/inventory/scanCanonical.js +11 -14
- package/dist/artifacts/javascript/JavaScriptArtifactFiles.js +6 -2
- package/dist/browser/CdpCaptureCompleteness.js +2 -0
- package/dist/browser/CdpCaptureEventHandlers.js +76 -11
- package/dist/browser/CdpCaptureEventTypes.js +5 -1
- package/dist/browser/CdpCaptureEvents.js +25 -0
- package/dist/browser/CdpConnection.js +30 -11
- package/dist/browser/CdpOptionalCommand.js +2 -3
- package/dist/browser/CdpPageCapture.js +23 -1
- package/dist/browser/PlaywrightElectronActiveActions.js +32 -23
- package/dist/browser/PlaywrightElectronActiveProjection.js +141 -0
- package/dist/browser/PlaywrightElectronActiveProvider.js +123 -124
- package/dist/browser/WebSourceMapFetcher.js +426 -72
- package/dist/browser/assets/LocalWebScriptArtifacts.js +2 -2
- package/dist/browser/history/CaptureFailures.js +37 -1
- package/dist/browser/history/CaptureRelease.js +5 -0
- package/dist/browser/history/HarCaptureAdapter.js +3 -3
- package/dist/browser/history/HarJson.js +3 -2
- package/dist/browser/history/HistoricalCaptureDecoder.js +4 -2
- package/dist/cli/analysisSelector.js +23 -0
- package/dist/cli/applicationCommands.js +2 -3
- package/dist/cli/coreBinaryCommands.js +70 -12
- package/dist/cli/electronCommands.js +7 -6
- package/dist/cli/javascriptApplicationAnalysis.js +7 -3
- package/dist/cli/jsonOutput.js +4 -99
- package/dist/cli/nativeCallCommands.js +2 -3
- package/dist/cli/webRuntimeCommands.js +8 -8
- package/dist/cli.js +2 -1
- package/dist/cliCommandNames.js +0 -1
- package/dist/cliJsonInput.js +9 -4
- package/dist/config/environment.js +4 -0
- package/dist/config/ghidraStartupTimeout.js +17 -0
- package/dist/config/mcpResponseBudget.js +22 -0
- package/dist/config/parseConfig.js +2 -1
- package/dist/contracts/enhancedInputs.js +5 -3
- package/dist/contracts/enhancedToolContracts.js +3 -3
- package/dist/contracts/hopperMemoryContracts.js +2 -5
- package/dist/contracts/javascript/electronToolContracts.js +1 -1
- package/dist/contracts/managed/managedToolContracts.js +2 -4
- package/dist/contracts/native/nativeToolContracts.js +4 -2
- package/dist/contracts/officialToolContracts.js +14 -15
- package/dist/contracts/promptContracts.js +3 -3
- package/dist/contracts/sessionLifecycleInputs.js +3 -7
- package/dist/contracts/sessionStatusContract.js +2 -4
- package/dist/contracts/sessionToolContracts.js +4 -6
- package/dist/contracts/sessionToolSchemas.js +2 -5
- package/dist/contracts/toolContractExamples.js +0 -1
- package/dist/contracts/toolContractHelpers.js +4 -1
- package/dist/contracts/toolEffects.js +0 -1
- package/dist/contracts/toolOutputSchemaGroups.js +28 -27
- package/dist/contracts/toolOutputSchemaPrimitives.js +18 -0
- package/dist/contracts/toolSchemaMetadata.js +69 -10
- package/dist/contracts/webNetworkCaptureToolContracts.js +1 -1
- package/dist/domain/analysisErrorBase.js +8 -3
- package/dist/domain/analysisErrorCore.js +3 -1
- package/dist/domain/analysisErrorPresentation.js +72 -17
- package/dist/domain/analysisErrorProjection.js +106 -27
- package/dist/domain/android/androidAnalysis.js +2 -4
- package/dist/domain/android/androidApplication.js +17 -9
- package/dist/domain/apple/appleApplication.js +41 -14
- package/dist/domain/apple/dyldEnvironment.js +73 -0
- package/dist/domain/apple/dylibResolution.js +120 -9
- package/dist/domain/apple/dylibResolutionFindings.js +9 -7
- package/dist/domain/apple/interfaceBuilderGraph.js +25 -13
- package/dist/domain/apple/interfaceBuilderKeyedArchive.js +13 -3
- package/dist/domain/apple/keyedArchive.js +9 -1
- package/dist/domain/apple/machoContainer.js +54 -0
- package/dist/domain/apple/plistNumbers.js +76 -0
- package/dist/domain/artifactIdentity.js +29 -0
- package/dist/domain/artifactInventoryEvidence.js +19 -24
- package/dist/domain/bridgeCandidateProjection.js +71 -0
- package/dist/domain/browserCompleteness.js +1 -0
- package/dist/domain/browserObservationError.js +24 -5
- package/dist/domain/browserScenarioValues.js +2 -6
- package/dist/domain/bundleComparison.js +3 -2
- package/dist/domain/canonicalJsonDigestSteps.js +133 -0
- package/dist/domain/canonicalOrdering.js +9 -5
- package/dist/domain/changedBehavior.js +6 -6
- package/dist/domain/comparisonSourceEvidence.js +40 -0
- package/dist/domain/configurationErrors.js +1 -1
- package/dist/domain/evidence.js +64 -9
- package/dist/domain/evidenceBundle.js +42 -1
- package/dist/domain/evidenceErrors.js +12 -1
- package/dist/domain/explicitSensitiveFailure.js +17 -1
- package/dist/domain/firmware/firmwareAnalysis.js +6 -7
- package/dist/domain/hopperErrors.js +23 -2
- package/dist/domain/hopperValues.js +51 -8
- package/dist/domain/immutableJson.js +114 -0
- package/dist/domain/inputIssueProjection.js +3 -0
- package/dist/domain/javascript/electronActiveObservation.js +30 -4
- package/dist/domain/javascript/electronStaticAnalysisValues.js +6 -12
- package/dist/domain/javascript/javascriptApplicationAnalysis.js +71 -23
- package/dist/domain/javascript/javascriptApplicationGraph.js +13 -0
- package/dist/domain/javascript/javascriptAstValues.js +20 -0
- package/dist/domain/javascript/javascriptRecovery.js +3 -8
- package/dist/domain/javascript/javascriptSemanticAnalysis.js +97 -2
- package/dist/domain/javascript/javascriptSemanticCoverage.js +6 -5
- package/dist/domain/javascript/javascriptSemanticDataEffectHelpers.js +9 -17
- package/dist/domain/javascript/javascriptSemanticDataEffects.js +22 -5
- package/dist/domain/javascript/javascriptSemanticGraph.js +13 -0
- package/dist/domain/javascript/javascriptSemanticGraphSchemas.js +1 -0
- package/dist/domain/javascript/javascriptSemanticPrimitives.js +25 -17
- package/dist/domain/javascript/javascriptSemanticQuerySchemas.js +8 -4
- package/dist/domain/javascript/javascriptSemanticResourceLimits.js +77 -0
- package/dist/domain/javascript/javascriptSemanticValues.js +83 -11
- package/dist/domain/javascript/javascriptSourceParser.js +6 -2
- package/dist/domain/javascript/javascriptStaticAnalysis.js +22 -17
- package/dist/domain/javascript/javascriptStaticAnalysisBundler.js +5 -3
- package/dist/domain/javascript/javascriptStaticAnalysisCalls.js +14 -4
- package/dist/domain/javascript/javascriptStaticAnalysisFindings.js +66 -7
- package/dist/domain/javascript/javascriptStaticAnalysisHelpers.js +28 -16
- package/dist/domain/javascript/javascriptStaticAnalysisState.js +3 -2
- package/dist/domain/jsonSerialization.js +109 -0
- package/dist/domain/jsonValue.js +90 -6
- package/dist/domain/localPath.js +6 -0
- package/dist/domain/managed/managedApplicationGraph.js +10 -10
- package/dist/domain/managed/managedInspectionEvidence.js +16 -0
- package/dist/domain/managed/managedMemberComparisonMatch.js +5 -9
- package/dist/domain/managed/managedNativeVerification.js +3 -9
- package/dist/domain/native/fileOffsetPartialObservation.js +1 -0
- package/dist/domain/native/nativeCallObservation.js +33 -11
- package/dist/domain/native/nativeCallPartialObservation.js +33 -0
- package/dist/domain/process/processCapture.js +8 -4
- package/dist/domain/process/processComparison.js +38 -4
- package/dist/domain/process/processScenario.js +11 -10
- package/dist/domain/propertyListKeys.js +1 -1
- package/dist/domain/propertyListXmlText.js +19 -0
- package/dist/domain/providerAdapterError.js +1 -1
- package/dist/domain/providerOperationHealth.js +54 -0
- package/dist/domain/reconstructionVerification.js +6 -43
- package/dist/domain/reconstructionVerificationSchemas.js +2 -2
- package/dist/domain/staticRuntimeCorrelation.js +6 -43
- package/dist/domain/webBundleAnalysis.js +20 -8
- package/dist/domain/webBundleAnalyzerInspection.js +13 -8
- package/dist/domain/webBundleAnalyzerResult.js +41 -2
- package/dist/domain/webRuntime.js +19 -5
- package/dist/domain/webSourceLocation.js +1 -0
- package/dist/firmware/FirmwareFiles.js +1 -1
- package/dist/generatedPackageMetadata.js +1 -1
- package/dist/ghidra/GhidraAnalysisProfile.js +5 -0
- package/dist/ghidra/GhidraClient.js +47 -14
- package/dist/ghidra/GhidraDiagnostics.js +7 -7
- package/dist/ghidra/GhidraFunctionValues.js +10 -7
- package/dist/ghidra/GhidraInstallation.js +18 -21
- package/dist/ghidra/GhidraInventoryValues.js +14 -4
- package/dist/ghidra/GhidraJavaLaunch.js +62 -0
- package/dist/ghidra/GhidraLauncher.js +33 -16
- package/dist/ghidra/GhidraProviderCapabilities.js +14 -2
- package/dist/ghidra/GhidraProviderClient.js +23 -12
- package/dist/ghidra/GhidraSessionError.js +11 -7
- package/dist/ghidra/GhidraTargetSnapshot.js +47 -12
- package/dist/ghidra/GhidraTransport.js +20 -0
- package/dist/hopper/BridgeLauncher.js +31 -11
- package/dist/hopper/HopperAnalysisProfile.js +62 -4
- package/dist/hopper/HopperCleanup.js +15 -3
- package/dist/hopper/HopperClient.js +222 -14
- package/dist/hopper/HopperFileOffset.js +237 -0
- package/dist/hopper/HopperMachOImage.js +196 -0
- package/dist/hopper/HopperOperationStage.js +6 -0
- package/dist/hopper/HopperProvider.js +63 -9
- package/dist/hopper/HopperProviderCapabilities.js +19 -1
- package/dist/hopper/HopperRegexSearch.js +137 -0
- package/dist/hopper/HopperRegexWorker.js +27 -0
- package/dist/hopper/HopperRequestQueue.js +68 -19
- package/dist/hopper/HopperTargetLease.js +3 -2
- package/dist/inspector/JavaScriptRuntimeScope.js +9 -1
- package/dist/inspector/V8InspectorCaptureProjection.js +8 -3
- package/dist/inspector/V8InspectorProvider.js +47 -11
- package/dist/javascript/sourceMaps/DecodedSourceMap.js +47 -0
- package/dist/javascript/sourceMaps/SourceMapCommand.js +16 -0
- package/dist/javascript/sourceMaps/SourceMapFormat.js +79 -31
- package/dist/javascript/sourceMaps/TraceSourceMap.js +14 -28
- package/dist/mcpDoctor.js +62 -2
- package/dist/native/AppleDispatchCoverage.js +47 -6
- package/dist/native/AppleDispatchDecodeFacts.js +6 -0
- package/dist/native/AppleDispatchMetadata.js +6 -3
- package/dist/native/AppleMachoFixups.js +48 -24
- package/dist/native/AppleMachoSelection.js +32 -16
- package/dist/native/AppleObjcCategories.js +6 -2
- package/dist/native/AppleObjcDispatchFacets.js +5 -2
- package/dist/native/AppleObjcProperties.js +2 -1
- package/dist/native/AppleSwiftDispatchFacets.js +27 -21
- package/dist/native/AppleSwiftVtables.js +21 -15
- package/dist/native/CommandRunner.js +81 -84
- package/dist/native/LldbCallTracer.js +365 -102
- package/dist/native/LldbRetainedObservations.js +178 -0
- package/dist/native/NativeCallObservation.js +62 -3
- package/dist/native/NativeMacOSProvider.js +98 -17
- package/dist/native/SignatureTargetBinding.js +155 -0
- package/dist/native/parsers/lipo.js +71 -1
- package/dist/native/pwndbg/PwndbgCoreContext.js +25 -5
- package/dist/native/pwntools/RecordedCrashStructureBindings.js +2 -0
- package/dist/process/DarwinProcessRunTokenReader.js +8 -9
- package/dist/process/ExecFileOutput.js +20 -0
- package/dist/process/OwnedCommand.js +7 -2
- package/dist/process/ProcessOwnership.js +203 -53
- package/dist/process/ProcessOwnershipObservation.js +49 -4
- package/dist/process/ProviderProcess.js +38 -7
- package/dist/process/capture/ProcessCaptureError.js +2 -2
- package/dist/process/capture/ProcessCaptureLifecycle.js +100 -34
- package/dist/server/EvidenceMcpServer.js +146 -0
- package/dist/server/createServer.js +15 -8
- package/dist/server/registerAndroidTools.js +2 -4
- package/dist/server/registerApplicationTools/helpers.js +2 -4
- package/dist/server/registerArtifactComparisonTool.js +11 -7
- package/dist/server/registerBinaryDiagnosticsTools.js +2 -4
- package/dist/server/registerBrowserScenarioTool.js +2 -4
- package/dist/server/registerBrowserTools.js +2 -4
- package/dist/server/registerBundleComparisonTool.js +2 -3
- package/dist/server/registerCloseLifecycleTool.js +8 -18
- package/dist/server/registerElectronTools.js +2 -4
- package/dist/server/registerEnhancedTools.js +18 -59
- package/dist/server/registerEvidenceTools.js +2 -4
- package/dist/server/registerEvmTools.js +2 -4
- package/dist/server/registerFirmwareTools.js +2 -4
- package/dist/server/registerFunctionComparisonTool.js +10 -6
- package/dist/server/registerInvestigationTools.js +7 -7
- package/dist/server/registerJavaScriptRecoveryTool.js +2 -4
- package/dist/server/registerJavaScriptRuntimeObservationTools.js +2 -4
- package/dist/server/registerManagedWorkflowTools/compareManagedMembers.js +2 -4
- package/dist/server/registerManagedWorkflowTools/importManagedReconstruction.js +2 -4
- package/dist/server/registerManagedWorkflowTools/projectManagedApplicationGraph.js +2 -4
- package/dist/server/registerManagedWorkflowTools/verifyManagedNativeBoundaries.js +2 -4
- package/dist/server/registerOfficialTools.js +2 -4
- package/dist/server/registerProcessComparisonTool.js +71 -45
- package/dist/server/registerRecordedCrashTools.js +2 -4
- package/dist/server/registerSessionRecordTools.js +5 -6
- package/dist/server/registerSessionStatusTool.js +17 -3
- package/dist/server/registerSessionTools.js +11 -3
- package/dist/server/registerWebModuleTool.js +2 -4
- package/dist/server/registerWebNetworkCaptureTool.js +2 -4
- package/dist/server/registerWebRuntimeTools.js +3 -7
- package/dist/server/registerWebScriptTool.js +2 -4
- package/dist/server/registerWebSourceLocationTool.js +2 -4
- package/dist/server/runDerivedOperation.js +15 -3
- package/dist/server/sessionAvailabilityPolicy.js +0 -1
- package/dist/server/toolRegistrationOptions.js +2 -2
- package/dist/server/toolResult.js +62 -10
- package/dist/server/toolResultEncoding.js +33 -0
- package/install.sh +1 -1
- package/native/windows/build/manifest.json +1 -1
- package/package.json +8 -3
- package/scripts/electron-active-hook-boundaries.cjs +18 -20
- package/scripts/electron-active-hook.cjs +241 -37
- package/skills/reverse-engineer-anything/SKILL.md +11 -3
- package/skills/reverse-engineer-anything/references/javascript-applications.md +8 -1
- package/dist/ghidra/GhidraDefaults.js +0 -2
|
@@ -1,16 +1,21 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
2
2
|
import { open, readFile, writeFile } from "node:fs/promises";
|
|
3
3
|
import { join } from "node:path";
|
|
4
4
|
import { fileURLToPath } from "node:url";
|
|
5
5
|
import { z } from "zod";
|
|
6
6
|
import { AnalysisCancelledError, AnalysisCapabilityUnavailableError, AnalysisTimeoutError, } from "../domain/analysisErrorCore.js";
|
|
7
|
+
import { EvidenceIntegrityError } from "../domain/evidenceErrors.js";
|
|
7
8
|
import { nativeCallEventSchema, nativeCodeLocationSchema, } from "../domain/native/nativeCallObservation.js";
|
|
8
9
|
import { ProviderAdapterError } from "../domain/providerAdapterError.js";
|
|
9
10
|
import { err, ok } from "../domain/result.js";
|
|
10
11
|
import { safeParseJson } from "../domain/safeJson.js";
|
|
11
|
-
import {
|
|
12
|
+
import { ProviderProcessSupervisor, spawnOwnedProviderProcess, } from "../process/ProviderProcess.js";
|
|
13
|
+
import { cleanupOwnedProcessGroup } from "../process/ProcessOwnership.js";
|
|
14
|
+
import { observeProcessStartIdentity, prepareProcessOwnershipInspection, signalProcessWithStartIdentity, } from "../process/ProcessOwnershipObservation.js";
|
|
15
|
+
import { ProviderStartupDeadline, waitForAbortableDelay, } from "../process/ProviderDeadline.js";
|
|
12
16
|
import { PrivateRuntimeRoot } from "../process/PrivateRuntimeRoot.js";
|
|
13
17
|
import { resolveXcrunTool } from "./CommandRunner.js";
|
|
18
|
+
import { readLldbObservationJournal, readBoundedPrefix, readPartialCapture, projectLldbPartialObservation, } from "./LldbRetainedObservations.js";
|
|
14
19
|
const OPERATION = "observe_native_calls";
|
|
15
20
|
const PROVIDER = "native-macos";
|
|
16
21
|
/** Captured target output kept per stream; the rest is counted, not stored. */
|
|
@@ -24,11 +29,39 @@ const tracedSchema = z.strictObject({
|
|
|
24
29
|
status: z.literal("traced"),
|
|
25
30
|
version: z.string(),
|
|
26
31
|
pid: z.number().int().positive(),
|
|
27
|
-
outcome: z.enum([
|
|
32
|
+
outcome: z.enum([
|
|
33
|
+
"exited",
|
|
34
|
+
"duration-elapsed",
|
|
35
|
+
"event-limit",
|
|
36
|
+
"stop-limit",
|
|
37
|
+
"resource-limit",
|
|
38
|
+
]),
|
|
28
39
|
exit_status: z.number().int().nullable(),
|
|
29
40
|
exit_description: z.string().nullable(),
|
|
30
41
|
killed: z.boolean(),
|
|
31
42
|
terminated: z.boolean(),
|
|
43
|
+
target_identity: z.strictObject({
|
|
44
|
+
loaded_image_sha256: z.null(),
|
|
45
|
+
selected_file_sha256: z
|
|
46
|
+
.string()
|
|
47
|
+
.regex(/^[0-9a-f]{64}$/u)
|
|
48
|
+
.nullable(),
|
|
49
|
+
file_device: z.string().nullable(),
|
|
50
|
+
file_inode: z.string().nullable(),
|
|
51
|
+
module_path: z.string().nullable(),
|
|
52
|
+
module_uuid: z.string().nullable(),
|
|
53
|
+
stable: z.boolean(),
|
|
54
|
+
}),
|
|
55
|
+
resource_limit_reached: z.boolean(),
|
|
56
|
+
breakpoint_locations_truncated: z.boolean().optional(),
|
|
57
|
+
target_output: z.strictObject({
|
|
58
|
+
stdout_bytes: z.number().int().nonnegative(),
|
|
59
|
+
stderr_bytes: z.number().int().nonnegative(),
|
|
60
|
+
stdout_truncated: z.boolean(),
|
|
61
|
+
stderr_truncated: z.boolean(),
|
|
62
|
+
stdout_complete: z.boolean(),
|
|
63
|
+
stderr_complete: z.boolean(),
|
|
64
|
+
}),
|
|
32
65
|
elapsed_ms: z.number().nonnegative(),
|
|
33
66
|
breakpoints: z.array(z.strictObject({
|
|
34
67
|
index: z.number().int().nonnegative(),
|
|
@@ -41,7 +74,13 @@ const tracedSchema = z.strictObject({
|
|
|
41
74
|
const tracerOutputSchema = z.discriminatedUnion("status", [
|
|
42
75
|
tracedSchema,
|
|
43
76
|
z.strictObject({
|
|
44
|
-
status: z.enum([
|
|
77
|
+
status: z.enum([
|
|
78
|
+
"target-error",
|
|
79
|
+
"target-integrity-error",
|
|
80
|
+
"target-identity-unavailable",
|
|
81
|
+
"launch-error",
|
|
82
|
+
"tracer-error",
|
|
83
|
+
]),
|
|
45
84
|
error: z.string().nullable(),
|
|
46
85
|
}),
|
|
47
86
|
]);
|
|
@@ -49,8 +88,14 @@ const tracerOutputSchema = z.discriminatedUnion("status", [
|
|
|
49
88
|
const ATTACH_REMEDIATION = "Allow debugging for this user (Developer Tools access: `DevToolsSecurity -enable`, or membership in the _developer group). A hardened-runtime target is debuggable only with the com.apple.security.get-task-allow entitlement; see inspect_signature's debugger-attach facet.";
|
|
50
89
|
/** Production tracer: `lldb --batch` running the REA LLDB bridge. */
|
|
51
90
|
export class LldbCallTracer {
|
|
91
|
+
launch;
|
|
92
|
+
resolveTool;
|
|
93
|
+
constructor(launch = runLldb, resolveTool = resolveXcrunTool) {
|
|
94
|
+
this.launch = launch;
|
|
95
|
+
this.resolveTool = resolveTool;
|
|
96
|
+
}
|
|
52
97
|
async trace(request, signal) {
|
|
53
|
-
const lldb = await
|
|
98
|
+
const lldb = await this.resolveTool("lldb", signal);
|
|
54
99
|
if (!lldb.ok)
|
|
55
100
|
return err(lldb.error.reason === "cancelled"
|
|
56
101
|
? new AnalysisCancelledError(OPERATION)
|
|
@@ -68,13 +113,18 @@ export class LldbCallTracer {
|
|
|
68
113
|
config: join(directory, "config.json"),
|
|
69
114
|
result: join(directory, "result.json"),
|
|
70
115
|
pid: join(directory, "pid"),
|
|
116
|
+
identityAck: join(directory, "identity.ack"),
|
|
71
117
|
stdout: join(directory, "stdout"),
|
|
72
118
|
stderr: join(directory, "stderr"),
|
|
119
|
+
stdoutCapture: join(directory, "stdout.capture"),
|
|
120
|
+
stderrCapture: join(directory, "stderr.capture"),
|
|
121
|
+
observations: join(directory, "observations.jsonl"),
|
|
73
122
|
};
|
|
74
123
|
const { input } = request;
|
|
75
124
|
await writeFile(paths.config, JSON.stringify({
|
|
76
125
|
executable: request.executable,
|
|
77
126
|
architecture: request.architecture,
|
|
127
|
+
expected_sha256: request.expectedSha256,
|
|
78
128
|
arguments: input.arguments,
|
|
79
129
|
environment: input.environment,
|
|
80
130
|
working_directory: input.working_directory ?? null,
|
|
@@ -85,12 +135,18 @@ export class LldbCallTracer {
|
|
|
85
135
|
max_events: input.max_events,
|
|
86
136
|
argument_registers: input.argument_registers,
|
|
87
137
|
backtrace_frames: input.backtrace_frames,
|
|
138
|
+
max_output_bytes: MAX_OUTPUT_BYTES,
|
|
88
139
|
stdout_path: paths.stdout,
|
|
89
140
|
stderr_path: paths.stderr,
|
|
141
|
+
stdout_capture_path: paths.stdoutCapture,
|
|
142
|
+
stderr_capture_path: paths.stderrCapture,
|
|
90
143
|
result_path: paths.result,
|
|
91
144
|
pid_path: paths.pid,
|
|
145
|
+
identity_ack_path: paths.identityAck,
|
|
146
|
+
observation_path: paths.observations,
|
|
92
147
|
}), { mode: 0o600 });
|
|
93
|
-
|
|
148
|
+
await prepareProcessOwnershipInspection(signal);
|
|
149
|
+
const exited = await this.launch(lldb.path, [
|
|
94
150
|
"--batch",
|
|
95
151
|
"--no-lldbinit",
|
|
96
152
|
"--no-use-colors",
|
|
@@ -100,47 +156,189 @@ export class LldbCallTracer {
|
|
|
100
156
|
`rea_trace ${paths.config}`,
|
|
101
157
|
], {
|
|
102
158
|
deadlineMs: input.duration_ms + DEADLINE_GRACE_MS,
|
|
159
|
+
pidPath: paths.pid,
|
|
160
|
+
identityAckPath: paths.identityAck,
|
|
103
161
|
...(signal === undefined ? {} : { signal }),
|
|
104
162
|
});
|
|
105
|
-
const
|
|
163
|
+
const output = exited.kind === "exited"
|
|
164
|
+
? await readTracerOutput(paths.result)
|
|
165
|
+
: undefined;
|
|
166
|
+
let targetTermination = await ensureTerminated(paths.pid, exited.targetIdentity, true);
|
|
167
|
+
// These two bridge outcomes are emitted before a target process exists.
|
|
168
|
+
// Other missing-PID cases, including a missing result, remain unknown.
|
|
169
|
+
if (output !== undefined &&
|
|
170
|
+
(output.status === "target-error" || output.status === "launch-error") &&
|
|
171
|
+
(await readTargetPid(paths.pid)) === undefined)
|
|
172
|
+
targetTermination = "terminated";
|
|
173
|
+
const terminated = targetTermination === "terminated";
|
|
174
|
+
const cleanupDetails = [
|
|
175
|
+
exited.cleanupFailure,
|
|
176
|
+
...(terminated ? [] : [targetTerminationReason(targetTermination)]),
|
|
177
|
+
].filter((detail) => detail !== undefined);
|
|
178
|
+
const lifecycleCleanup = cleanupDetails.length === 0
|
|
179
|
+
? undefined
|
|
180
|
+
: {
|
|
181
|
+
reason: cleanupDetails.join("; "),
|
|
182
|
+
resources: [
|
|
183
|
+
...(exited.cleanupFailure === undefined
|
|
184
|
+
? []
|
|
185
|
+
: ["lldb-process-group"]),
|
|
186
|
+
...(terminated
|
|
187
|
+
? []
|
|
188
|
+
: [
|
|
189
|
+
`native-target:${String((await readTargetPid(paths.pid)) ?? "unknown")}`,
|
|
190
|
+
]),
|
|
191
|
+
],
|
|
192
|
+
};
|
|
193
|
+
const partialReason = exited.kind === "cancelled"
|
|
194
|
+
? "cancelled"
|
|
195
|
+
: exited.kind === "timeout"
|
|
196
|
+
? "timeout"
|
|
197
|
+
: exited.cleanupFailure !== undefined
|
|
198
|
+
? "cleanup-failure"
|
|
199
|
+
: "tracer-failure";
|
|
200
|
+
const getPartialObservation = (reason = partialReason) => partialNativeObservation({
|
|
201
|
+
request,
|
|
202
|
+
pidPath: paths.pid,
|
|
203
|
+
stdoutPath: paths.stdoutCapture,
|
|
204
|
+
stderrPath: paths.stderrCapture,
|
|
205
|
+
observationPath: paths.observations,
|
|
206
|
+
version: output?.status === "traced" ? output.version : null,
|
|
207
|
+
reason,
|
|
208
|
+
});
|
|
106
209
|
if (exited.kind === "cancelled")
|
|
107
|
-
return err(new AnalysisCancelledError(OPERATION
|
|
210
|
+
return err(new AnalysisCancelledError(OPERATION, {
|
|
211
|
+
...(lifecycleCleanup === undefined
|
|
212
|
+
? {}
|
|
213
|
+
: { cleanup: lifecycleCleanup }),
|
|
214
|
+
partialObservation: await getPartialObservation(),
|
|
215
|
+
}));
|
|
108
216
|
if (exited.kind === "timeout")
|
|
109
|
-
return err(new AnalysisTimeoutError(OPERATION, input.duration_ms + DEADLINE_GRACE_MS
|
|
110
|
-
|
|
217
|
+
return err(new AnalysisTimeoutError(OPERATION, input.duration_ms + DEADLINE_GRACE_MS, {
|
|
218
|
+
...(lifecycleCleanup === undefined
|
|
219
|
+
? {}
|
|
220
|
+
: { cleanup: lifecycleCleanup }),
|
|
221
|
+
partialObservation: await getPartialObservation(),
|
|
222
|
+
}));
|
|
223
|
+
if (output !== undefined && output.status !== "traced")
|
|
224
|
+
return err(tracerFailure(output, lifecycleCleanup, await getPartialObservation()));
|
|
225
|
+
if (exited.cleanupFailure !== undefined)
|
|
226
|
+
return err(new ProviderAdapterError(PROVIDER, OPERATION, {
|
|
227
|
+
cleanup: lifecycleCleanup ?? {
|
|
228
|
+
reason: exited.cleanupFailure,
|
|
229
|
+
resources: ["lldb-process-group"],
|
|
230
|
+
},
|
|
231
|
+
diagnostics: {
|
|
232
|
+
reason: "LLDB process-group cleanup could not be verified",
|
|
233
|
+
cleanup_failure: exited.cleanupFailure,
|
|
234
|
+
...(lifecycleCleanup === undefined
|
|
235
|
+
? {}
|
|
236
|
+
: { target_cleanup: lifecycleCleanup }),
|
|
237
|
+
},
|
|
238
|
+
partialObservation: await getPartialObservation(),
|
|
239
|
+
}));
|
|
111
240
|
if (output === undefined)
|
|
112
241
|
return err(new ProviderAdapterError(PROVIDER, OPERATION, {
|
|
113
242
|
diagnostics: {
|
|
114
243
|
reason: "LLDB exited without a tracer result",
|
|
115
244
|
exit_code: exited.exitCode,
|
|
116
245
|
lldb_output: exited.output,
|
|
246
|
+
...(lifecycleCleanup === undefined
|
|
247
|
+
? {}
|
|
248
|
+
: { target_cleanup: lifecycleCleanup }),
|
|
117
249
|
},
|
|
250
|
+
partialObservation: await getPartialObservation(),
|
|
251
|
+
...(lifecycleCleanup === undefined
|
|
252
|
+
? {}
|
|
253
|
+
: { cleanup: lifecycleCleanup }),
|
|
118
254
|
}));
|
|
119
|
-
|
|
120
|
-
|
|
255
|
+
const stdout = await capturedOutput(paths.stdoutCapture, output.target_output.stdout_bytes, output.target_output.stdout_truncated, output.target_output.stdout_complete);
|
|
256
|
+
const stderr = await capturedOutput(paths.stderrCapture, output.target_output.stderr_bytes, output.target_output.stderr_truncated, output.target_output.stderr_complete);
|
|
257
|
+
if (!stdout.ok || !stderr.ok) {
|
|
258
|
+
const partial = await getPartialObservation("capture-failure");
|
|
259
|
+
return err(new ProviderAdapterError(PROVIDER, OPERATION, {
|
|
260
|
+
partialObservation: partial,
|
|
261
|
+
diagnostics: {
|
|
262
|
+
reason: "LLDB target output capture could not be read",
|
|
263
|
+
...(stdout.ok ? {} : { stdout_capture_error: stdout.error }),
|
|
264
|
+
...(stderr.ok ? {} : { stderr_capture_error: stderr.error }),
|
|
265
|
+
...(lifecycleCleanup === undefined
|
|
266
|
+
? {}
|
|
267
|
+
: { target_cleanup: lifecycleCleanup }),
|
|
268
|
+
},
|
|
269
|
+
...(lifecycleCleanup === undefined
|
|
270
|
+
? {}
|
|
271
|
+
: { cleanup: lifecycleCleanup }),
|
|
272
|
+
}));
|
|
273
|
+
}
|
|
121
274
|
return ok({
|
|
122
275
|
debugger: { ...lldb, version: output.version },
|
|
123
276
|
run: output,
|
|
124
|
-
stdout:
|
|
125
|
-
stderr:
|
|
277
|
+
stdout: stdout.value,
|
|
278
|
+
stderr: stderr.value,
|
|
126
279
|
terminated,
|
|
127
280
|
});
|
|
128
281
|
}
|
|
129
282
|
}
|
|
130
283
|
/** LLDB command arguments are quoted; the bridge path is REA's own install path. */
|
|
131
284
|
const quote = (value) => `"${value.replaceAll("\\", "\\\\").replaceAll('"', '\\"')}"`;
|
|
132
|
-
|
|
285
|
+
/** Preserve a decoded bridge failure's type independently of cleanup outcome. */
|
|
286
|
+
const tracerFailure = (output, cleanup, partialObservation) => {
|
|
287
|
+
const options = {
|
|
288
|
+
...(cleanup === undefined ? {} : { cleanup }),
|
|
289
|
+
...(partialObservation === undefined ? {} : { partialObservation }),
|
|
290
|
+
};
|
|
133
291
|
const message = output.error ?? "no LLDB error text";
|
|
292
|
+
if (output.status === "target-integrity-error")
|
|
293
|
+
return new EvidenceIntegrityError(message || "The launched native target did not match its session digest", options);
|
|
294
|
+
if (output.status === "target-identity-unavailable")
|
|
295
|
+
return new ProviderAdapterError(PROVIDER, OPERATION, {
|
|
296
|
+
...options,
|
|
297
|
+
diagnostics: {
|
|
298
|
+
reason: message,
|
|
299
|
+
constraint: "target process start identity could not be verified",
|
|
300
|
+
},
|
|
301
|
+
});
|
|
134
302
|
if (output.status === "tracer-error")
|
|
135
303
|
return new ProviderAdapterError(PROVIDER, OPERATION, {
|
|
304
|
+
...options,
|
|
136
305
|
diagnostics: { reason: `LLDB bridge failed: ${message}` },
|
|
137
306
|
});
|
|
138
307
|
if (output.status === "target-error")
|
|
139
|
-
return new AnalysisCapabilityUnavailableError(PROVIDER, OPERATION, `lldb-target-unloadable: ${message}
|
|
308
|
+
return new AnalysisCapabilityUnavailableError(PROVIDER, OPERATION, `lldb-target-unloadable: ${message}`, options);
|
|
140
309
|
// debugserver reports denied task access as an attach failure.
|
|
141
310
|
return /attach|not allowed|permission|denied|debugserver/iu.test(message)
|
|
142
|
-
? new AnalysisCapabilityUnavailableError(PROVIDER, OPERATION, `debugger-attach-denied: ${message}`, { userMessage: ATTACH_REMEDIATION })
|
|
143
|
-
: new AnalysisCapabilityUnavailableError(PROVIDER, OPERATION, `launch-failed: ${message}
|
|
311
|
+
? new AnalysisCapabilityUnavailableError(PROVIDER, OPERATION, `debugger-attach-denied: ${message}`, { userMessage: ATTACH_REMEDIATION, ...options })
|
|
312
|
+
: new AnalysisCapabilityUnavailableError(PROVIDER, OPERATION, `launch-failed: ${message}`, options);
|
|
313
|
+
};
|
|
314
|
+
const partialNativeObservation = async (options) => {
|
|
315
|
+
const [journal, pid, stdout, stderr] = await Promise.all([
|
|
316
|
+
readLldbObservationJournal(options.observationPath),
|
|
317
|
+
readTargetPid(options.pidPath),
|
|
318
|
+
readPartialCapture(options.stdoutPath),
|
|
319
|
+
readPartialCapture(options.stderrPath),
|
|
320
|
+
]);
|
|
321
|
+
return projectLldbPartialObservation({
|
|
322
|
+
target: {
|
|
323
|
+
path: options.request.executable,
|
|
324
|
+
sha256: options.request.expectedSha256,
|
|
325
|
+
architecture: options.request.architecture,
|
|
326
|
+
arguments: options.request.input.arguments,
|
|
327
|
+
environment: options.request.input.environment,
|
|
328
|
+
working_directory: options.request.input.working_directory ?? null,
|
|
329
|
+
},
|
|
330
|
+
pid,
|
|
331
|
+
stdout: stdout?.capture ?? null,
|
|
332
|
+
stderr: stderr?.capture ?? null,
|
|
333
|
+
version: options.version,
|
|
334
|
+
journal: journal.parsed,
|
|
335
|
+
journalLimitations: journal.limitations,
|
|
336
|
+
limitations: [
|
|
337
|
+
...(stdout?.limitations ?? []),
|
|
338
|
+
...(stderr?.limitations ?? []),
|
|
339
|
+
],
|
|
340
|
+
reason: options.reason,
|
|
341
|
+
});
|
|
144
342
|
};
|
|
145
343
|
const readTracerOutput = async (path) => {
|
|
146
344
|
let text;
|
|
@@ -156,105 +354,179 @@ const readTracerOutput = async (path) => {
|
|
|
156
354
|
const output = tracerOutputSchema.safeParse(parsed.value);
|
|
157
355
|
return output.success ? output.data : undefined;
|
|
158
356
|
};
|
|
159
|
-
const capturedOutput = async (path) => {
|
|
357
|
+
const capturedOutput = async (path, observedBytes, truncated, complete) => {
|
|
160
358
|
let handle;
|
|
161
359
|
try {
|
|
162
360
|
handle = await open(path, "r");
|
|
361
|
+
const { size } = await handle.stat();
|
|
362
|
+
const captureHandle = handle;
|
|
363
|
+
const { buffer, bytesRead } = await readBoundedPrefix(Math.min(size, MAX_OUTPUT_BYTES), async (target, offset, length, position) => (await captureHandle.read(target, offset, length, position)).bytesRead);
|
|
364
|
+
const captured = {
|
|
365
|
+
text: buffer.subarray(0, bytesRead).toString("utf8"),
|
|
366
|
+
bytes: observedBytes,
|
|
367
|
+
truncated: truncated || size > MAX_OUTPUT_BYTES,
|
|
368
|
+
complete: complete && bytesRead >= Math.min(observedBytes, MAX_OUTPUT_BYTES),
|
|
369
|
+
};
|
|
370
|
+
await handle.close();
|
|
371
|
+
handle = undefined;
|
|
372
|
+
return ok(captured);
|
|
163
373
|
}
|
|
164
|
-
catch {
|
|
165
|
-
|
|
374
|
+
catch (cause) {
|
|
375
|
+
const failure = cause instanceof Error ? cause.message : String(cause);
|
|
376
|
+
if (handle !== undefined) {
|
|
377
|
+
try {
|
|
378
|
+
await handle.close();
|
|
379
|
+
}
|
|
380
|
+
catch (closeCause) {
|
|
381
|
+
return err(`${failure}; closing capture file failed: ${closeCause instanceof Error ? closeCause.message : String(closeCause)}`);
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
return err(failure);
|
|
166
385
|
}
|
|
386
|
+
};
|
|
387
|
+
/** Supervise LLDB as an owned process group while observing its target PID identity. */
|
|
388
|
+
const runLldb = async (executable, arguments_, options) => {
|
|
389
|
+
if (options.signal?.aborted === true)
|
|
390
|
+
return {
|
|
391
|
+
kind: "cancelled",
|
|
392
|
+
targetIdentity: undefined,
|
|
393
|
+
cleanupFailure: undefined,
|
|
394
|
+
};
|
|
395
|
+
const deadline = new ProviderStartupDeadline(options.deadlineMs, options.signal);
|
|
396
|
+
let targetIdentity;
|
|
397
|
+
let supervisor;
|
|
167
398
|
try {
|
|
168
|
-
const
|
|
169
|
-
|
|
170
|
-
|
|
399
|
+
const launched = await spawnOwnedProviderProcess({
|
|
400
|
+
command: executable,
|
|
401
|
+
arguments: arguments_,
|
|
402
|
+
runId: randomUUID(),
|
|
403
|
+
expectedCommand: executable,
|
|
404
|
+
signal: deadline.signal,
|
|
405
|
+
});
|
|
406
|
+
supervisor = new ProviderProcessSupervisor({
|
|
407
|
+
...launched,
|
|
408
|
+
ownsProcessLifetime: true,
|
|
409
|
+
cleanup: () => cleanupOwnedProcessGroup(launched.ownership),
|
|
410
|
+
}, { maxDiagnosticBytes: DIAGNOSTIC_BYTES });
|
|
411
|
+
let identityAckWritten = false;
|
|
412
|
+
while (!(await supervisor.waitForOutputClose(20))) {
|
|
413
|
+
const pid = await readTargetPid(options.pidPath);
|
|
414
|
+
if (pid !== undefined && !identityAckWritten) {
|
|
415
|
+
let observation;
|
|
416
|
+
try {
|
|
417
|
+
observation = await observeProcessStartIdentity(pid);
|
|
418
|
+
}
|
|
419
|
+
catch {
|
|
420
|
+
observation = undefined;
|
|
421
|
+
}
|
|
422
|
+
if (observation?.state === "readable")
|
|
423
|
+
targetIdentity = observation.identity;
|
|
424
|
+
await writeFile(options.identityAckPath, observation?.state === "readable" ? "readable" : "unavailable", { mode: 0o600 });
|
|
425
|
+
identityAckWritten = true;
|
|
426
|
+
}
|
|
427
|
+
const interruption = deadline.interruption;
|
|
428
|
+
if (interruption !== undefined) {
|
|
429
|
+
const stopped = await supervisor.stop();
|
|
430
|
+
const cleanupFailure = stopped.status === "incomplete" ? stopped.reason : undefined;
|
|
431
|
+
return interruption === "cancelled"
|
|
432
|
+
? { kind: "cancelled", targetIdentity, cleanupFailure }
|
|
433
|
+
: { kind: "timeout", targetIdentity, cleanupFailure };
|
|
434
|
+
}
|
|
435
|
+
await waitForAbortableDelay(20, deadline.signal);
|
|
436
|
+
}
|
|
437
|
+
const snapshot = supervisor.snapshot();
|
|
438
|
+
const stopped = await supervisor.stop();
|
|
439
|
+
const cleanupFailure = stopped.status === "incomplete" ? stopped.reason : undefined;
|
|
440
|
+
const interruption = deadline.interruption;
|
|
441
|
+
if (interruption === "cancelled")
|
|
442
|
+
return { kind: "cancelled", targetIdentity, cleanupFailure };
|
|
443
|
+
if (interruption === "timeout")
|
|
444
|
+
return { kind: "timeout", targetIdentity, cleanupFailure };
|
|
171
445
|
return {
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
446
|
+
kind: "exited",
|
|
447
|
+
exitCode: snapshot.exitCode ?? null,
|
|
448
|
+
output: `${snapshot.stdout.text}${snapshot.stderr.text}`.slice(-DIAGNOSTIC_BYTES),
|
|
449
|
+
targetIdentity,
|
|
450
|
+
cleanupFailure,
|
|
175
451
|
};
|
|
176
452
|
}
|
|
453
|
+
catch (cause) {
|
|
454
|
+
const interruption = deadline.interruption;
|
|
455
|
+
const stopped = await supervisor?.stop();
|
|
456
|
+
const cleanupFailure = stopped?.status === "incomplete" ? stopped.reason : undefined;
|
|
457
|
+
return interruption === "timeout"
|
|
458
|
+
? { kind: "timeout", targetIdentity, cleanupFailure }
|
|
459
|
+
: interruption === "cancelled"
|
|
460
|
+
? { kind: "cancelled", targetIdentity, cleanupFailure }
|
|
461
|
+
: {
|
|
462
|
+
kind: "exited",
|
|
463
|
+
exitCode: null,
|
|
464
|
+
output: cause instanceof Error ? cause.message : String(cause),
|
|
465
|
+
targetIdentity,
|
|
466
|
+
cleanupFailure,
|
|
467
|
+
};
|
|
468
|
+
}
|
|
177
469
|
finally {
|
|
178
|
-
|
|
470
|
+
supervisor?.dispose();
|
|
471
|
+
deadline.dispose();
|
|
179
472
|
}
|
|
180
473
|
};
|
|
181
|
-
/** Run LLDB in its own process group so cancellation can stop it and debugserver. */
|
|
182
|
-
const runLldb = (executable, arguments_, options) => new Promise((resolve) => {
|
|
183
|
-
if (options.signal?.aborted === true) {
|
|
184
|
-
resolve({ kind: "cancelled" });
|
|
185
|
-
return;
|
|
186
|
-
}
|
|
187
|
-
const child = spawn(executable, [...arguments_], {
|
|
188
|
-
shell: false,
|
|
189
|
-
detached: true,
|
|
190
|
-
stdio: ["ignore", "pipe", "pipe"],
|
|
191
|
-
});
|
|
192
|
-
let output = "";
|
|
193
|
-
let stopped;
|
|
194
|
-
const keep = (chunk) => {
|
|
195
|
-
output = (output + chunk.toString("utf8")).slice(-DIAGNOSTIC_BYTES);
|
|
196
|
-
};
|
|
197
|
-
child.stdout.on("data", keep);
|
|
198
|
-
child.stderr.on("data", keep);
|
|
199
|
-
const kill = (reason) => {
|
|
200
|
-
stopped ??= reason;
|
|
201
|
-
if (child.pid !== undefined)
|
|
202
|
-
try {
|
|
203
|
-
process.kill(-child.pid, "SIGKILL");
|
|
204
|
-
}
|
|
205
|
-
catch {
|
|
206
|
-
// The group already exited.
|
|
207
|
-
}
|
|
208
|
-
};
|
|
209
|
-
const timer = setTimeout(() => {
|
|
210
|
-
kill("timeout");
|
|
211
|
-
}, options.deadlineMs);
|
|
212
|
-
const onAbort = () => {
|
|
213
|
-
kill("cancelled");
|
|
214
|
-
};
|
|
215
|
-
options.signal?.addEventListener("abort", onAbort, { once: true });
|
|
216
|
-
const finish = (exitCode) => {
|
|
217
|
-
clearTimeout(timer);
|
|
218
|
-
options.signal?.removeEventListener("abort", onAbort);
|
|
219
|
-
resolve(stopped === undefined
|
|
220
|
-
? { kind: "exited", exitCode, output }
|
|
221
|
-
: { kind: stopped });
|
|
222
|
-
};
|
|
223
|
-
child.on("error", () => {
|
|
224
|
-
finish(null);
|
|
225
|
-
});
|
|
226
|
-
child.on("close", (code) => {
|
|
227
|
-
finish(code);
|
|
228
|
-
});
|
|
229
|
-
});
|
|
230
474
|
/**
|
|
231
|
-
*
|
|
232
|
-
*
|
|
475
|
+
* Confirm target exit, and signal a survivor only when its OS start identity
|
|
476
|
+
* still matches the one captured while it was stopped at entry.
|
|
233
477
|
*/
|
|
234
|
-
const
|
|
235
|
-
let pid;
|
|
478
|
+
const readTargetPid = async (pidPath) => {
|
|
236
479
|
try {
|
|
237
|
-
pid = Number.parseInt(await readFile(pidPath, "utf8"), 10);
|
|
480
|
+
const pid = Number.parseInt(await readFile(pidPath, "utf8"), 10);
|
|
481
|
+
return Number.isSafeInteger(pid) && pid > 0 ? pid : undefined;
|
|
238
482
|
}
|
|
239
483
|
catch {
|
|
240
|
-
|
|
241
|
-
return true;
|
|
484
|
+
return undefined;
|
|
242
485
|
}
|
|
486
|
+
};
|
|
487
|
+
/** Only signal a surviving target when its captured OS start identity still matches. */
|
|
488
|
+
const ensureTerminated = async (pidPath, identity, missingPidIsUnknown) => {
|
|
489
|
+
const pid = await readTargetPid(pidPath);
|
|
490
|
+
if (pid === undefined)
|
|
491
|
+
return missingPidIsUnknown ? "identity-unavailable" : "terminated";
|
|
243
492
|
if (!Number.isSafeInteger(pid) || pid <= 0)
|
|
244
|
-
return
|
|
493
|
+
return "identity-unavailable";
|
|
494
|
+
let signalDisposition;
|
|
245
495
|
for (let attempt = 0; attempt < 20; attempt++) {
|
|
246
496
|
if (!alive(pid))
|
|
247
|
-
return
|
|
248
|
-
if (attempt === 10
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
497
|
+
return "terminated";
|
|
498
|
+
if (attempt === 10) {
|
|
499
|
+
if (identity === undefined)
|
|
500
|
+
signalDisposition = "identity-unavailable";
|
|
501
|
+
else {
|
|
502
|
+
const result = await signalProcessWithStartIdentity(pid, identity, "SIGKILL");
|
|
503
|
+
if (result === "gone")
|
|
504
|
+
return "terminated";
|
|
505
|
+
if (result === "identity-changed")
|
|
506
|
+
signalDisposition = "identity-changed";
|
|
507
|
+
else if (result === "unverified")
|
|
508
|
+
signalDisposition = "identity-unavailable";
|
|
509
|
+
else if (result === "signaled")
|
|
510
|
+
signalDisposition = "signal-did-not-stop";
|
|
254
511
|
}
|
|
512
|
+
}
|
|
255
513
|
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
256
514
|
}
|
|
257
|
-
return !alive(pid)
|
|
515
|
+
return !alive(pid)
|
|
516
|
+
? "terminated"
|
|
517
|
+
: (signalDisposition ?? "identity-unavailable");
|
|
518
|
+
};
|
|
519
|
+
const targetTerminationReason = (termination) => {
|
|
520
|
+
switch (termination) {
|
|
521
|
+
case "terminated":
|
|
522
|
+
return "target process terminated";
|
|
523
|
+
case "identity-unavailable":
|
|
524
|
+
return "target process start identity was unavailable; survivor was left untouched";
|
|
525
|
+
case "identity-changed":
|
|
526
|
+
return "target PID start identity changed; replacement process was left untouched";
|
|
527
|
+
case "signal-did-not-stop":
|
|
528
|
+
return "verified target did not exit after SIGKILL";
|
|
529
|
+
}
|
|
258
530
|
};
|
|
259
531
|
const alive = (pid) => {
|
|
260
532
|
try {
|
|
@@ -267,12 +539,3 @@ const alive = (pid) => {
|
|
|
267
539
|
cause.code === "ESRCH");
|
|
268
540
|
}
|
|
269
541
|
};
|
|
270
|
-
const commandOf = async (pid) => {
|
|
271
|
-
try {
|
|
272
|
-
const { stdout } = await execFileOutput("/bin/ps", ["-o", "comm=", "-p", String(pid)], { timeout: 5_000 });
|
|
273
|
-
return stdout.trim();
|
|
274
|
-
}
|
|
275
|
-
catch {
|
|
276
|
-
return undefined;
|
|
277
|
-
}
|
|
278
|
-
};
|