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.
Files changed (343) hide show
  1. package/README.md +23 -17
  2. package/bridge/android/ReaJadxBridge.java +14 -5
  3. package/bridge/ghidra/ReaGhidraBridge.java +173 -47
  4. package/bridge/hopper_bridge.py +310 -128
  5. package/bridge/mitmproxy/capture.py +0 -2
  6. package/bridge/native/rea_lldb_tracer.py +630 -94
  7. package/bridge/process/ProcessRunTokenReader.swift +39 -5
  8. package/bridge/pwntools/recorded_crash_impl.py +4 -0
  9. package/dist/android/JadxConfiguration.js +196 -33
  10. package/dist/android/JadxMcpTransport.js +4 -0
  11. package/dist/android/JadxProtocol.js +6 -0
  12. package/dist/android/JadxProvider.js +19 -2
  13. package/dist/android/JadxSession.js +23 -4
  14. package/dist/application/AnalysisContextQueries.js +8 -35
  15. package/dist/application/AnalysisDocument.js +32 -0
  16. package/dist/application/AppBundleExecutable.js +67 -21
  17. package/dist/application/CallPathTracing.js +19 -3
  18. package/dist/application/CapabilityInventory.js +6 -3
  19. package/dist/application/ClientConfigurationDocument.js +871 -6
  20. package/dist/application/ClientRegistrationIdentity.js +26 -5
  21. package/dist/application/ClientRegistrationStatus.js +29 -14
  22. package/dist/application/DirectAnalysis.js +27 -37
  23. package/dist/application/Doctor.js +4 -2
  24. package/dist/application/DoctorScope.js +25 -13
  25. package/dist/application/EnhancedLiteralTracing.js +22 -25
  26. package/dist/application/EnhancedTools.js +13 -11
  27. package/dist/application/EvidenceBundleFiles.js +23 -11
  28. package/dist/application/FunctionAnalysisEvidence.js +2 -3
  29. package/dist/application/InventoryProjectionEvidence.js +4 -3
  30. package/dist/application/InvestigationProviders.js +3 -2
  31. package/dist/application/JsonFiles.js +50 -28
  32. package/dist/application/ProcedureAddressResolution.js +11 -0
  33. package/dist/application/ReconstructionCoverageService.js +7 -4
  34. package/dist/application/ReconstructionObligationLedgerService.js +4 -3
  35. package/dist/application/Setup.js +13 -1
  36. package/dist/application/SetupClientConfiguration.js +10 -15
  37. package/dist/application/SetupHost.js +5 -2
  38. package/dist/application/SetupPlan.js +5 -3
  39. package/dist/application/SupportedClients.js +34 -1
  40. package/dist/application/Uninstall.js +4 -3
  41. package/dist/application/Update.js +18 -3
  42. package/dist/application/UpdateRuntime.js +46 -25
  43. package/dist/application/WebModuleTraceService.js +4 -2
  44. package/dist/application/WebNetworkCaptureService.js +4 -2
  45. package/dist/application/WebRuntimeService.js +5 -3
  46. package/dist/application/WebScriptExportService.js +2 -2
  47. package/dist/application/WebSourceLocationService.js +4 -2
  48. package/dist/application/WorkflowEvidence.js +82 -0
  49. package/dist/application/binary/AnalysisProviderEvaluation.js +21 -5
  50. package/dist/application/binary/AnalysisProviderRegistry.js +2 -0
  51. package/dist/application/binary/AnalysisSnapshotCache.js +3 -6
  52. package/dist/application/binary/BinarySession.js +95 -68
  53. package/dist/application/binary/BinarySessionOpen.js +3 -3
  54. package/dist/application/binary/BinarySessionRecords.js +25 -3
  55. package/dist/application/binary/BinarySessionStatus.js +17 -1
  56. package/dist/application/binary/CompositeProvider.js +13 -0
  57. package/dist/application/binary/LazyAnalysisProvider.js +3 -0
  58. package/dist/application/binaryDiagnostics/BinaryLayoutService.js +4 -2
  59. package/dist/application/binaryDiagnostics/RecordedCrashService.js +4 -2
  60. package/dist/application/evm/EvmInterfaceService.js +4 -2
  61. package/dist/application/investigation/EvidenceLedger.js +21 -7
  62. package/dist/application/investigation/InvestigationRecords.js +8 -0
  63. package/dist/application/javascript/JavaScriptAnalysisControl.js +34 -0
  64. package/dist/application/javascript/JavaScriptApplicationEvidence.js +24 -3
  65. package/dist/application/javascript/JavaScriptApplicationEvidenceGraph.js +19 -2
  66. package/dist/application/javascript/JavaScriptApplicationService.js +22 -15
  67. package/dist/application/javascript/JavaScriptApplicationWorkflowService.js +5 -5
  68. package/dist/application/javascript/JavaScriptArtifactAnalysis.js +5 -4
  69. package/dist/application/javascript/JavaScriptArtifactGraphBuilder.js +39 -7
  70. package/dist/application/javascript/JavaScriptArtifactPathResolution.js +6 -3
  71. package/dist/application/javascript/JavaScriptArtifactReconstruction.js +11 -3
  72. package/dist/application/javascript/JavaScriptModuleRelationships.js +37 -8
  73. package/dist/application/javascript/JavaScriptRecoveryService.js +4 -2
  74. package/dist/application/javascript/JavaScriptRuntimeObservationService.js +3 -1
  75. package/dist/application/javascript/JavaScriptRuntimeReconciliationService.js +3 -3
  76. package/dist/application/javascript/JavaScriptSemanticGraphBuilder.js +63 -61
  77. package/dist/application/javascript/JavaScriptSemanticGraphValueProjection.js +23 -1
  78. package/dist/application/managed/ManagedApplicationGraphService.js +4 -3
  79. package/dist/application/managed/ManagedMemberComparisonService.js +7 -4
  80. package/dist/application/managed/ManagedNativeVerificationService.js +4 -3
  81. package/dist/application/managed/ManagedReconstructionService.js +4 -3
  82. package/dist/application/process/ProcessCli.js +14 -6
  83. package/dist/application/process/ProcessEvidence.js +4 -3
  84. package/dist/application/workflowInputError.js +18 -0
  85. package/dist/artifacts/ArtifactProvider.js +12 -2
  86. package/dist/artifacts/MachOSliceArtifactReader.js +98 -14
  87. package/dist/artifacts/NativeDmgArtifactReader.js +13 -6
  88. package/dist/artifacts/apple/AppleAssetCatalogAnalysis.js +22 -3
  89. package/dist/artifacts/apple/InterfaceBuilderAnalysis.js +176 -56
  90. package/dist/artifacts/apple/InterfaceBuilderDecodeBudget.js +195 -0
  91. package/dist/artifacts/apple/KeyedArchiveReader.js +36 -22
  92. package/dist/artifacts/apple/MachoLoadCommandReader.js +92 -31
  93. package/dist/artifacts/apple/NibArchive.js +81 -14
  94. package/dist/artifacts/apple/NibViewHierarchy.js +10 -3
  95. package/dist/artifacts/apple/PlistNumberLiterals.js +164 -0
  96. package/dist/artifacts/inventory/ArtifactGraphConstruction.js +13 -8
  97. package/dist/artifacts/inventory/scanCanonical.js +11 -14
  98. package/dist/artifacts/javascript/JavaScriptArtifactFiles.js +6 -2
  99. package/dist/browser/CdpCaptureCompleteness.js +2 -0
  100. package/dist/browser/CdpCaptureEventHandlers.js +76 -11
  101. package/dist/browser/CdpCaptureEventTypes.js +5 -1
  102. package/dist/browser/CdpCaptureEvents.js +25 -0
  103. package/dist/browser/CdpConnection.js +30 -11
  104. package/dist/browser/CdpOptionalCommand.js +2 -3
  105. package/dist/browser/CdpPageCapture.js +23 -1
  106. package/dist/browser/PlaywrightElectronActiveActions.js +32 -23
  107. package/dist/browser/PlaywrightElectronActiveProjection.js +141 -0
  108. package/dist/browser/PlaywrightElectronActiveProvider.js +123 -124
  109. package/dist/browser/WebSourceMapFetcher.js +426 -72
  110. package/dist/browser/assets/LocalWebScriptArtifacts.js +2 -2
  111. package/dist/browser/history/CaptureFailures.js +37 -1
  112. package/dist/browser/history/CaptureRelease.js +5 -0
  113. package/dist/browser/history/HarCaptureAdapter.js +3 -3
  114. package/dist/browser/history/HarJson.js +3 -2
  115. package/dist/browser/history/HistoricalCaptureDecoder.js +4 -2
  116. package/dist/cli/analysisSelector.js +23 -0
  117. package/dist/cli/applicationCommands.js +2 -3
  118. package/dist/cli/coreBinaryCommands.js +70 -12
  119. package/dist/cli/electronCommands.js +7 -6
  120. package/dist/cli/javascriptApplicationAnalysis.js +7 -3
  121. package/dist/cli/jsonOutput.js +4 -99
  122. package/dist/cli/nativeCallCommands.js +2 -3
  123. package/dist/cli/webRuntimeCommands.js +8 -8
  124. package/dist/cli.js +2 -1
  125. package/dist/cliCommandNames.js +0 -1
  126. package/dist/cliJsonInput.js +9 -4
  127. package/dist/config/environment.js +4 -0
  128. package/dist/config/ghidraStartupTimeout.js +17 -0
  129. package/dist/config/mcpResponseBudget.js +22 -0
  130. package/dist/config/parseConfig.js +2 -1
  131. package/dist/contracts/enhancedInputs.js +5 -3
  132. package/dist/contracts/enhancedToolContracts.js +3 -3
  133. package/dist/contracts/hopperMemoryContracts.js +2 -5
  134. package/dist/contracts/javascript/electronToolContracts.js +1 -1
  135. package/dist/contracts/managed/managedToolContracts.js +2 -4
  136. package/dist/contracts/native/nativeToolContracts.js +4 -2
  137. package/dist/contracts/officialToolContracts.js +14 -15
  138. package/dist/contracts/promptContracts.js +3 -3
  139. package/dist/contracts/sessionLifecycleInputs.js +3 -7
  140. package/dist/contracts/sessionStatusContract.js +2 -4
  141. package/dist/contracts/sessionToolContracts.js +4 -6
  142. package/dist/contracts/sessionToolSchemas.js +2 -5
  143. package/dist/contracts/toolContractExamples.js +0 -1
  144. package/dist/contracts/toolContractHelpers.js +4 -1
  145. package/dist/contracts/toolEffects.js +0 -1
  146. package/dist/contracts/toolOutputSchemaGroups.js +28 -27
  147. package/dist/contracts/toolOutputSchemaPrimitives.js +18 -0
  148. package/dist/contracts/toolSchemaMetadata.js +69 -10
  149. package/dist/contracts/webNetworkCaptureToolContracts.js +1 -1
  150. package/dist/domain/analysisErrorBase.js +8 -3
  151. package/dist/domain/analysisErrorCore.js +3 -1
  152. package/dist/domain/analysisErrorPresentation.js +72 -17
  153. package/dist/domain/analysisErrorProjection.js +106 -27
  154. package/dist/domain/android/androidAnalysis.js +2 -4
  155. package/dist/domain/android/androidApplication.js +17 -9
  156. package/dist/domain/apple/appleApplication.js +41 -14
  157. package/dist/domain/apple/dyldEnvironment.js +73 -0
  158. package/dist/domain/apple/dylibResolution.js +120 -9
  159. package/dist/domain/apple/dylibResolutionFindings.js +9 -7
  160. package/dist/domain/apple/interfaceBuilderGraph.js +25 -13
  161. package/dist/domain/apple/interfaceBuilderKeyedArchive.js +13 -3
  162. package/dist/domain/apple/keyedArchive.js +9 -1
  163. package/dist/domain/apple/machoContainer.js +54 -0
  164. package/dist/domain/apple/plistNumbers.js +76 -0
  165. package/dist/domain/artifactIdentity.js +29 -0
  166. package/dist/domain/artifactInventoryEvidence.js +19 -24
  167. package/dist/domain/bridgeCandidateProjection.js +71 -0
  168. package/dist/domain/browserCompleteness.js +1 -0
  169. package/dist/domain/browserObservationError.js +24 -5
  170. package/dist/domain/browserScenarioValues.js +2 -6
  171. package/dist/domain/bundleComparison.js +3 -2
  172. package/dist/domain/canonicalJsonDigestSteps.js +133 -0
  173. package/dist/domain/canonicalOrdering.js +9 -5
  174. package/dist/domain/changedBehavior.js +6 -6
  175. package/dist/domain/comparisonSourceEvidence.js +40 -0
  176. package/dist/domain/configurationErrors.js +1 -1
  177. package/dist/domain/evidence.js +64 -9
  178. package/dist/domain/evidenceBundle.js +42 -1
  179. package/dist/domain/evidenceErrors.js +12 -1
  180. package/dist/domain/explicitSensitiveFailure.js +17 -1
  181. package/dist/domain/firmware/firmwareAnalysis.js +6 -7
  182. package/dist/domain/hopperErrors.js +23 -2
  183. package/dist/domain/hopperValues.js +51 -8
  184. package/dist/domain/immutableJson.js +114 -0
  185. package/dist/domain/inputIssueProjection.js +3 -0
  186. package/dist/domain/javascript/electronActiveObservation.js +30 -4
  187. package/dist/domain/javascript/electronStaticAnalysisValues.js +6 -12
  188. package/dist/domain/javascript/javascriptApplicationAnalysis.js +71 -23
  189. package/dist/domain/javascript/javascriptApplicationGraph.js +13 -0
  190. package/dist/domain/javascript/javascriptAstValues.js +20 -0
  191. package/dist/domain/javascript/javascriptRecovery.js +3 -8
  192. package/dist/domain/javascript/javascriptSemanticAnalysis.js +97 -2
  193. package/dist/domain/javascript/javascriptSemanticCoverage.js +6 -5
  194. package/dist/domain/javascript/javascriptSemanticDataEffectHelpers.js +9 -17
  195. package/dist/domain/javascript/javascriptSemanticDataEffects.js +22 -5
  196. package/dist/domain/javascript/javascriptSemanticGraph.js +13 -0
  197. package/dist/domain/javascript/javascriptSemanticGraphSchemas.js +1 -0
  198. package/dist/domain/javascript/javascriptSemanticPrimitives.js +25 -17
  199. package/dist/domain/javascript/javascriptSemanticQuerySchemas.js +8 -4
  200. package/dist/domain/javascript/javascriptSemanticResourceLimits.js +77 -0
  201. package/dist/domain/javascript/javascriptSemanticValues.js +83 -11
  202. package/dist/domain/javascript/javascriptSourceParser.js +6 -2
  203. package/dist/domain/javascript/javascriptStaticAnalysis.js +22 -17
  204. package/dist/domain/javascript/javascriptStaticAnalysisBundler.js +5 -3
  205. package/dist/domain/javascript/javascriptStaticAnalysisCalls.js +14 -4
  206. package/dist/domain/javascript/javascriptStaticAnalysisFindings.js +66 -7
  207. package/dist/domain/javascript/javascriptStaticAnalysisHelpers.js +28 -16
  208. package/dist/domain/javascript/javascriptStaticAnalysisState.js +3 -2
  209. package/dist/domain/jsonSerialization.js +109 -0
  210. package/dist/domain/jsonValue.js +90 -6
  211. package/dist/domain/localPath.js +6 -0
  212. package/dist/domain/managed/managedApplicationGraph.js +10 -10
  213. package/dist/domain/managed/managedInspectionEvidence.js +16 -0
  214. package/dist/domain/managed/managedMemberComparisonMatch.js +5 -9
  215. package/dist/domain/managed/managedNativeVerification.js +3 -9
  216. package/dist/domain/native/fileOffsetPartialObservation.js +1 -0
  217. package/dist/domain/native/nativeCallObservation.js +33 -11
  218. package/dist/domain/native/nativeCallPartialObservation.js +33 -0
  219. package/dist/domain/process/processCapture.js +8 -4
  220. package/dist/domain/process/processComparison.js +38 -4
  221. package/dist/domain/process/processScenario.js +11 -10
  222. package/dist/domain/propertyListKeys.js +1 -1
  223. package/dist/domain/propertyListXmlText.js +19 -0
  224. package/dist/domain/providerAdapterError.js +1 -1
  225. package/dist/domain/providerOperationHealth.js +54 -0
  226. package/dist/domain/reconstructionVerification.js +6 -43
  227. package/dist/domain/reconstructionVerificationSchemas.js +2 -2
  228. package/dist/domain/staticRuntimeCorrelation.js +6 -43
  229. package/dist/domain/webBundleAnalysis.js +20 -8
  230. package/dist/domain/webBundleAnalyzerInspection.js +13 -8
  231. package/dist/domain/webBundleAnalyzerResult.js +41 -2
  232. package/dist/domain/webRuntime.js +19 -5
  233. package/dist/domain/webSourceLocation.js +1 -0
  234. package/dist/firmware/FirmwareFiles.js +1 -1
  235. package/dist/generatedPackageMetadata.js +1 -1
  236. package/dist/ghidra/GhidraAnalysisProfile.js +5 -0
  237. package/dist/ghidra/GhidraClient.js +47 -14
  238. package/dist/ghidra/GhidraDiagnostics.js +7 -7
  239. package/dist/ghidra/GhidraFunctionValues.js +10 -7
  240. package/dist/ghidra/GhidraInstallation.js +18 -21
  241. package/dist/ghidra/GhidraInventoryValues.js +14 -4
  242. package/dist/ghidra/GhidraJavaLaunch.js +62 -0
  243. package/dist/ghidra/GhidraLauncher.js +33 -16
  244. package/dist/ghidra/GhidraProviderCapabilities.js +14 -2
  245. package/dist/ghidra/GhidraProviderClient.js +23 -12
  246. package/dist/ghidra/GhidraSessionError.js +11 -7
  247. package/dist/ghidra/GhidraTargetSnapshot.js +47 -12
  248. package/dist/ghidra/GhidraTransport.js +20 -0
  249. package/dist/hopper/BridgeLauncher.js +31 -11
  250. package/dist/hopper/HopperAnalysisProfile.js +62 -4
  251. package/dist/hopper/HopperCleanup.js +15 -3
  252. package/dist/hopper/HopperClient.js +222 -14
  253. package/dist/hopper/HopperFileOffset.js +237 -0
  254. package/dist/hopper/HopperMachOImage.js +196 -0
  255. package/dist/hopper/HopperOperationStage.js +6 -0
  256. package/dist/hopper/HopperProvider.js +63 -9
  257. package/dist/hopper/HopperProviderCapabilities.js +19 -1
  258. package/dist/hopper/HopperRegexSearch.js +137 -0
  259. package/dist/hopper/HopperRegexWorker.js +27 -0
  260. package/dist/hopper/HopperRequestQueue.js +68 -19
  261. package/dist/hopper/HopperTargetLease.js +3 -2
  262. package/dist/inspector/JavaScriptRuntimeScope.js +9 -1
  263. package/dist/inspector/V8InspectorCaptureProjection.js +8 -3
  264. package/dist/inspector/V8InspectorProvider.js +47 -11
  265. package/dist/javascript/sourceMaps/DecodedSourceMap.js +47 -0
  266. package/dist/javascript/sourceMaps/SourceMapCommand.js +16 -0
  267. package/dist/javascript/sourceMaps/SourceMapFormat.js +79 -31
  268. package/dist/javascript/sourceMaps/TraceSourceMap.js +14 -28
  269. package/dist/mcpDoctor.js +62 -2
  270. package/dist/native/AppleDispatchCoverage.js +47 -6
  271. package/dist/native/AppleDispatchDecodeFacts.js +6 -0
  272. package/dist/native/AppleDispatchMetadata.js +6 -3
  273. package/dist/native/AppleMachoFixups.js +48 -24
  274. package/dist/native/AppleMachoSelection.js +32 -16
  275. package/dist/native/AppleObjcCategories.js +6 -2
  276. package/dist/native/AppleObjcDispatchFacets.js +5 -2
  277. package/dist/native/AppleObjcProperties.js +2 -1
  278. package/dist/native/AppleSwiftDispatchFacets.js +27 -21
  279. package/dist/native/AppleSwiftVtables.js +21 -15
  280. package/dist/native/CommandRunner.js +81 -84
  281. package/dist/native/LldbCallTracer.js +365 -102
  282. package/dist/native/LldbRetainedObservations.js +178 -0
  283. package/dist/native/NativeCallObservation.js +62 -3
  284. package/dist/native/NativeMacOSProvider.js +98 -17
  285. package/dist/native/SignatureTargetBinding.js +155 -0
  286. package/dist/native/parsers/lipo.js +71 -1
  287. package/dist/native/pwndbg/PwndbgCoreContext.js +25 -5
  288. package/dist/native/pwntools/RecordedCrashStructureBindings.js +2 -0
  289. package/dist/process/DarwinProcessRunTokenReader.js +8 -9
  290. package/dist/process/ExecFileOutput.js +20 -0
  291. package/dist/process/OwnedCommand.js +7 -2
  292. package/dist/process/ProcessOwnership.js +203 -53
  293. package/dist/process/ProcessOwnershipObservation.js +49 -4
  294. package/dist/process/ProviderProcess.js +38 -7
  295. package/dist/process/capture/ProcessCaptureError.js +2 -2
  296. package/dist/process/capture/ProcessCaptureLifecycle.js +100 -34
  297. package/dist/server/EvidenceMcpServer.js +146 -0
  298. package/dist/server/createServer.js +15 -8
  299. package/dist/server/registerAndroidTools.js +2 -4
  300. package/dist/server/registerApplicationTools/helpers.js +2 -4
  301. package/dist/server/registerArtifactComparisonTool.js +11 -7
  302. package/dist/server/registerBinaryDiagnosticsTools.js +2 -4
  303. package/dist/server/registerBrowserScenarioTool.js +2 -4
  304. package/dist/server/registerBrowserTools.js +2 -4
  305. package/dist/server/registerBundleComparisonTool.js +2 -3
  306. package/dist/server/registerCloseLifecycleTool.js +8 -18
  307. package/dist/server/registerElectronTools.js +2 -4
  308. package/dist/server/registerEnhancedTools.js +18 -59
  309. package/dist/server/registerEvidenceTools.js +2 -4
  310. package/dist/server/registerEvmTools.js +2 -4
  311. package/dist/server/registerFirmwareTools.js +2 -4
  312. package/dist/server/registerFunctionComparisonTool.js +10 -6
  313. package/dist/server/registerInvestigationTools.js +7 -7
  314. package/dist/server/registerJavaScriptRecoveryTool.js +2 -4
  315. package/dist/server/registerJavaScriptRuntimeObservationTools.js +2 -4
  316. package/dist/server/registerManagedWorkflowTools/compareManagedMembers.js +2 -4
  317. package/dist/server/registerManagedWorkflowTools/importManagedReconstruction.js +2 -4
  318. package/dist/server/registerManagedWorkflowTools/projectManagedApplicationGraph.js +2 -4
  319. package/dist/server/registerManagedWorkflowTools/verifyManagedNativeBoundaries.js +2 -4
  320. package/dist/server/registerOfficialTools.js +2 -4
  321. package/dist/server/registerProcessComparisonTool.js +71 -45
  322. package/dist/server/registerRecordedCrashTools.js +2 -4
  323. package/dist/server/registerSessionRecordTools.js +5 -6
  324. package/dist/server/registerSessionStatusTool.js +17 -3
  325. package/dist/server/registerSessionTools.js +11 -3
  326. package/dist/server/registerWebModuleTool.js +2 -4
  327. package/dist/server/registerWebNetworkCaptureTool.js +2 -4
  328. package/dist/server/registerWebRuntimeTools.js +3 -7
  329. package/dist/server/registerWebScriptTool.js +2 -4
  330. package/dist/server/registerWebSourceLocationTool.js +2 -4
  331. package/dist/server/runDerivedOperation.js +15 -3
  332. package/dist/server/sessionAvailabilityPolicy.js +0 -1
  333. package/dist/server/toolRegistrationOptions.js +2 -2
  334. package/dist/server/toolResult.js +62 -10
  335. package/dist/server/toolResultEncoding.js +33 -0
  336. package/install.sh +1 -1
  337. package/native/windows/build/manifest.json +1 -1
  338. package/package.json +8 -3
  339. package/scripts/electron-active-hook-boundaries.cjs +18 -20
  340. package/scripts/electron-active-hook.cjs +241 -37
  341. package/skills/reverse-engineer-anything/SKILL.md +11 -3
  342. package/skills/reverse-engineer-anything/references/javascript-applications.md +8 -1
  343. package/dist/ghidra/GhidraDefaults.js +0 -2
