rea-agents 5.0.0 → 6.0.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 (275) hide show
  1. package/README.md +157 -650
  2. package/bridge/mitmproxy/capture.py +210 -0
  3. package/bridge/native/rea_lldb_tracer.py +303 -0
  4. package/bridge/process/ProcessRunTokenReader.swift +263 -0
  5. package/bridge/process/ProcessRunTokenReaderMain.swift +35 -0
  6. package/bridge/pwndbg/core_context.py +101 -0
  7. package/bridge/pwndbg/launch.py +39 -0
  8. package/bridge/pwndbg/launch_impl.py +34 -0
  9. package/bridge/pwntools/decoder_runtime.py +59 -0
  10. package/bridge/pwntools/layout.py +39 -0
  11. package/bridge/pwntools/layout_impl.py +309 -0
  12. package/bridge/pwntools/recorded_crash.py +39 -0
  13. package/bridge/pwntools/recorded_crash_impl.py +143 -0
  14. package/dist/android/JadxProvider.js +1 -0
  15. package/dist/application/CapabilityInventory.js +21 -0
  16. package/dist/application/DirectAnalysis.js +97 -23
  17. package/dist/application/EvidenceBundleFiles.js +1 -1
  18. package/dist/application/ReconstructionObligationCandidates.js +5 -1
  19. package/dist/application/WebNetworkCapturePort.js +1 -0
  20. package/dist/application/WebNetworkCaptureService.js +132 -0
  21. package/dist/application/WindowsCapabilities.js +1 -1
  22. package/dist/application/binary/AnalysisSnapshotCache.js +49 -6
  23. package/dist/application/binary/BinarySession.js +38 -10
  24. package/dist/application/binary/BinarySessionOpen.js +16 -3
  25. package/dist/application/binary/BinarySessionRecords.js +20 -0
  26. package/dist/application/binaryDiagnostics/BinaryLayoutPort.js +1 -0
  27. package/dist/application/binaryDiagnostics/BinaryLayoutService.js +57 -0
  28. package/dist/application/binaryDiagnostics/RecordedCrashPort.js +1 -0
  29. package/dist/application/binaryDiagnostics/RecordedCrashService.js +60 -0
  30. package/dist/application/evm/EvmInterfacePort.js +1 -0
  31. package/dist/application/evm/EvmInterfaceService.js +62 -0
  32. package/dist/application/javascript/JavaScriptApplicationEvidence.js +1 -2
  33. package/dist/application/javascript/JavaScriptApplicationService.js +15 -2
  34. package/dist/application/javascript/JavaScriptArtifactAnalysis.js +42 -23
  35. package/dist/application/javascript/JavaScriptArtifactGraphDocuments.js +26 -2
  36. package/dist/application/javascript/JavaScriptArtifactPathResolution.js +74 -24
  37. package/dist/application/javascript/JavaScriptArtifactReconstruction.js +23 -10
  38. package/dist/application/javascript/JavaScriptSemanticGraphAsyncProjection.js +7 -7
  39. package/dist/application/javascript/JavaScriptSemanticGraphBuilder.js +145 -70
  40. package/dist/application/javascript/JavaScriptSemanticGraphChildProcessProjection.js +14 -15
  41. package/dist/application/javascript/JavaScriptSemanticGraphConstruction.js +41 -1
  42. package/dist/application/javascript/JavaScriptSemanticGraphDataProjection.js +7 -7
  43. package/dist/application/javascript/JavaScriptSemanticGraphFlowProjection.js +5 -9
  44. package/dist/application/javascript/JavaScriptSemanticGraphObjectProjection.js +3 -3
  45. package/dist/application/javascript/JavaScriptSemanticGraphProjection.js +118 -12
  46. package/dist/application/javascript/JavaScriptSemanticGraphResourceProjection.js +3 -3
  47. package/dist/application/javascript/JavaScriptSemanticGraphValueProjection.js +5 -5
  48. package/dist/application/{ProcessCli.js → process/ProcessCli.js} +10 -10
  49. package/dist/application/{ProcessEvidence.js → process/ProcessEvidence.js} +4 -4
  50. package/dist/artifacts/ArtifactProvider.js +30 -3
  51. package/dist/artifacts/ArtifactProviderMetadata.js +2 -1
  52. package/dist/artifacts/ZipArtifactReader.js +3 -0
  53. package/dist/artifacts/apple/DylibResolutionReader.js +259 -0
  54. package/dist/artifacts/apple/MachoLoadCommandReader.js +294 -0
  55. package/dist/{application → artifacts/extraction}/ArtifactExtraction.js +10 -10
  56. package/dist/{application → artifacts/inventory}/ArtifactGraphConstruction.js +3 -3
  57. package/dist/{application → artifacts/inventory}/ArtifactInventory.js +4 -4
  58. package/dist/{application/ArtifactInventory → artifacts/inventory}/classify.js +2 -2
  59. package/dist/{application/ArtifactInventory → artifacts/inventory}/reader.js +5 -5
  60. package/dist/{application/ArtifactInventory → artifacts/inventory}/scanCanonical.js +3 -3
  61. package/dist/{application/ArtifactInventory → artifacts/inventory}/scanReader.js +5 -5
  62. package/dist/artifacts/readStableArtifact.js +20 -4
  63. package/dist/browser/CdpCaptureEventHandlers.js +90 -2
  64. package/dist/browser/CdpCaptureEvents.js +9 -0
  65. package/dist/browser/PlaywrightElectronActiveProvider.js +12 -3
  66. package/dist/browser/history/CaptureFailures.js +101 -0
  67. package/dist/browser/history/CaptureFormatError.js +10 -0
  68. package/dist/browser/history/CaptureRedaction.js +65 -0
  69. package/dist/browser/history/CaptureRelease.js +12 -0
  70. package/dist/browser/history/HarCapture.js +165 -0
  71. package/dist/browser/history/HarCaptureAdapter.js +20 -0
  72. package/dist/browser/history/HarCaptureProcess.js +57 -0
  73. package/dist/browser/history/HarJson.js +91 -0
  74. package/dist/browser/history/HarSchema.js +72 -0
  75. package/dist/browser/history/HistoricalCaptureDecoder.js +186 -0
  76. package/dist/browser/history/HistoricalCaptureFormatAdapter.js +1 -0
  77. package/dist/browser/history/MitmproxyCaptureAdapter.js +42 -0
  78. package/dist/cli/artifactCommands.js +26 -1
  79. package/dist/cli/binaryDiagnosticsCommands.js +39 -0
  80. package/dist/cli/coreAnalysisCommands.js +2 -2
  81. package/dist/cli/coreBinaryCommands.js +8 -8
  82. package/dist/cli/electronCommands.js +16 -9
  83. package/dist/cli/evmCommands.js +24 -0
  84. package/dist/cli/firmwareCommands.js +6 -3
  85. package/dist/cli/javascriptApplicationAnalysis.js +16 -3
  86. package/dist/cli/jsonOutput.js +133 -0
  87. package/dist/cli/nativeCallCommands.js +35 -0
  88. package/dist/cli/processCommands.js +1 -1
  89. package/dist/cli/streamedJsonOutput.js +56 -0
  90. package/dist/cli/utilityCommands.js +2 -0
  91. package/dist/cli/webNetworkCaptureCommands.js +40 -0
  92. package/dist/cli.js +9 -3
  93. package/dist/cliBrowserScenarioCommands.js +2 -2
  94. package/dist/cliCommandNames.js +6 -0
  95. package/dist/cliJsonInput.js +30 -1
  96. package/dist/cliOutput.js +21 -6
  97. package/dist/composition/binaryDiagnostics.js +8 -0
  98. package/dist/composition/evm.js +4 -0
  99. package/dist/composition/webNetworkCaptures.js +6 -0
  100. package/dist/contracts/artifactToolContracts.js +6 -0
  101. package/dist/contracts/browserCaptureToolInputSchema.js +43 -0
  102. package/dist/contracts/browserToolContracts.js +4 -3
  103. package/dist/contracts/errorSchemas.js +8 -1
  104. package/dist/contracts/evm/evmToolContracts.js +20 -0
  105. package/dist/contracts/investigationExamples.js +2 -2
  106. package/dist/contracts/managed/managedToolContracts.js +7 -3
  107. package/dist/contracts/native/binaryDiagnosticsToolContracts.js +42 -0
  108. package/dist/contracts/native/nativeToolContracts.js +17 -1
  109. package/dist/contracts/officialToolContracts.js +12 -9
  110. package/dist/contracts/process/processCaptureExample.js +2 -0
  111. package/dist/contracts/sessionLifecycleInputs.js +22 -5
  112. package/dist/contracts/sessionStatusContract.js +10 -2
  113. package/dist/contracts/sessionToolContracts.js +8 -1
  114. package/dist/contracts/sessionToolSchemas.js +10 -3
  115. package/dist/contracts/toolContractExamples.js +1 -1
  116. package/dist/contracts/toolContracts.js +6 -0
  117. package/dist/contracts/toolEffects.js +30 -0
  118. package/dist/contracts/toolOutputSchemaGroups.js +5 -1
  119. package/dist/contracts/toolOutputSchemaPrimitives.js +1 -1
  120. package/dist/contracts/webNetworkCaptureToolContracts.js +24 -0
  121. package/dist/domain/analysisErrorBase.js +15 -0
  122. package/dist/domain/analysisErrorCore.js +59 -5
  123. package/dist/domain/analysisErrorPresentation.js +29 -1
  124. package/dist/domain/analysisErrorProjection.js +52 -2
  125. package/dist/domain/analysisSnapshot.js +166 -2
  126. package/dist/domain/apple/dyldPaths.js +81 -0
  127. package/dist/domain/apple/dylibResolution.js +367 -0
  128. package/dist/domain/apple/dylibResolutionFindings.js +75 -0
  129. package/dist/domain/artifactPathSyntax.js +1 -1
  130. package/dist/domain/browserObservation.js +1 -1
  131. package/dist/domain/browserObservationSchemas.js +13 -0
  132. package/dist/domain/browserScenarioValues.js +9 -2
  133. package/dist/domain/callPathSchemas.js +1 -1
  134. package/dist/domain/canonicalDigest.js +13 -1
  135. package/dist/domain/changedBehavior.js +3 -3
  136. package/dist/domain/comparisonStatus.js +2 -30
  137. package/dist/domain/conformanceTrustGate.js +0 -16
  138. package/dist/domain/evidence.js +17 -5
  139. package/dist/domain/evm/evmInterface.js +63 -0
  140. package/dist/domain/explicitSensitiveFailure.js +92 -0
  141. package/dist/domain/explicitSensitiveValues.js +10 -0
  142. package/dist/domain/firmware/firmwareAnalysis.js +8 -1
  143. package/dist/domain/javascript/electronActiveObservation.js +9 -4
  144. package/dist/domain/javascript/htmlArtifactReferences.js +40 -0
  145. package/dist/domain/javascript/javascriptExportShapeComparison.js +21 -5
  146. package/dist/domain/javascript/javascriptExportShapeVariants.js +0 -10
  147. package/dist/domain/javascript/javascriptSemanticGraph.js +21 -5
  148. package/dist/domain/jsonValue.js +4 -0
  149. package/dist/domain/localPath.js +2 -0
  150. package/dist/domain/native/binaryLayout.js +357 -0
  151. package/dist/domain/native/nativeCallObservation.js +164 -0
  152. package/dist/domain/native/objcSwiftMetadata.js +0 -39
  153. package/dist/domain/native/recordedCrash.js +266 -0
  154. package/dist/domain/native/recordedCrashNoteCoverage.js +73 -0
  155. package/dist/domain/{processCapture.js → process/processCapture.js} +139 -2
  156. package/dist/domain/{processCaptureExample.js → process/processCaptureExample.js} +5 -0
  157. package/dist/domain/process/processCaptureParsing.js +54 -0
  158. package/dist/domain/{processCaptureValidation.js → process/processCaptureValidation.js} +18 -2
  159. package/dist/domain/{processComparison.js → process/processComparison.js} +1 -1
  160. package/dist/domain/{processObservation.js → process/processObservation.js} +1 -1
  161. package/dist/domain/{processTraceSpecification.js → process/processTraceSpecification.js} +1 -1
  162. package/dist/domain/providerSelectionError.js +4 -2
  163. package/dist/domain/reconstructionVerification.js +3 -3
  164. package/dist/domain/reconstructionVerificationSchemas.js +1 -1
  165. package/dist/domain/residualUnknown.js +3 -2
  166. package/dist/domain/staticRuntimeCorrelation.js +3 -3
  167. package/dist/domain/webCaptureDiff.js +1 -0
  168. package/dist/domain/webNetworkCapture.js +118 -0
  169. package/dist/domain/webNetworkCaptureCoordinates.js +58 -0
  170. package/dist/dotnet/ManagedExceptionRegionValidation.js +41 -0
  171. package/dist/dotnet/ManagedMemberInspector.js +4 -2
  172. package/dist/dotnet/ManagedMemberInspectorCore.js +38 -32
  173. package/dist/dotnet/ManagedMemberInstructionDecoder.js +23 -7
  174. package/dist/dotnet/ManagedMemberRows.js +14 -3
  175. package/dist/dotnet/ManagedMetadataHeaps.js +20 -2
  176. package/dist/dotnet/ManagedMetadataInventory.js +38 -28
  177. package/dist/dotnet/ManagedMetadataInventoryRows.js +24 -10
  178. package/dist/dotnet/ManagedMethodBodyReader.js +7 -1
  179. package/dist/dotnet/ManagedNativeBoundaryHelpers.js +15 -3
  180. package/dist/dotnet/ManagedNativeBoundaryInspector.js +3 -1
  181. package/dist/dotnet/ManagedPeReader.js +11 -0
  182. package/dist/evm/EvmBytecodeCarrier.js +21 -0
  183. package/dist/evm/EvmWorkerLimits.js +40 -0
  184. package/dist/evm/EvmoleFailures.js +126 -0
  185. package/dist/evm/EvmoleInterfaceAdapter.js +53 -0
  186. package/dist/evm/EvmoleInterfaceProvider.js +273 -0
  187. package/dist/evm/EvmoleInterfaceWorker.js +87 -0
  188. package/dist/evm/EvmoleRelease.js +16 -0
  189. package/dist/firmware/FirmwareCommand.js +1 -0
  190. package/dist/generatedPackageMetadata.js +2 -2
  191. package/dist/ghidra/GhidraLauncher.js +1 -0
  192. package/dist/ghidra/GhidraProvider.js +2 -2
  193. package/dist/ghidra/GhidraProviderCapabilities.js +1 -1
  194. package/dist/hopper/BridgeLauncher.js +2 -0
  195. package/dist/hopper/LinuxPrivateDisplayProbe.js +4 -2
  196. package/dist/javascript/recovery/WakaruCommand.js +3 -0
  197. package/dist/javascript/sourceMaps/SourceMapCommand.js +1 -0
  198. package/dist/native/CommandRunner.js +2 -1
  199. package/dist/native/LldbCallTracer.js +278 -0
  200. package/dist/native/NativeCallObservation.js +135 -0
  201. package/dist/native/NativeMacOSProvider.js +19 -3
  202. package/dist/native/NativeMacOSProviderMetadata.js +8 -3
  203. package/dist/native/pwndbg/PwndbgCoreContext.js +160 -0
  204. package/dist/native/pwntools/PwntoolsDecoder.js +203 -0
  205. package/dist/native/pwntools/PwntoolsFailures.js +180 -0
  206. package/dist/native/pwntools/PwntoolsLayoutProvider.js +36 -0
  207. package/dist/native/pwntools/PwntoolsRecordedCrashProvider.js +75 -0
  208. package/dist/native/pwntools/PwntoolsRelease.js +19 -0
  209. package/dist/native/pwntools/PwntoolsResourceLimits.js +76 -0
  210. package/dist/native/pwntools/RecordedCrashSourceBindings.js +46 -0
  211. package/dist/native/pwntools/RecordedCrashStructureBindings.js +101 -0
  212. package/dist/process/DarwinProcessRunTokenReader.js +294 -0
  213. package/dist/process/OwnedCommand.js +113 -0
  214. package/dist/process/ProcessOwnership.js +263 -33
  215. package/dist/process/ProcessOwnershipObservation.js +161 -24
  216. package/dist/process/ProviderProcess.js +30 -3
  217. package/dist/{application → process/capture}/ProcessCaptureCapability.js +15 -0
  218. package/dist/{application → process/capture}/ProcessCaptureError.js +17 -1
  219. package/dist/process/capture/ProcessCaptureLifecycle.js +615 -0
  220. package/dist/{application → process/capture}/ProcessHarness.js +195 -29
  221. package/dist/{application → process/capture}/ProcessSampling.js +2 -2
  222. package/dist/server/createServer.js +22 -0
  223. package/dist/server/promptCompletion.js +10 -3
  224. package/dist/server/registerBinaryDiagnosticsTools.js +17 -0
  225. package/dist/server/registerEvidenceTools.js +1 -1
  226. package/dist/server/registerEvmTools.js +17 -0
  227. package/dist/server/registerProcessComparisonTool.js +1 -1
  228. package/dist/server/registerRecordedCrashTools.js +17 -0
  229. package/dist/server/registerSessionTools.js +2 -2
  230. package/dist/server/registerWebNetworkCaptureTool.js +28 -0
  231. package/dist/server/sessionAvailabilityPolicy.js +3 -0
  232. package/dist/server/sessionToolPolicies.js +1 -1
  233. package/dist/server/toolResult.js +1 -1
  234. package/native/windows/build/manifest.json +2 -2
  235. package/native/windows/build/rea-windows-x64.node +0 -0
  236. package/package.json +37 -9
  237. package/scripts/rea.mjs +11 -3
  238. package/skills/reverse-engineer-anything/SKILL.md +23 -3
  239. package/skills/reverse-engineer-anything/references/native-and-artifacts.md +29 -0
  240. package/third_party/README.md +60 -0
  241. package/third_party/evmole/LICENSE +21 -0
  242. package/third_party/evmole/README.md +22 -0
  243. package/third_party/pwndbg/LICENSE.md +21 -0
  244. package/third_party/pwndbg/README.md +23 -0
  245. package/third_party/pwntools/README.md +40 -0
  246. package/dist/application/AuthorizedArtifactInventory.js +0 -7
  247. package/dist/application/ProcessCaptureLifecycle.js +0 -299
  248. package/dist/application/StoreFileLock.js +0 -124
  249. package/dist/contracts/processCaptureExample.js +0 -2
  250. package/dist/domain/bytecodeProvider.js +0 -178
  251. package/dist/domain/customProtocolCapture.js +0 -137
  252. package/dist/domain/eventProcessTree.js +0 -225
  253. package/dist/domain/mobileApplicationInvestigation.js +0 -159
  254. package/dist/domain/packageAnalysis.js +0 -123
  255. package/dist/domain/peInspection.js +0 -197
  256. package/dist/domain/processCaptureParsing.js +0 -21
  257. package/dist/domain/protocolCapture.js +0 -217
  258. package/dist/domain/runtimeIdentification.js +0 -212
  259. package/dist/generatedMcpToolCatalog.js +0 -37
  260. /package/dist/application/{ArtifactExtractionDestination.js → artifacts/ArtifactExtractionDestination.js} +0 -0
  261. /package/dist/{application/ArtifactInventory → artifacts/inventory}/policy.js +0 -0
  262. /package/dist/{application/ArtifactInventory → artifacts/inventory}/types.js +0 -0
  263. /package/dist/domain/{processEvidenceProvider.js → process/processEvidenceProvider.js} +0 -0
  264. /package/dist/domain/{processScenario.js → process/processScenario.js} +0 -0
  265. /package/dist/domain/{processTraceComparison.js → process/processTraceComparison.js} +0 -0
  266. /package/dist/domain/{processTraceDimensionProjection.js → process/processTraceDimensionProjection.js} +0 -0
  267. /package/dist/domain/{processTraceEvaluation.js → process/processTraceEvaluation.js} +0 -0
  268. /package/dist/{application → process/capture}/FilesystemSnapshot.js +0 -0
  269. /package/dist/{application → process/capture}/ProcessCaptureEnvironment.js +0 -0
  270. /package/dist/{application → process/capture}/ProcessCaptureJournal.js +0 -0
  271. /package/dist/{application → process/capture}/ProcessFilesystemEffects.js +0 -0
  272. /package/dist/{application → process/capture}/ProcessNormalization.js +0 -0
  273. /package/dist/{application → process/capture}/ProcessScenarioRuntimeValidation.js +0 -0
  274. /package/dist/{application → process/capture}/ProcessTimer.js +0 -0
  275. /package/dist/{application → process/capture}/TerminalRenderer.js +0 -0
