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,203 @@
1
+ import { pwntoolsDecoderFailure, pwntoolsUnavailable, capturedPwntoolsOutput, } from "./PwntoolsFailures.js";
2
+ import { randomUUID } from "node:crypto";
3
+ import { access, stat, writeFile } from "node:fs/promises";
4
+ import { constants } from "node:fs";
5
+ import { isAbsolute, join } from "node:path";
6
+ import { fileURLToPath } from "node:url";
7
+ import { z } from "zod";
8
+ import { readStableArtifact } from "../../artifacts/readStableArtifact.js";
9
+ import { AnalysisCancelledError, AnalysisCapabilityUnavailableError, AnalysisUnsupportedTargetError, AnalysisInputError, AnalysisOutputError, AnalysisResourceConstraintError, } from "../../domain/analysisErrorCore.js";
10
+ import { projectAnalysisError } from "../../domain/analysisErrorProjection.js";
11
+ import { ProviderAdapterError } from "../../domain/providerAdapterError.js";
12
+ import { ProviderCleanupError } from "../../domain/providerCleanupError.js";
13
+ import { err, ok } from "../../domain/result.js";
14
+ import { runOwnedCommand } from "../../process/OwnedCommand.js";
15
+ import { OwnedCommandFailure } from "../../process/OwnedCommand.js";
16
+ import { pwntoolsResourceLimitsSchema, readPwntoolsFailureEvidence, } from "./PwntoolsResourceLimits.js";
17
+ import { PrivateRuntimeRoot } from "../../process/PrivateRuntimeRoot.js";
18
+ import { PWNTOOLS_PROVIDER_IDENTITY, PWNTOOLS_LIMITS, } from "./PwntoolsRelease.js";
19
+ /** Thin owned-process adapter; all ELF parsing and heuristics come from unchanged upstream code. */
20
+ export class PwntoolsDecoder {
21
+ profile;
22
+ environment;
23
+ launcher;
24
+ createRuntime;
25
+ identity;
26
+ constructor(profile, environment, launcher, createRuntime = () => PrivateRuntimeRoot.create({ prefix: "rea-artifact-decoder-" })) {
27
+ this.profile = profile;
28
+ this.environment = environment;
29
+ this.launcher = launcher;
30
+ this.createRuntime = createRuntime;
31
+ this.identity = profile.identity;
32
+ }
33
+ /** Snapshot one explicit artifact and return complete static observations without launching the selected object as a host process. */
34
+ async inspect(input, options) {
35
+ const OPERATION = this.profile.operation;
36
+ const context = { operation: OPERATION, providerId: this.identity.id };
37
+ const replySchema = z.discriminatedUnion("ok", [
38
+ z.strictObject({
39
+ ok: z.literal(true),
40
+ profile: z.literal(PWNTOOLS_PROVIDER_IDENTITY.version),
41
+ value: this.profile.payloadSchema,
42
+ }),
43
+ z.strictObject({
44
+ ok: z.literal(false),
45
+ reason: z.enum([
46
+ "format",
47
+ "unsupported",
48
+ "unsupported-target",
49
+ "unavailable",
50
+ "resource-limit",
51
+ "output-limit",
52
+ "decoder",
53
+ ]),
54
+ message: z.string(),
55
+ reported_limits: pwntoolsResourceLimitsSchema.nullable().optional(),
56
+ }),
57
+ ]);
58
+ let root;
59
+ let result;
60
+ let phase = "configuration";
61
+ const executablePath = this.environment.REA_PWNTOOLS_PYTHON ?? "";
62
+ let selectedPath = executablePath;
63
+ try {
64
+ if (options?.signal?.aborted)
65
+ throw new AnalysisCancelledError(OPERATION);
66
+ if (process.platform !== "linux" || process.arch !== "x64")
67
+ throw new AnalysisCapabilityUnavailableError(this.identity.id, OPERATION, "The initial real-verified pwntools artifact profile supports Linux x64 only.");
68
+ if (!isAbsolute(selectedPath))
69
+ throw pwntoolsUnavailable("Set REA_PWNTOOLS_PYTHON to an absolute caller-supplied Python executable with pwntools 4.15.0, pyelftools 0.33 and Unicorn 2.1.2; REA never installs Python or packages.", selectedPath, undefined, undefined, context);
70
+ await access(selectedPath, constants.X_OK);
71
+ if (!(await stat(selectedPath)).isFile())
72
+ throw pwntoolsUnavailable("Selected Python executable must be a regular executable file, not a directory: " +
73
+ selectedPath, selectedPath, undefined, undefined, context);
74
+ phase = "artifact-read";
75
+ selectedPath = input.path;
76
+ const snapshot = await readStableArtifact(input.path, PWNTOOLS_LIMITS.inputBytes, options?.signal);
77
+ phase = "decoder";
78
+ root = await this.createRuntime();
79
+ const requestPath = join(root.path, "request.json");
80
+ const snapshotPath = join(root.path, "object.snapshot");
81
+ const replyPath = join(root.path, "reply.json");
82
+ await writeFile(snapshotPath, snapshot.bytes, {
83
+ flag: "wx",
84
+ mode: 0o600,
85
+ ...(options?.signal === undefined ? {} : { signal: options.signal }),
86
+ });
87
+ await writeFile(requestPath, JSON.stringify({ snapshot_path: snapshotPath, reply_path: replyPath }), { flag: "wx", mode: 0o600 });
88
+ const execution = await runOwnedCommand({
89
+ command: this.environment.REA_PWNTOOLS_PYTHON ?? "",
90
+ arguments: [
91
+ "-I",
92
+ fileURLToPath(this.profile.bridge),
93
+ join(root.path, "resource.failure"),
94
+ requestPath,
95
+ ],
96
+ cwd: root.path,
97
+ runId: `rea-artifact-decoder-${randomUUID()}`,
98
+ hostEnvironment: {
99
+ ...this.environment,
100
+ PWNLIB_NOTERM: "1",
101
+ OPENBLAS_NUM_THREADS: "1",
102
+ OMP_NUM_THREADS: "1",
103
+ },
104
+ }, {
105
+ timeoutMs: PWNTOOLS_LIMITS.timeoutMs,
106
+ diagnosticBytes: PWNTOOLS_LIMITS.diagnosticBytes,
107
+ }, {
108
+ ...(options?.signal === undefined ? {} : { signal: options.signal }),
109
+ ...(this.launcher === undefined ? {} : { launcher: this.launcher }),
110
+ });
111
+ const capturedOutput = capturedPwntoolsOutput(execution);
112
+ let reply;
113
+ try {
114
+ const file = await readStableArtifact(replyPath, PWNTOOLS_LIMITS.outputBytes, options?.signal);
115
+ reply = replySchema.parse(JSON.parse(file.bytes.toString("utf8")));
116
+ }
117
+ catch (cause) {
118
+ if (options?.signal?.aborted)
119
+ throw new AnalysisCancelledError(OPERATION, { capturedOutput });
120
+ throw new AnalysisOutputError(OPERATION, `Owned pwntools decoder reply failed for ${input.path}: ${cause instanceof z.ZodError ? cause.issues[0]?.message + " at " + (cause.issues[0]?.path.map(String).join(".") ?? "root") : cause instanceof Error ? cause.message : String(cause)}`, { cause, capturedOutput });
121
+ }
122
+ if (!reply.ok) {
123
+ if (reply.reason === "unsupported-target")
124
+ throw new AnalysisUnsupportedTargetError(OPERATION, input.path, reply.message, { capturedOutput });
125
+ if (reply.reason === "resource-limit")
126
+ throw new AnalysisResourceConstraintError(OPERATION, "memory", reply.message, reply.reported_limits ?? null, { capturedOutput });
127
+ if (reply.reason === "format")
128
+ throw new AnalysisInputError(OPERATION, { capturedOutput }, [
129
+ {
130
+ path: ["path"],
131
+ reason: "invalid_format",
132
+ message: reply.message,
133
+ },
134
+ ]);
135
+ if (reply.reason === "unavailable")
136
+ throw pwntoolsUnavailable(reply.message, this.environment.REA_PWNTOOLS_PYTHON ?? "", undefined, capturedOutput, context);
137
+ if (reply.reason === "unsupported")
138
+ throw new AnalysisCapabilityUnavailableError(this.identity.id, OPERATION, reply.message, { userMessage: reply.message, capturedOutput });
139
+ if (reply.reason === "output-limit")
140
+ throw new AnalysisOutputError(OPERATION, reply.message, {
141
+ capturedOutput,
142
+ });
143
+ throw new ProviderAdapterError(this.identity.id, OPERATION, {
144
+ diagnostics: {
145
+ phase,
146
+ failure_kind: reply.reason,
147
+ reason: reply.message,
148
+ stdout: execution.stdout.text,
149
+ stderr: execution.stderr.text,
150
+ captured_output: { ...capturedOutput },
151
+ },
152
+ });
153
+ }
154
+ result = ok(await this.profile.project({
155
+ input,
156
+ payload: reply.value,
157
+ snapshot,
158
+ rootPath: root.path,
159
+ diagnostics: capturedOutput,
160
+ options,
161
+ }));
162
+ }
163
+ catch (cause) {
164
+ const evidence = await readPwntoolsFailureEvidence(cause, root?.path);
165
+ result = err(options?.signal?.aborted &&
166
+ (cause === options.signal.reason ||
167
+ (cause instanceof Error && cause.name === "AbortError"))
168
+ ? new AnalysisCancelledError(OPERATION)
169
+ : pwntoolsDecoderFailure(cause, phase, selectedPath, executablePath, evidence, context));
170
+ }
171
+ if (root !== undefined) {
172
+ try {
173
+ await root.close();
174
+ }
175
+ catch (cause) {
176
+ return err(new ProviderCleanupError(this.identity.id, [root.path], {
177
+ reason: cause instanceof Error ? cause.message : String(cause),
178
+ previous_error: result.ok
179
+ ? null
180
+ : projectAnalysisError(result.error),
181
+ ...(result.ok
182
+ ? {
183
+ captured_output: {
184
+ stdout: result.value.diagnostics.stdout,
185
+ stderr: result.value.diagnostics.stderr,
186
+ truncated: result.value.diagnostics.truncated,
187
+ },
188
+ }
189
+ : {}),
190
+ }, { operation: OPERATION }));
191
+ }
192
+ }
193
+ return result.ok && options?.signal?.aborted
194
+ ? err(new AnalysisCancelledError(OPERATION, {
195
+ capturedOutput: {
196
+ stdout: result.value.diagnostics.stdout,
197
+ stderr: result.value.diagnostics.stderr,
198
+ truncated: result.value.diagnostics.truncated,
199
+ },
200
+ }))
201
+ : result;
202
+ }
203
+ }
@@ -0,0 +1,180 @@
1
+ import { ArtifactReaderFailure } from "../../artifacts/ArtifactReader.js";
2
+ import { AnalysisError, } from "../../domain/analysisErrorBase.js";
3
+ import { AnalysisAccessDeniedError, AnalysisArtifactChangedError, AnalysisCancelledError, AnalysisInputError, AnalysisOutputError, AnalysisTimeoutError, AnalysisResourceConstraintError, } from "../../domain/analysisErrorCore.js";
4
+ import { ProviderAdapterError } from "../../domain/providerAdapterError.js";
5
+ import { ProviderCleanupError } from "../../domain/providerCleanupError.js";
6
+ import { ProviderSelectionError } from "../../domain/providerSelectionError.js";
7
+ import { OwnedCommandFailure } from "../../process/OwnedCommand.js";
8
+ import { PWNTOOLS_PROVIDER_IDENTITY, PWNTOOLS_LIMITS, PWNTOOLS_MEMORY_FAILURE_EXIT, PWNTOOLS_FILE_SIZE_FAILURE_EXIT, } from "./PwntoolsRelease.js";
9
+ const DEFAULT_CONTEXT = {
10
+ operation: "inspect_binary_layout",
11
+ providerId: PWNTOOLS_PROVIDER_IDENTITY.id,
12
+ };
13
+ /** Keep read, engine, output and owned-lifecycle failure reasons distinct. */
14
+ export const pwntoolsDecoderFailure = (cause, phase, path, executablePath = path, failureEvidence = {}, context = DEFAULT_CONTEXT) => {
15
+ const limitReport = failureEvidence.limits;
16
+ if (cause instanceof AnalysisError)
17
+ return cause;
18
+ if (cause instanceof OwnedCommandFailure) {
19
+ const currentOutput = cause.snapshot === null
20
+ ? undefined
21
+ : capturedPwntoolsOutput(cause.snapshot);
22
+ const combinedOutput = context.precedingOutput === undefined
23
+ ? currentOutput
24
+ : currentOutput === undefined
25
+ ? context.precedingOutput
26
+ : {
27
+ stdout: `[core decoder]\n${context.precedingOutput.stdout}\n[debugger]\n${currentOutput.stdout}`,
28
+ stderr: `[core decoder]\n${context.precedingOutput.stderr}\n[debugger]\n${currentOutput.stderr}`,
29
+ truncated: context.precedingOutput.truncated || currentOutput.truncated,
30
+ };
31
+ const outputOptions = combinedOutput === undefined
32
+ ? undefined
33
+ : { capturedOutput: combinedOutput };
34
+ if (cause.cleanupFailure !== null)
35
+ return new ProviderCleanupError(context.providerId, cause.resources, {
36
+ reason: cause.cleanupFailure,
37
+ ...(combinedOutput === undefined
38
+ ? {}
39
+ : {
40
+ captured_output: { ...combinedOutput },
41
+ }),
42
+ previous_error: {
43
+ failure_kind: cause.reason,
44
+ message: cause.message,
45
+ exit_code: cause.snapshot?.exitCode ?? null,
46
+ signal: cause.snapshot?.signal ?? null,
47
+ stdout: cause.snapshot?.stdout.text ?? null,
48
+ stderr: cause.snapshot?.stderr.text ?? null,
49
+ },
50
+ }, { operation: context.operation, cause });
51
+ if (cause.reason === "cancelled")
52
+ return new AnalysisCancelledError(context.operation, outputOptions);
53
+ if (cause.reason === "timeout")
54
+ return new AnalysisTimeoutError(context.operation, PWNTOOLS_LIMITS.timeoutMs, outputOptions);
55
+ if (cause.reason === "output-limit")
56
+ return new AnalysisOutputError(context.operation, cause.message, outputOptions);
57
+ if (cause.reason === "process" &&
58
+ (cause.snapshot?.signal === "SIGXFSZ" ||
59
+ (failureEvidence.marker?.resource === "file-size" &&
60
+ cause.snapshot?.exitCode === PWNTOOLS_FILE_SIZE_FAILURE_EXIT &&
61
+ cause.snapshot.signal === null)))
62
+ return new AnalysisResourceConstraintError(context.operation, "file-size", (cause.snapshot.signal === "SIGXFSZ"
63
+ ? "Owned Python terminated with SIGXFSZ; the exact signal cause is unknown."
64
+ : "The owned Python bridge reported EFBIG while writing a file; the exact write failure cause is unknown.") +
65
+ (limitReport?.failure === null || limitReport === undefined
66
+ ? ""
67
+ : ` Effective limit report unavailable: ${limitReport.failure}`), limitReport?.limits ?? null, outputOptions);
68
+ if (cause.reason === "process" && cause.snapshot?.signal === "SIGXCPU")
69
+ return new AnalysisResourceConstraintError(context.operation, "cpu", "Owned Python terminated with SIGXCPU; the exact signal cause is unknown." +
70
+ (limitReport?.failure === null || limitReport === undefined
71
+ ? ""
72
+ : ` Effective limit report unavailable: ${limitReport.failure}`), limitReport?.limits ?? null, outputOptions);
73
+ if (cause.reason === "process" &&
74
+ failureEvidence.marker?.resource === "memory" &&
75
+ cause.snapshot?.exitCode === PWNTOOLS_MEMORY_FAILURE_EXIT &&
76
+ cause.snapshot.signal === null)
77
+ return new AnalysisResourceConstraintError(context.operation, "memory", "The owned Python bridge reported a memory allocation failure without a structured reply; the exact allocation cause is unknown." +
78
+ (limitReport?.failure === null || limitReport === undefined
79
+ ? ""
80
+ : ` Effective limit report unavailable: ${limitReport.failure}`), limitReport?.limits ?? null, outputOptions);
81
+ }
82
+ if (cause instanceof Error &&
83
+ "code" in cause &&
84
+ "syscall" in cause &&
85
+ typeof cause.syscall === "string" &&
86
+ cause.syscall.startsWith("spawn "))
87
+ return pwntoolsUnavailable(`Selected Python could not launch (${String(cause.code)}): ${executablePath}. Check the configured executable, its interpreter and host execute access.`, executablePath, String(cause.code), undefined, context);
88
+ if (cause instanceof ArtifactReaderFailure) {
89
+ if (cause.reason === "integrity")
90
+ return new AnalysisArtifactChangedError(context.operation, path, cause.message, {
91
+ cause,
92
+ });
93
+ if (cause.reason === "cancelled")
94
+ return new AnalysisCancelledError(context.operation);
95
+ if (cause.reason === "limit" ||
96
+ cause.reason === "path" ||
97
+ cause.reason === "format")
98
+ return new AnalysisInputError(context.operation, { cause }, [
99
+ {
100
+ path: ["path"],
101
+ reason: cause.reason === "limit" ? "out_of_range" : "invalid_format",
102
+ message: cause.message,
103
+ },
104
+ ]);
105
+ }
106
+ if (cause instanceof Error && "code" in cause) {
107
+ if (phase === "configuration")
108
+ return pwntoolsUnavailable(`Selected Python executable is unavailable (${String(cause.code)}): ${path}. Check the caller-selected path and execute access.`, path, String(cause.code), undefined, context);
109
+ if (phase === "artifact-read" &&
110
+ (cause.code === "EACCES" || cause.code === "EPERM"))
111
+ return new AnalysisAccessDeniedError(context.operation, path, cause.code, {
112
+ cause,
113
+ });
114
+ if (phase === "artifact-read" &&
115
+ (cause.code === "ENOENT" || cause.code === "ENOTDIR"))
116
+ return new AnalysisInputError(context.operation, { cause }, [
117
+ {
118
+ path: ["path"],
119
+ reason: "invalid_value",
120
+ message: `Selected object could not be read (${String(cause.code)}): ${path}.`,
121
+ },
122
+ ]);
123
+ }
124
+ return new ProviderAdapterError(context.providerId, context.operation, {
125
+ cause,
126
+ diagnostics: {
127
+ phase,
128
+ path,
129
+ ...(context.precedingOutput === undefined
130
+ ? {}
131
+ : { preceding_output: { ...context.precedingOutput } }),
132
+ reason: cause instanceof Error ? cause.message : String(cause),
133
+ ...(failureEvidence.marker === undefined
134
+ ? {}
135
+ : { resource_failure_marker: { ...failureEvidence.marker } }),
136
+ ...(cause instanceof OwnedCommandFailure
137
+ ? {
138
+ failure_kind: cause.reason,
139
+ exit_code: cause.snapshot?.exitCode ?? null,
140
+ signal: cause.snapshot?.signal ?? null,
141
+ stdout: cause.snapshot?.stdout.text ?? null,
142
+ stderr: cause.snapshot?.stderr.text ?? null,
143
+ ...(cause.snapshot === null
144
+ ? {}
145
+ : {
146
+ captured_output: {
147
+ ...capturedPwntoolsOutput(cause.snapshot),
148
+ },
149
+ }),
150
+ }
151
+ : {}),
152
+ },
153
+ });
154
+ };
155
+ /** Report caller-selected engine absence with its actual configuration and host constraint. */
156
+ export const pwntoolsUnavailable = (reason, path, systemCode, capturedOutput, context = DEFAULT_CONTEXT) => new ProviderSelectionError({
157
+ ...(capturedOutput === undefined ? {} : { capturedOutput }),
158
+ operation: context.operation,
159
+ reason: "provider_unavailable",
160
+ requestedProviderId: context.providerId,
161
+ candidateIds: [context.providerId],
162
+ rejections: [
163
+ {
164
+ providerId: context.providerId,
165
+ code: "provider_unavailable",
166
+ reason,
167
+ diagnostics: {
168
+ configuration_key: "REA_PWNTOOLS_PYTHON",
169
+ executable_path: path,
170
+ ...(systemCode === undefined ? {} : { system_code: systemCode }),
171
+ },
172
+ },
173
+ ],
174
+ });
175
+ /** Retain bounded text output and the shared supervisor's observed truncation flag. */
176
+ export const capturedPwntoolsOutput = (snapshot) => ({
177
+ stdout: snapshot.stdout.text,
178
+ stderr: snapshot.stderr.text,
179
+ truncated: snapshot.diagnosticTruncated === true,
180
+ });
@@ -0,0 +1,36 @@
1
+ import { binaryLayoutPayloadSchema, binaryLayoutSchema, } from "../../domain/native/binaryLayout.js";
2
+ import { AnalysisOutputError } from "../../domain/analysisErrorCore.js";
3
+ import { PwntoolsDecoder } from "./PwntoolsDecoder.js";
4
+ import { PWNTOOLS_PROVIDER_IDENTITY } from "./PwntoolsRelease.js";
5
+ import { PrivateRuntimeRoot } from "../../process/PrivateRuntimeRoot.js";
6
+ /** ELF-specific projection around the shared owned pwntools decoder boundary. */
7
+ export class PwntoolsLayoutProvider extends PwntoolsDecoder {
8
+ constructor(environment, launcher, createRuntime = () => PrivateRuntimeRoot.create({ prefix: "rea-elf-layout-" })) {
9
+ super({
10
+ identity: PWNTOOLS_PROVIDER_IDENTITY,
11
+ operation: "inspect_binary_layout",
12
+ bridge: new URL("../../../bridge/pwntools/layout.py", import.meta.url),
13
+ payloadSchema: binaryLayoutPayloadSchema,
14
+ project: ({ input, payload, snapshot, diagnostics }) => {
15
+ const value = binaryLayoutSchema.safeParse({
16
+ ...payload,
17
+ artifact: {
18
+ path: input.path,
19
+ sha256: snapshot.sha256,
20
+ bytes: snapshot.bytes.length,
21
+ },
22
+ diagnostics,
23
+ });
24
+ if (!value.success)
25
+ throw new AnalysisOutputError("inspect_binary_layout", `ELF reply contains invalid source ranges or value meanings: ${value.error.issues[0]?.message ?? "schema mismatch"}`, { capturedOutput: diagnostics });
26
+ for (const table of value.data.packed_relative_relocations) {
27
+ const start = Number(BigInt(table.location.offset));
28
+ const length = Number(BigInt(table.location.bytes));
29
+ if (!Buffer.from(table.encoded_bytes_base64, "base64").equals(snapshot.bytes.subarray(start, start + length)))
30
+ throw new AnalysisOutputError("inspect_binary_layout", "Reported packed relocation bytes differ from the selected snapshot at their original file range.", { capturedOutput: diagnostics });
31
+ }
32
+ return value.data;
33
+ },
34
+ }, environment, launcher, createRuntime);
35
+ }
36
+ }
@@ -0,0 +1,75 @@
1
+ import { AnalysisOutputError } from "../../domain/analysisErrorCore.js";
2
+ import { recordedCrashPayloadSchema, recordedCrashSchema, } from "../../domain/native/recordedCrash.js";
3
+ import { PrivateRuntimeRoot } from "../../process/PrivateRuntimeRoot.js";
4
+ import { PwntoolsDecoder } from "./PwntoolsDecoder.js";
5
+ import { PWNTOOLS_PROVIDER_IDENTITY } from "./PwntoolsRelease.js";
6
+ import { validateRecordedCrashSources } from "./RecordedCrashSourceBindings.js";
7
+ import { validateRecordedCrashStructure } from "./RecordedCrashStructureBindings.js";
8
+ import { inspectPwndbgCore, recordedCrashStageOutput, } from "../pwndbg/PwndbgCoreContext.js";
9
+ /** Preserve every recorded thread while optionally enriching the same owned snapshot. */
10
+ export class PwntoolsRecordedCrashProvider extends PwntoolsDecoder {
11
+ constructor(environment, launcher, createRuntime = () => PrivateRuntimeRoot.create({ prefix: "rea-recorded-crash-" })) {
12
+ super({
13
+ identity: PWNTOOLS_PROVIDER_IDENTITY,
14
+ operation: "inspect_recorded_crash",
15
+ bridge: new URL("../../../bridge/pwntools/recorded_crash.py", import.meta.url),
16
+ payloadSchema: recordedCrashPayloadSchema,
17
+ project: async ({ input, payload, snapshot, rootPath, diagnostics, options, }) => {
18
+ const validate = (debuggerContext) => recordedCrashSchema.safeParse({
19
+ ...payload,
20
+ artifact: {
21
+ path: input.path,
22
+ sha256: snapshot.sha256,
23
+ bytes: snapshot.bytes.length,
24
+ },
25
+ debugger: debuggerContext,
26
+ decoder_diagnostics: diagnostics,
27
+ diagnostics: debuggerContext.status === "available"
28
+ ? recordedCrashStageOutput(diagnostics, debuggerContext.diagnostics)
29
+ : diagnostics,
30
+ });
31
+ const checked = validate({ status: "not-requested" });
32
+ if (!checked.success)
33
+ throw new AnalysisOutputError("inspect_recorded_crash", `Recorded core reply is invalid: ${checked.error.issues[0]?.message ?? "schema mismatch"}`, { capturedOutput: diagnostics });
34
+ const value = checked.data;
35
+ validateRecordedCrashStructure(value, snapshot.bytes, diagnostics);
36
+ const checkBytes = (raw, range) => {
37
+ const start = Number(BigInt(range.offset));
38
+ const length = Number(BigInt(range.bytes));
39
+ if (!Buffer.from(raw, "base64").equals(snapshot.bytes.subarray(start, start + length)))
40
+ throw new AnalysisOutputError("inspect_recorded_crash", "Reported raw note bytes differ from the selected snapshot.", { capturedOutput: diagnostics });
41
+ };
42
+ for (const note of value.notes) {
43
+ checkBytes(note.owner_bytes_base64, note.owner_location);
44
+ checkBytes(note.descriptor_bytes_base64, note.descriptor_location);
45
+ }
46
+ for (const padding of value.note_padding)
47
+ checkBytes(padding.bytes_base64, padding.location);
48
+ validateRecordedCrashSources(value, snapshot.bytes, diagnostics);
49
+ for (const thread of value.threads)
50
+ for (const register of thread.registers) {
51
+ const start = Number(BigInt(register.location.offset));
52
+ if (snapshot.bytes.readBigUInt64LE(start) !== BigInt(register.value))
53
+ throw new AnalysisOutputError("inspect_recorded_crash", "Reported register value differs from its recorded source bytes.", { capturedOutput: diagnostics });
54
+ }
55
+ if (!input.include_debugger_context)
56
+ return value;
57
+ const context = await inspectPwndbgCore({
58
+ environment,
59
+ rootPath,
60
+ diagnostics,
61
+ options,
62
+ ...(launcher === undefined ? {} : { launcher }),
63
+ });
64
+ const enriched = validate(context);
65
+ if (!enriched.success)
66
+ throw new AnalysisOutputError("inspect_recorded_crash", `Recorded debugger context is invalid: ${enriched.error.issues[0]?.message ?? "schema mismatch"}`, {
67
+ capturedOutput: context.status === "available"
68
+ ? recordedCrashStageOutput(diagnostics, context.diagnostics)
69
+ : diagnostics,
70
+ });
71
+ return enriched.data;
72
+ },
73
+ }, environment, launcher, createRuntime);
74
+ }
75
+ }
@@ -0,0 +1,19 @@
1
+ /** Verified unchanged upstream profile for the Linux ELF adapter. */
2
+ export const PWNTOOLS_PROVIDER_IDENTITY = {
3
+ id: "pwntools-elf",
4
+ name: "REA pwntools ELF adapter",
5
+ version: "pwntools@4.15.0;pyelftools@0.33;unicorn@2.1.2",
6
+ };
7
+ /** Bridge-reserved status for MemoryError when no structured reply can be written. */
8
+ export const PWNTOOLS_MEMORY_FAILURE_EXIT = 75;
9
+ /** Bridge-reserved status for an observed EFBIG failure without a complete reply. */
10
+ export const PWNTOOLS_FILE_SIZE_FAILURE_EXIT = 76;
11
+ /** Complete evidence budgets; address-space is separate from resident memory. */
12
+ export const PWNTOOLS_LIMITS = {
13
+ inputBytes: 32 * 1024 * 1024,
14
+ outputBytes: 64 * 1024 * 1024,
15
+ diagnosticBytes: 1024 * 1024,
16
+ timeoutMs: 30_000,
17
+ addressSpaceBytes: 3 * 1024 * 1024 * 1024,
18
+ cpuSeconds: 30,
19
+ };
@@ -0,0 +1,76 @@
1
+ import { join } from "node:path";
2
+ import { readStableArtifact } from "../../artifacts/readStableArtifact.js";
3
+ import { OwnedCommandFailure } from "../../process/OwnedCommand.js";
4
+ import { PWNTOOLS_MEMORY_FAILURE_EXIT, PWNTOOLS_FILE_SIZE_FAILURE_EXIT, } from "./PwntoolsRelease.js";
5
+ import { z } from "zod";
6
+ import { PWNTOOLS_LIMITS } from "./PwntoolsRelease.js";
7
+ /** Actual limits reported by the owned bridge after lowering inherited soft limits. */
8
+ export const pwntoolsResourceLimitsSchema = z.strictObject({
9
+ address_space_bytes: z
10
+ .number()
11
+ .int()
12
+ .nonnegative()
13
+ .max(PWNTOOLS_LIMITS.addressSpaceBytes),
14
+ cpu_seconds: z.number().int().nonnegative().max(PWNTOOLS_LIMITS.cpuSeconds),
15
+ file_size_bytes: z
16
+ .number()
17
+ .int()
18
+ .nonnegative()
19
+ .max(PWNTOOLS_LIMITS.outputBytes),
20
+ });
21
+ /** Read only bounded owned reports; unverified reserved statuses stay unclassified. */
22
+ export const readPwntoolsFailureEvidence = async (cause, rootPath) => {
23
+ let limitReport;
24
+ let marker;
25
+ if (cause instanceof OwnedCommandFailure &&
26
+ cause.snapshot?.signal === null &&
27
+ (cause.snapshot.exitCode === PWNTOOLS_MEMORY_FAILURE_EXIT ||
28
+ cause.snapshot.exitCode === PWNTOOLS_FILE_SIZE_FAILURE_EXIT) &&
29
+ rootPath !== undefined) {
30
+ try {
31
+ const reported = await readStableArtifact(join(rootPath, "resource.failure"), 1);
32
+ const expected = cause.snapshot.exitCode === PWNTOOLS_MEMORY_FAILURE_EXIT ? "M" : "F";
33
+ if (!reported.bytes.equals(Buffer.from(expected, "ascii")))
34
+ throw new Error("Private bridge failure marker does not match the observed exit status.");
35
+ marker = {
36
+ resource: expected === "M" ? "memory" : "file-size",
37
+ failure: null,
38
+ };
39
+ }
40
+ catch (markerFailure) {
41
+ marker = {
42
+ resource: null,
43
+ failure: markerFailure instanceof Error
44
+ ? markerFailure.message
45
+ : String(markerFailure),
46
+ };
47
+ }
48
+ }
49
+ if (cause instanceof OwnedCommandFailure &&
50
+ (cause.snapshot?.signal === "SIGXCPU" ||
51
+ cause.snapshot?.signal === "SIGXFSZ" ||
52
+ ((cause.snapshot?.exitCode === PWNTOOLS_FILE_SIZE_FAILURE_EXIT ||
53
+ cause.snapshot?.exitCode === PWNTOOLS_MEMORY_FAILURE_EXIT) &&
54
+ cause.snapshot.signal === null)) &&
55
+ rootPath !== undefined) {
56
+ try {
57
+ const reported = await readStableArtifact(join(rootPath, "limits.json"), 4096);
58
+ limitReport = {
59
+ limits: pwntoolsResourceLimitsSchema.parse(JSON.parse(reported.bytes.toString("utf8"))),
60
+ failure: null,
61
+ };
62
+ }
63
+ catch (reportFailure) {
64
+ limitReport = {
65
+ limits: null,
66
+ failure: reportFailure instanceof Error
67
+ ? reportFailure.message
68
+ : String(reportFailure),
69
+ };
70
+ }
71
+ }
72
+ return {
73
+ ...(limitReport === undefined ? {} : { limits: limitReport }),
74
+ ...(marker === undefined ? {} : { marker }),
75
+ };
76
+ };
@@ -0,0 +1,46 @@
1
+ import { AnalysisOutputError } from "../../domain/analysisErrorCore.js";
2
+ import { RECORDED_LINUX_AMD64_REGISTERS, } from "../../domain/native/recordedCrash.js";
3
+ const registerOffsets = new Map(RECORDED_LINUX_AMD64_REGISTERS.map((name, index) => [name, 112 + index * 8]));
4
+ /** Bind interpreted values and register identities to Linux amd64 source bytes. */
5
+ export function validateRecordedCrashSources(report, snapshot, diagnostics) {
6
+ const fail = () => {
7
+ throw new AnalysisOutputError("inspect_recorded_crash", "Reported PID or signal value differs from its recorded Linux amd64 source bytes.", { capturedOutput: diagnostics });
8
+ };
9
+ const descriptor = (noteIndex, minimumBytes) => {
10
+ const note = report.notes[noteIndex];
11
+ if (note === undefined)
12
+ return fail();
13
+ const start = Number(BigInt(note.descriptor_location.offset));
14
+ const length = Number(BigInt(note.descriptor_location.bytes));
15
+ if (length < minimumBytes)
16
+ return fail();
17
+ return snapshot.subarray(start, start + length);
18
+ };
19
+ // Fixed offsets independently checked against unchanged pwntools 4.15.0
20
+ // elf_prstatus_amd64 / elf_siginfo_64; this is the sole supported note ABI.
21
+ for (const thread of report.threads) {
22
+ const bytes = descriptor(thread.note_index, 336);
23
+ if (thread.historical_pid !== bytes.readInt32LE(32) ||
24
+ thread.recorded_current_signal !== bytes.readInt16LE(12))
25
+ fail();
26
+ const note = report.notes[thread.note_index];
27
+ if (note === undefined)
28
+ return fail();
29
+ for (const register of thread.registers) {
30
+ const offset = registerOffsets.get(register.name);
31
+ if (offset === undefined ||
32
+ BigInt(register.location.offset) !==
33
+ BigInt(note.descriptor_location.offset) + BigInt(offset))
34
+ throw new AnalysisOutputError("inspect_recorded_crash", "Reported register source does not match its Linux amd64 register identity.", { capturedOutput: diagnostics });
35
+ }
36
+ }
37
+ for (const signal of report.signals) {
38
+ const bytes = descriptor(signal.note_index, 32);
39
+ if (signal.number !== bytes.readInt32LE(0) ||
40
+ signal.errno !== bytes.readInt32LE(4) ||
41
+ signal.code !== bytes.readInt32LE(8) ||
42
+ (signal.fault_address !== null &&
43
+ BigInt(signal.fault_address) !== bytes.readBigUInt64LE(16)))
44
+ fail();
45
+ }
46
+ }