@@ -1,16 +1,21 @@
1
- import { spawn } from "node:child_process";
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 { execFileOutput } from "../process/ExecFileOutput.js";
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(["exited", "duration-elapsed", "event-limit", "stop-limit"]),
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(["target-error", "launch-error", "tracer-error"]),
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 resolveXcrunTool("lldb", signal);
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
- const exited = await runLldb(lldb.path, [
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 terminated = await ensureTerminated(paths.pid, request.executable);
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
- const output = await readTracerOutput(paths.result);
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
- if (output.status !== "traced")
120
- return err(tracerFailure(output));
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: await capturedOutput(paths.stdout),
125
- stderr: await capturedOutput(paths.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
- const tracerFailure = (output) => {
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
- return { text: "", bytes: 0, truncated: false };
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 { size } = await handle.stat();
169
- const buffer = Buffer.alloc(Math.min(size, MAX_OUTPUT_BYTES));
170
- const { bytesRead } = await handle.read(buffer, 0, buffer.length, 0);
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
- text: buffer.subarray(0, bytesRead).toString("utf8"),
173
- bytes: size,
174
- truncated: size > MAX_OUTPUT_BYTES,
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
- await handle.close();
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
- * debugserver kills the traced process when LLDB exits. Confirm it, and kill a
232
- * survivor only when its executable is still the traced one.
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 ensureTerminated = async (pidPath, executable) => {
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
- // LLDB never launched the target.
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 false;
493
+ return "identity-unavailable";
494
+ let signalDisposition;
245
495
  for (let attempt = 0; attempt < 20; attempt++) {
246
496
  if (!alive(pid))
247
- return true;
248
- if (attempt === 10 && (await commandOf(pid)) === executable)
249
- try {
250
- process.kill(pid, "SIGKILL");
251
- }
252
- catch {
253
- // It exited between the checks.
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
- };