@@ -0,0 +1,278 @@
1
+ import { spawn } from "node:child_process";
2
+ import { open, readFile, writeFile } from "node:fs/promises";
3
+ import { join } from "node:path";
4
+ import { fileURLToPath } from "node:url";
5
+ import { z } from "zod";
6
+ import { AnalysisCancelledError, AnalysisCapabilityUnavailableError, AnalysisTimeoutError, } from "../domain/analysisErrorCore.js";
7
+ import { nativeCallEventSchema, nativeCodeLocationSchema, } from "../domain/native/nativeCallObservation.js";
8
+ import { ProviderAdapterError } from "../domain/providerAdapterError.js";
9
+ import { err, ok } from "../domain/result.js";
10
+ import { safeParseJson } from "../domain/safeJson.js";
11
+ import { execFileOutput } from "../process/ExecFileOutput.js";
12
+ import { PrivateRuntimeRoot } from "../process/PrivateRuntimeRoot.js";
13
+ import { resolveXcrunTool } from "./CommandRunner.js";
14
+ const OPERATION = "observe_native_calls";
15
+ const PROVIDER = "native-macos";
16
+ /** Captured target output kept per stream; the rest is counted, not stored. */
17
+ const MAX_OUTPUT_BYTES = 1024 * 1024;
18
+ /** LLDB startup, symbol loading and teardown beyond the observation window. */
19
+ const DEADLINE_GRACE_MS = 60_000;
20
+ const DIAGNOSTIC_BYTES = 16 * 1024;
21
+ const BRIDGE = fileURLToPath(new URL("../../bridge/native/rea_lldb_tracer.py", import.meta.url));
22
+ /** JSON the LLDB bridge writes after a completed run. */
23
+ const tracedSchema = z.strictObject({
24
+ status: z.literal("traced"),
25
+ version: z.string(),
26
+ pid: z.number().int().positive(),
27
+ outcome: z.enum(["exited", "duration-elapsed", "event-limit", "stop-limit"]),
28
+ exit_status: z.number().int().nullable(),
29
+ exit_description: z.string().nullable(),
30
+ killed: z.boolean(),
31
+ terminated: z.boolean(),
32
+ elapsed_ms: z.number().nonnegative(),
33
+ breakpoints: z.array(z.strictObject({
34
+ index: z.number().int().nonnegative(),
35
+ location_count: z.number().int().nonnegative(),
36
+ locations: z.array(nativeCodeLocationSchema),
37
+ })),
38
+ events: z.array(nativeCallEventSchema),
39
+ other_stops: z.array(z.string()),
40
+ });
41
+ const tracerOutputSchema = z.discriminatedUnion("status", [
42
+ tracedSchema,
43
+ z.strictObject({
44
+ status: z.enum(["target-error", "launch-error", "tracer-error"]),
45
+ error: z.string().nullable(),
46
+ }),
47
+ ]);
48
+ /** Remediation for a target LLDB may not debug. */
49
+ 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
+ /** Production tracer: `lldb --batch` running the REA LLDB bridge. */
51
+ export class LldbCallTracer {
52
+ async trace(request, signal) {
53
+ const lldb = await resolveXcrunTool("lldb", signal);
54
+ if (!lldb.ok)
55
+ return err(lldb.error.reason === "cancelled"
56
+ ? new AnalysisCancelledError(OPERATION)
57
+ : new AnalysisCapabilityUnavailableError(PROVIDER, OPERATION, "LLDB from Xcode or the Command Line Tools is not available"));
58
+ const root = await PrivateRuntimeRoot.create({ prefix: "rea-lldb-" });
59
+ try {
60
+ return await this.#run(lldb.value, root.path, request, signal);
61
+ }
62
+ finally {
63
+ await root.close();
64
+ }
65
+ }
66
+ async #run(lldb, directory, request, signal) {
67
+ const paths = {
68
+ config: join(directory, "config.json"),
69
+ result: join(directory, "result.json"),
70
+ pid: join(directory, "pid"),
71
+ stdout: join(directory, "stdout"),
72
+ stderr: join(directory, "stderr"),
73
+ };
74
+ const { input } = request;
75
+ await writeFile(paths.config, JSON.stringify({
76
+ executable: request.executable,
77
+ architecture: request.architecture,
78
+ arguments: input.arguments,
79
+ environment: input.environment,
80
+ working_directory: input.working_directory ?? null,
81
+ breakpoints: input.breakpoints.map((breakpoint) => breakpoint.kind === "function"
82
+ ? { ...breakpoint, module: breakpoint.module ?? null }
83
+ : { ...breakpoint, class_name: breakpoint.class_name ?? null }),
84
+ duration_ms: input.duration_ms,
85
+ max_events: input.max_events,
86
+ argument_registers: input.argument_registers,
87
+ backtrace_frames: input.backtrace_frames,
88
+ stdout_path: paths.stdout,
89
+ stderr_path: paths.stderr,
90
+ result_path: paths.result,
91
+ pid_path: paths.pid,
92
+ }), { mode: 0o600 });
93
+ const exited = await runLldb(lldb.path, [
94
+ "--batch",
95
+ "--no-lldbinit",
96
+ "--no-use-colors",
97
+ "-o",
98
+ `command script import ${quote(BRIDGE)}`,
99
+ "-o",
100
+ `rea_trace ${paths.config}`,
101
+ ], {
102
+ deadlineMs: input.duration_ms + DEADLINE_GRACE_MS,
103
+ ...(signal === undefined ? {} : { signal }),
104
+ });
105
+ const terminated = await ensureTerminated(paths.pid, request.executable);
106
+ if (exited.kind === "cancelled")
107
+ return err(new AnalysisCancelledError(OPERATION));
108
+ if (exited.kind === "timeout")
109
+ return err(new AnalysisTimeoutError(OPERATION, input.duration_ms + DEADLINE_GRACE_MS));
110
+ const output = await readTracerOutput(paths.result);
111
+ if (output === undefined)
112
+ return err(new ProviderAdapterError(PROVIDER, OPERATION, {
113
+ diagnostics: {
114
+ reason: "LLDB exited without a tracer result",
115
+ exit_code: exited.exitCode,
116
+ lldb_output: exited.output,
117
+ },
118
+ }));
119
+ if (output.status !== "traced")
120
+ return err(tracerFailure(output));
121
+ return ok({
122
+ debugger: { ...lldb, version: output.version },
123
+ run: output,
124
+ stdout: await capturedOutput(paths.stdout),
125
+ stderr: await capturedOutput(paths.stderr),
126
+ terminated,
127
+ });
128
+ }
129
+ }
130
+ /** LLDB command arguments are quoted; the bridge path is REA's own install path. */
131
+ const quote = (value) => `"${value.replaceAll("\\", "\\\\").replaceAll('"', '\\"')}"`;
132
+ const tracerFailure = (output) => {
133
+ const message = output.error ?? "no LLDB error text";
134
+ if (output.status === "tracer-error")
135
+ return new ProviderAdapterError(PROVIDER, OPERATION, {
136
+ diagnostics: { reason: `LLDB bridge failed: ${message}` },
137
+ });
138
+ if (output.status === "target-error")
139
+ return new AnalysisCapabilityUnavailableError(PROVIDER, OPERATION, `lldb-target-unloadable: ${message}`);
140
+ // debugserver reports denied task access as an attach failure.
141
+ 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}`);
144
+ };
145
+ const readTracerOutput = async (path) => {
146
+ let text;
147
+ try {
148
+ text = await readFile(path, "utf8");
149
+ }
150
+ catch {
151
+ return undefined;
152
+ }
153
+ const parsed = safeParseJson(text);
154
+ if (!parsed.ok)
155
+ return undefined;
156
+ const output = tracerOutputSchema.safeParse(parsed.value);
157
+ return output.success ? output.data : undefined;
158
+ };
159
+ const capturedOutput = async (path) => {
160
+ let handle;
161
+ try {
162
+ handle = await open(path, "r");
163
+ }
164
+ catch {
165
+ return { text: "", bytes: 0, truncated: false };
166
+ }
167
+ 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);
171
+ return {
172
+ text: buffer.subarray(0, bytesRead).toString("utf8"),
173
+ bytes: size,
174
+ truncated: size > MAX_OUTPUT_BYTES,
175
+ };
176
+ }
177
+ finally {
178
+ await handle.close();
179
+ }
180
+ };
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
+ /**
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.
233
+ */
234
+ const ensureTerminated = async (pidPath, executable) => {
235
+ let pid;
236
+ try {
237
+ pid = Number.parseInt(await readFile(pidPath, "utf8"), 10);
238
+ }
239
+ catch {
240
+ // LLDB never launched the target.
241
+ return true;
242
+ }
243
+ if (!Number.isSafeInteger(pid) || pid <= 0)
244
+ return false;
245
+ for (let attempt = 0; attempt < 20; attempt++) {
246
+ 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.
254
+ }
255
+ await new Promise((resolve) => setTimeout(resolve, 100));
256
+ }
257
+ return !alive(pid);
258
+ };
259
+ const alive = (pid) => {
260
+ try {
261
+ process.kill(pid, 0);
262
+ return true;
263
+ }
264
+ catch (cause) {
265
+ return !(cause instanceof Error &&
266
+ "code" in cause &&
267
+ cause.code === "ESRCH");
268
+ }
269
+ };
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
+ };
@@ -0,0 +1,135 @@
1
+ import { createHash } from "node:crypto";
2
+ import { open } from "node:fs/promises";
3
+ import { AnalysisCancelledError, AnalysisCapabilityUnavailableError, AnalysisInputError, } from "../domain/analysisErrorCore.js";
4
+ import { EvidenceIntegrityError } from "../domain/evidenceErrors.js";
5
+ import { nativeCallObservationInputSchema, nativeCallObservationResultSchema, } from "../domain/native/nativeCallObservation.js";
6
+ import { err, ok } from "../domain/result.js";
7
+ const OPERATION = "observe_native_calls";
8
+ /** What every call observation leaves unobserved or approximated. */
9
+ export const NATIVE_CALL_OBSERVATION_LIMITATIONS = [
10
+ "Only entries are observed; return values, floating-point and stack-passed arguments are not captured.",
11
+ "Registers are the raw integer argument registers at entry. For Objective-C methods the first two hold the receiver and the selector.",
12
+ "Receiver classes come from LLDB's Objective-C runtime reader without running target code; they are null for class methods and for receivers LLDB cannot classify.",
13
+ "Calls that never reach a symbol's entry, such as inlined code or objc_direct methods called directly, are not observed.",
14
+ "The target is launched directly under LLDB, not through LaunchServices; LLDB disables ASLR for it, so load addresses can equal file addresses.",
15
+ "The observation window is checked about once a second, and recording pauses the process at every entry, which slows it.",
16
+ ];
17
+ /** Launch the active Mach-O under LLDB, record requested call entries, then stop it. */
18
+ export const observeNativeCalls = async (target, parameters, tracer, signal) => {
19
+ if (target.kind !== "executable" || target.format !== "mach-o")
20
+ return err(new AnalysisCapabilityUnavailableError("native-macos", OPERATION, "Native call observation launches a Mach-O executable; the active target is not one"));
21
+ const parsed = nativeCallObservationInputSchema.safeParse(parameters);
22
+ if (!parsed.success)
23
+ return err(new AnalysisInputError(OPERATION, { cause: parsed.error }));
24
+ // Observe the bytes the session's other Evidence describes.
25
+ if ((await fileSha256(target.path, signal)) !== target.sha256)
26
+ return err(new EvidenceIntegrityError("Native call observation target digest changed after session binding"));
27
+ if (signal?.aborted === true)
28
+ return err(new AnalysisCancelledError(OPERATION));
29
+ const traced = await tracer.trace({
30
+ executable: target.path,
31
+ architecture: target.architecture,
32
+ input: parsed.data,
33
+ }, signal);
34
+ if (!traced.ok)
35
+ return traced;
36
+ return ok(projectNativeCalls(target, parsed.data, traced.value));
37
+ };
38
+ /** Join the request, the tracer's run and REA's own checks into the result contract. */
39
+ export const projectNativeCalls = (target, input, trace) => {
40
+ const { run } = trace;
41
+ const unresolved = run.breakpoints
42
+ .filter(({ location_count: count }) => count === 0)
43
+ .map(({ index }) => index);
44
+ const eventLimit = run.outcome === "event-limit";
45
+ const partial = eventLimit || run.outcome === "stop-limit" || unresolved.length > 0;
46
+ return nativeCallObservationResultSchema.parse({
47
+ target: {
48
+ path: target.path,
49
+ sha256: target.sha256,
50
+ architecture: target.architecture,
51
+ arguments: input.arguments,
52
+ working_directory: input.working_directory ?? null,
53
+ },
54
+ debugger: trace.debugger,
55
+ process: {
56
+ pid: run.pid,
57
+ outcome: run.outcome,
58
+ exit_status: run.exit_status,
59
+ exit_description: run.exit_description,
60
+ terminated: run.terminated && trace.terminated,
61
+ elapsed_ms: run.elapsed_ms,
62
+ stdout: trace.stdout,
63
+ stderr: trace.stderr,
64
+ other_stops: run.other_stops,
65
+ },
66
+ breakpoints: input.breakpoints.map((request, index) => ({
67
+ index,
68
+ request: request.kind === "function"
69
+ ? { ...request, module: request.module ?? null }
70
+ : { ...request, class_name: request.class_name ?? null },
71
+ location_count: run.breakpoints[index]?.location_count ?? 0,
72
+ locations: run.breakpoints[index]?.locations ?? [],
73
+ })),
74
+ events: run.events,
75
+ coverage: {
76
+ status: partial ? "partial" : "complete",
77
+ event_limit_reached: eventLimit,
78
+ unresolved_breakpoints: unresolved,
79
+ },
80
+ limitations: [
81
+ ...NATIVE_CALL_OBSERVATION_LIMITATIONS,
82
+ ...(eventLimit
83
+ ? [
84
+ `Observation stopped at max_events (${input.max_events}); later calls were not recorded and the process was killed.`,
85
+ ]
86
+ : []),
87
+ ...(run.outcome === "duration-elapsed"
88
+ ? [
89
+ `The process was still running after duration_ms (${input.duration_ms}) and was killed.`,
90
+ ]
91
+ : []),
92
+ ...(run.outcome === "stop-limit"
93
+ ? [
94
+ "Observation stopped after repeated signal or exception stops; the process was killed.",
95
+ ]
96
+ : []),
97
+ ...(unresolved.length === 0
98
+ ? []
99
+ : [
100
+ `Breakpoints ${unresolved.join(", ")} matched no code in any image loaded while observing; their calls could not be observed.`,
101
+ ]),
102
+ ...(run.breakpoints.some(({ location_count: count, locations }) => locations.length < count)
103
+ ? [
104
+ "Breakpoints with many matching locations list only the first 64; location_count gives the total.",
105
+ ]
106
+ : []),
107
+ ...(trace.stdout.truncated || trace.stderr.truncated
108
+ ? ["Target output beyond 1 MiB per stream is counted but not kept."]
109
+ : []),
110
+ ...(run.terminated && trace.terminated
111
+ ? []
112
+ : [
113
+ `REA could not confirm that process ${run.pid} exited; check for it before relying on host state.`,
114
+ ]),
115
+ ],
116
+ });
117
+ };
118
+ const fileSha256 = async (path, signal) => {
119
+ const handle = await open(path, "r");
120
+ try {
121
+ const hash = createHash("sha256");
122
+ const buffer = new Uint8Array(1024 * 1024);
123
+ for (;;) {
124
+ signal?.throwIfAborted();
125
+ const { bytesRead } = await handle.read(buffer, 0, buffer.length, null);
126
+ if (bytesRead === 0)
127
+ break;
128
+ hash.update(buffer.subarray(0, bytesRead));
129
+ }
130
+ return hash.digest("hex");
131
+ }
132
+ finally {
133
+ await handle.close();
134
+ }
135
+ };
@@ -2,6 +2,8 @@ import { NATIVE_MACOS_PROVIDER_IDENTITY as IDENTITY, nativeMacOSCapabilities, }
2
2
  export { NATIVE_MACOS_PROVIDER_IDENTITY } from "./NativeMacOSProviderMetadata.js";
3
3
  import { inspectAppleDispatchMetadata } from "./AppleDispatchMetadata.js";
4
4
  import { observeNativeUi } from "./NativeUiObservation.js";
5
+ import { LldbCallTracer } from "./LldbCallTracer.js";
6
+ import { observeNativeCalls } from "./NativeCallObservation.js";
5
7
  import { dirname, isAbsolute, resolve } from "node:path";
6
8
  import { open, realpath, stat } from "node:fs/promises";
7
9
  import { z } from "zod";
@@ -24,9 +26,11 @@ import { architectureLocations, inspectNativeMacho, } from "./NativeMachoInspect
24
26
  /** Read-only semantic provider composed from Xcode command-line utilities. */
25
27
  export class NativeMacOSProvider {
26
28
  runner;
29
+ tracer;
27
30
  #capabilities;
28
- constructor(runner = new XcrunCommandRunner(), platform = process.platform) {
31
+ constructor(runner = new XcrunCommandRunner(), platform = process.platform, tracer = new LldbCallTracer()) {
29
32
  this.runner = runner;
33
+ this.tracer = tracer;
30
34
  this.#capabilities = nativeMacOSCapabilities(platform);
31
35
  }
32
36
  identity() {
@@ -36,15 +40,17 @@ export class NativeMacOSProvider {
36
40
  return this.#capabilities;
37
41
  }
38
42
  createClient(target) {
39
- return new NativeMacOSClient(target, this.runner);
43
+ return new NativeMacOSClient(target, this.runner, this.tracer);
40
44
  }
41
45
  }
42
46
  class NativeMacOSClient {
43
47
  target;
44
48
  runner;
45
- constructor(target, runner) {
49
+ tracer;
50
+ constructor(target, runner, tracer) {
46
51
  this.target = target;
47
52
  this.runner = runner;
53
+ this.tracer = tracer;
48
54
  }
49
55
  async execute(operation, parameters, options) {
50
56
  if (options?.signal?.aborted === true)
@@ -83,6 +89,15 @@ class NativeMacOSClient {
83
89
  }
84
90
  if (!isNativeOperation(operation))
85
91
  return err(new AnalysisCapabilityUnavailableError(IDENTITY.id, operation, "Operation is not implemented by native macOS tools."));
92
+ if (operation === "observe_native_calls") {
93
+ const result = await observeNativeCalls(this.target, parameters, this.tracer, options?.signal);
94
+ return result.ok
95
+ ? ok(createAnalysisExecution(result.value, IDENTITY, {
96
+ limitations: result.value.limitations,
97
+ locations: [{ kind: "artifact-path", path: this.target.path }],
98
+ }))
99
+ : result;
100
+ }
86
101
  if (operation === "observe_native_ui" ||
87
102
  operation === "capture_native_ui_scenario") {
88
103
  const result = await observeNativeUi(this.target, operation, parameters, {
@@ -117,6 +132,7 @@ class NativeMacOSClient {
117
132
  switch (operation) {
118
133
  case "observe_native_ui":
119
134
  case "capture_native_ui_scenario":
135
+ case "observe_native_calls":
120
136
  return Promise.resolve(err(new AnalysisCapabilityUnavailableError(IDENTITY.id, operation, "UI observation uses its explicit authority boundary")));
121
137
  case "inspect_macho":
122
138
  return this.#inspectMacho(signal);
@@ -6,6 +6,11 @@ export const NATIVE_MACOS_PROVIDER_IDENTITY = Object.freeze({
6
6
  version: null,
7
7
  });
8
8
  const IDENTITY = NATIVE_MACOS_PROVIDER_IDENTITY;
9
+ /** Operations whose target process can show UI, use the network and write files. */
10
+ const OWNED_PROCESS_OPERATIONS = new Set([
11
+ "capture_native_ui_scenario",
12
+ "observe_native_calls",
13
+ ]);
9
14
  /** Declare native host coverage without creating a command runner. */
10
15
  export const nativeMacOSCapabilities = (platform = process.platform) => {
11
16
  const available = platform === "darwin";
@@ -27,9 +32,9 @@ export const nativeMacOSCapabilities = (platform = process.platform) => {
27
32
  effects: Object.freeze({
28
33
  mutatesArtifact: false,
29
34
  launchesProcess: contract.name !== "inspect_native_dispatch_metadata",
30
- mayShowUi: contract.name === "capture_native_ui_scenario",
31
- mayAccessNetwork: contract.name === "capture_native_ui_scenario",
32
- mayWriteFilesystem: contract.name === "capture_native_ui_scenario" ||
35
+ mayShowUi: OWNED_PROCESS_OPERATIONS.has(contract.name),
36
+ mayAccessNetwork: OWNED_PROCESS_OPERATIONS.has(contract.name),
37
+ mayWriteFilesystem: OWNED_PROCESS_OPERATIONS.has(contract.name) ||
33
38
  contract.name === "observe_native_ui",
34
39
  changesPermissions: false,
35
40
  requiresRoot: false,
@@ -0,0 +1,160 @@
1
+ import { access, mkdir, stat, writeFile } from "node:fs/promises";
2
+ import { constants } from "node:fs";
3
+ import { randomUUID } from "node:crypto";
4
+ import { isAbsolute, join } from "node:path";
5
+ import { fileURLToPath } from "node:url";
6
+ import { z } from "zod";
7
+ import { AnalysisCancelledError, AnalysisCapabilityUnavailableError, AnalysisOutputError, } from "../../domain/analysisErrorCore.js";
8
+ import { ProviderAdapterError } from "../../domain/providerAdapterError.js";
9
+ import { recordedCrashDebuggerSchema, } from "../../domain/native/recordedCrash.js";
10
+ import { readStableArtifact } from "../../artifacts/readStableArtifact.js";
11
+ import { runOwnedCommand } from "../../process/OwnedCommand.js";
12
+ import { capturedPwntoolsOutput, pwntoolsDecoderFailure, } from "../pwntools/PwntoolsFailures.js";
13
+ import { PWNTOOLS_LIMITS } from "../pwntools/PwntoolsRelease.js";
14
+ import { readPwntoolsFailureEvidence } from "../pwntools/PwntoolsResourceLimits.js";
15
+ const OPERATION = "inspect_recorded_crash";
16
+ const replySchema = z.discriminatedUnion("ok", [
17
+ z.strictObject({ ok: z.literal(true), value: recordedCrashDebuggerSchema }),
18
+ z.strictObject({
19
+ ok: z.literal(false),
20
+ reason: z.enum([
21
+ "unavailable",
22
+ "unsupported",
23
+ "decoder",
24
+ "format",
25
+ "output-limit",
26
+ ]),
27
+ message: z.string(),
28
+ }),
29
+ ]);
30
+ /** Add mapping candidates from an owned core-only debugger, using the existing snapshot. */
31
+ export const inspectPwndbgCore = async ({ environment, rootPath, diagnostics, options, launcher, }) => {
32
+ const gdb = environment.REA_PWNDBG_GDB ?? "/usr/bin/gdb";
33
+ const entry = environment.REA_PWNDBG_GDBINIT ?? "";
34
+ const venv = environment.REA_PWNDBG_VENV_PATH ?? "";
35
+ const unavailable = (message, cause) => new AnalysisCapabilityUnavailableError("pwndbg-core", OPERATION, message, {
36
+ ...(cause === undefined ? {} : { cause }),
37
+ userMessage: message,
38
+ capturedOutput: diagnostics,
39
+ });
40
+ for (const [key, path, kind] of [
41
+ ["REA_PWNDBG_GDB", gdb, "file"],
42
+ ["REA_PWNDBG_GDBINIT", entry, "file"],
43
+ ["REA_PWNDBG_VENV_PATH", venv, "directory"],
44
+ ]) {
45
+ try {
46
+ if (!isAbsolute(path))
47
+ throw new Error("Expected an absolute caller-supplied path.");
48
+ await access(path, key === "REA_PWNDBG_GDB" ? constants.X_OK : constants.R_OK);
49
+ const information = await stat(path);
50
+ if (kind === "file" ? !information.isFile() : !information.isDirectory())
51
+ throw new Error(`Expected a ${kind}.`);
52
+ }
53
+ catch (cause) {
54
+ throw unavailable(`${key} is unavailable at ${path}: ${cause instanceof Error ? cause.message : String(cause)}. Configure the requested core-only context, or omit include_debugger_context for basic recorded evidence.`, cause);
55
+ }
56
+ }
57
+ const stage = join(rootPath, "debugger");
58
+ await mkdir(stage, { mode: 0o700 });
59
+ const requestPath = join(stage, "request.json");
60
+ const replyPath = join(stage, "reply.json");
61
+ await writeFile(requestPath, JSON.stringify({
62
+ snapshot_path: join(rootPath, "object.snapshot"),
63
+ reply_path: replyPath,
64
+ gdb,
65
+ gdbinit: entry,
66
+ venv,
67
+ }), { flag: "wx", mode: 0o600 });
68
+ let output;
69
+ try {
70
+ options?.signal?.throwIfAborted();
71
+ const execution = await runOwnedCommand({
72
+ command: environment.REA_PWNTOOLS_PYTHON ?? "",
73
+ arguments: [
74
+ "-I",
75
+ fileURLToPath(new URL("../../../bridge/pwndbg/launch.py", import.meta.url)),
76
+ join(stage, "resource.failure"),
77
+ requestPath,
78
+ ],
79
+ cwd: stage,
80
+ runId: `rea-recorded-core-debugger-${randomUUID()}`,
81
+ hostEnvironment: {
82
+ ...environment,
83
+ PWNDBG_NO_AUTOUPDATE: "1",
84
+ PWNDBG_VENV_PATH: venv,
85
+ PWNLIB_NOTERM: "1",
86
+ TERM: "dumb",
87
+ HOME: stage,
88
+ XDG_CACHE_HOME: join(stage, "cache"),
89
+ XDG_CONFIG_HOME: join(stage, "config"),
90
+ PYTHONNOUSERSITE: "1",
91
+ OPENBLAS_NUM_THREADS: "1",
92
+ OMP_NUM_THREADS: "1",
93
+ },
94
+ }, {
95
+ timeoutMs: PWNTOOLS_LIMITS.timeoutMs,
96
+ diagnosticBytes: PWNTOOLS_LIMITS.diagnosticBytes,
97
+ }, {
98
+ ...(options?.signal === undefined ? {} : { signal: options.signal }),
99
+ ...(launcher === undefined ? {} : { launcher }),
100
+ });
101
+ output = capturedPwntoolsOutput(execution);
102
+ let reply;
103
+ try {
104
+ const raw = await readStableArtifact(replyPath, PWNTOOLS_LIMITS.outputBytes, options?.signal);
105
+ reply = replySchema.parse(JSON.parse(raw.bytes.toString("utf8")));
106
+ }
107
+ catch (cause) {
108
+ if (options?.signal?.aborted)
109
+ throw new AnalysisCancelledError(OPERATION, {
110
+ capturedOutput: recordedCrashStageOutput(diagnostics, output),
111
+ });
112
+ throw new AnalysisOutputError(OPERATION, `Core-only debugger reply failed: ${cause instanceof Error ? cause.message : String(cause)}`, {
113
+ cause,
114
+ capturedOutput: recordedCrashStageOutput(diagnostics, output),
115
+ });
116
+ }
117
+ if (!reply.ok) {
118
+ if (reply.reason === "unavailable" || reply.reason === "unsupported")
119
+ throw new AnalysisCapabilityUnavailableError("pwndbg-core", OPERATION, reply.message, {
120
+ userMessage: reply.message,
121
+ capturedOutput: recordedCrashStageOutput(diagnostics, output),
122
+ });
123
+ if (reply.reason === "format" || reply.reason === "output-limit")
124
+ throw new AnalysisOutputError(OPERATION, reply.message, {
125
+ capturedOutput: recordedCrashStageOutput(diagnostics, output),
126
+ });
127
+ throw new ProviderAdapterError("pwndbg-core", OPERATION, {
128
+ diagnostics: {
129
+ stage: "debugger",
130
+ reason: reply.message,
131
+ captured_output: { ...recordedCrashStageOutput(diagnostics, output) },
132
+ },
133
+ });
134
+ }
135
+ if (reply.value.status !== "available")
136
+ throw new AnalysisOutputError(OPERATION, "Requested debugger context returned no core evidence.", { capturedOutput: recordedCrashStageOutput(diagnostics, output) });
137
+ return { ...reply.value, diagnostics: output };
138
+ }
139
+ catch (cause) {
140
+ if (options?.signal?.aborted &&
141
+ (cause === options.signal.reason ||
142
+ (cause instanceof Error && cause.name === "AbortError")))
143
+ throw new AnalysisCancelledError(OPERATION, {
144
+ capturedOutput: recordedCrashStageOutput(diagnostics, output),
145
+ });
146
+ throw pwntoolsDecoderFailure(cause, "debugger", gdb, environment.REA_PWNTOOLS_PYTHON ?? "", await readPwntoolsFailureEvidence(cause, stage), {
147
+ operation: OPERATION,
148
+ providerId: "pwndbg-core",
149
+ precedingOutput: diagnostics,
150
+ });
151
+ }
152
+ };
153
+ /** Preserve both stages' exact text and their independently observed truncation state. */
154
+ export const recordedCrashStageOutput = (basic, debuggerOutput) => debuggerOutput === undefined
155
+ ? basic
156
+ : {
157
+ stdout: `[core decoder]\n${basic.stdout}\n[debugger]\n${debuggerOutput.stdout}`,
158
+ stderr: `[core decoder]\n${basic.stderr}\n[debugger]\n${debuggerOutput.stderr}`,
159
+ truncated: basic.truncated || debuggerOutput.truncated,
160
+ };