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
@@ -1,5 +1,5 @@
1
1
  import { createJavaScriptSemanticGraphUnknown } from "../../domain/javascript/javascriptSemanticGraph.js";
2
- import { addSemanticGraphNode, addSemanticGraphRelation, addSemanticGraphUnknown, constructSemanticGraphNode, } from "./JavaScriptSemanticGraphConstruction.js";
2
+ import { retainSemanticGraphNode, addSemanticGraphRelation, addSemanticGraphUnknown, } from "./JavaScriptSemanticGraphConstruction.js";
3
3
  import { unknownSemanticEvidence } from "./JavaScriptSemanticGraphEvidence.js";
4
4
  import { semanticPropertySlot } from "./JavaScriptSemanticGraphValueProjection.js";
5
5
  /** Project static object reads, writes, spreads, and destructuring. */
@@ -45,7 +45,7 @@ export const projectSemanticObjects = (context) => {
45
45
  addObjectUnknown(context, operation, slot ?? occurrence);
46
46
  }
47
47
  };
48
- const addObjectOccurrence = (context, operation) => addSemanticGraphNode(context.state, constructSemanticGraphNode(context.file, {
48
+ const addObjectOccurrence = (context, operation) => retainSemanticGraphNode(context.state, context.file, {
49
49
  kind: "expression",
50
50
  roleKey: operation.objectOperationId,
51
51
  location: operation.location,
@@ -60,7 +60,7 @@ const addObjectOccurrence = (context, operation) => addSemanticGraphNode(context
60
60
  operation_kind: operation.kind,
61
61
  property_name: operation.propertyName,
62
62
  },
63
- }, context.state));
63
+ });
64
64
  const addObjectUnknown = (context, operation, node) => {
65
65
  const relation = operation.kind === "read"
66
66
  ? "reads-property"
@@ -1,18 +1,124 @@
1
1
  import { compareCodePoints } from "../../domain/canonicalOrdering.js";
2
2
  import { JAVASCRIPT_SEMANTIC_RELATION_FAMILIES } from "../../domain/javascript/javascriptSemanticGraphSchemas.js";
3
- /** Find the innermost callable that contains one exact source range. */
4
- export const owningSemanticCallableNode = (location, ir, nodes) => {
5
- if (location === null)
6
- return undefined;
7
- return ir.callables
8
- .filter((callable) => contains(callable.location, location))
9
- .sort((left, right) => compareCodePoints(positionKey(right.location), positionKey(left.location)))
10
- .map(({ callableId }) => nodes.get(callableId))
11
- .find((node) => node !== undefined);
3
+ /** Index one immutable file IR; read retained callable membership at query time. */
4
+ export const createSemanticCallableOwnerLookup = (ir, nodes) => {
5
+ const entries = ir.callables
6
+ .map((callable, order) => ({
7
+ callableId: callable.callableId,
8
+ range: callable.location,
9
+ order,
10
+ priority: positionKey(callable.location),
11
+ }))
12
+ .sort((left, right) => pointCompare(left.range.start, right.range.start) ||
13
+ left.order - right.order);
14
+ const tree = buildSourceIntervalTree(entries, 0, entries.length);
15
+ return (location) => {
16
+ if (location === null || tree === null || nodes.size === 0)
17
+ return undefined;
18
+ const pending = [tree];
19
+ let best;
20
+ let result;
21
+ while (pending.length > 0) {
22
+ const current = pending.pop();
23
+ if (current === undefined ||
24
+ pointCompare(current.minimumStart, location.start) > 0 ||
25
+ pointCompare(current.maximumEnd, location.end) < 0)
26
+ continue;
27
+ const candidate = current.entry;
28
+ if (contains(candidate.range, location)) {
29
+ const node = nodes.get(candidate.callableId);
30
+ const rank = best === undefined
31
+ ? 1
32
+ : compareCodePoints(candidate.priority, best.priority) ||
33
+ best.order - candidate.order;
34
+ if (node !== undefined && rank > 0) {
35
+ best = candidate;
36
+ result = node;
37
+ }
38
+ }
39
+ if (current.left !== null)
40
+ pending.push(current.left);
41
+ if (current.right !== null)
42
+ pending.push(current.right);
43
+ }
44
+ return result;
45
+ };
12
46
  };
13
- /** Find retained semantic nodes whose exact ranges are within one expression. */
14
- export const semanticNodesWithinRange = (nodes, range) => nodes.filter((node) => node.identity.source_range !== null &&
15
- contains(range, node.identity.source_range));
47
+ const buildSourceIntervalTree = (entries, start, end) => {
48
+ if (start >= end)
49
+ return null;
50
+ const middle = Math.floor((start + end) / 2);
51
+ const entry = entries[middle];
52
+ if (entry === undefined)
53
+ return null;
54
+ // This recursion follows a balanced index, not the producer's nesting depth.
55
+ const left = buildSourceIntervalTree(entries, start, middle);
56
+ const right = buildSourceIntervalTree(entries, middle + 1, end);
57
+ let minimumEnd = entry.range.end;
58
+ let maximumEnd = entry.range.end;
59
+ for (const child of [left, right]) {
60
+ if (child !== null && pointCompare(child.minimumEnd, minimumEnd) < 0)
61
+ minimumEnd = child.minimumEnd;
62
+ if (child !== null && pointCompare(child.maximumEnd, maximumEnd) > 0)
63
+ maximumEnd = child.maximumEnd;
64
+ }
65
+ return {
66
+ entry,
67
+ minimumStart: left?.minimumStart ?? entry.range.start,
68
+ maximumStart: right?.maximumStart ?? entry.range.start,
69
+ minimumEnd,
70
+ maximumEnd,
71
+ left,
72
+ right,
73
+ };
74
+ };
75
+ /** Index an immutable node list; return contained ranges in producer order. */
76
+ export const createSemanticNodeRangeLookup = (nodes) => {
77
+ const entries = nodes
78
+ .flatMap((node, order) => {
79
+ const range = node.identity.source_range;
80
+ return range === null ? [] : [{ node, range, order }];
81
+ })
82
+ .sort((left, right) => pointCompare(left.range.start, right.range.start) ||
83
+ left.order - right.order);
84
+ const tree = buildSourceIntervalTree(entries, 0, entries.length);
85
+ return (range) => {
86
+ if (tree === null)
87
+ return [];
88
+ const pending = [tree];
89
+ const matches = [];
90
+ while (pending.length > 0) {
91
+ const current = pending.pop();
92
+ if (current === undefined ||
93
+ pointCompare(current.maximumStart, range.start) < 0 ||
94
+ pointCompare(current.minimumEnd, range.end) > 0)
95
+ continue;
96
+ if (contains(range, current.entry.range))
97
+ matches.push(current.entry);
98
+ if (current.left !== null)
99
+ pending.push(current.left);
100
+ if (current.right !== null)
101
+ pending.push(current.right);
102
+ }
103
+ return matches
104
+ .sort((left, right) => left.order - right.order)
105
+ .map(({ node }) => node);
106
+ };
107
+ };
108
+ /** Index exact call ranges, preserving the first producer occurrence. */
109
+ export const createSemanticCallSiteLookup = (ir, nodes) => {
110
+ const identifiers = new Map();
111
+ for (const call of ir.callSites) {
112
+ const key = rangeKey(call.location);
113
+ if (!identifiers.has(key))
114
+ identifiers.set(key, call.callSiteId);
115
+ }
116
+ return (range) => {
117
+ const identifier = identifiers.get(rangeKey(range));
118
+ return identifier === undefined ? undefined : nodes.get(identifier);
119
+ };
120
+ };
121
+ const rangeKey = (range) => `${range.start.line}:${range.start.column}-${range.end.line}:${range.end.column}`;
16
122
  /** Report extractor support without treating missing families as absence. */
17
123
  export const semanticFamilyStatus = (family, analysis) => {
18
124
  if (![
@@ -1,5 +1,5 @@
1
1
  import { createJavaScriptSemanticGraphUnknown } from "../../domain/javascript/javascriptSemanticGraph.js";
2
- import { addSemanticGraphNode, addSemanticGraphRelation, addSemanticGraphUnknown, constructSemanticGraphNode, } from "./JavaScriptSemanticGraphConstruction.js";
2
+ import { retainSemanticGraphNode, addSemanticGraphRelation, addSemanticGraphUnknown, } from "./JavaScriptSemanticGraphConstruction.js";
3
3
  import { unknownSemanticEvidence } from "./JavaScriptSemanticGraphEvidence.js";
4
4
  import { semanticCallSiteAt, } from "./JavaScriptSemanticGraphFlowProjection.js";
5
5
  /** Project built-in resource acquisition and exact local release handles. */
@@ -21,7 +21,7 @@ export const projectSemanticResources = (context) => {
21
21
  else
22
22
  projectRelease(context, operation, resourceNodes);
23
23
  };
24
- const addResourceNode = (context, operation) => addSemanticGraphNode(context.state, constructSemanticGraphNode(context.file, {
24
+ const addResourceNode = (context, operation) => retainSemanticGraphNode(context.state, context.file, {
25
25
  kind: "resource",
26
26
  roleKey: operation.resourceId,
27
27
  location: operation.location,
@@ -31,7 +31,7 @@ const addResourceNode = (context, operation) => addSemanticGraphNode(context.sta
31
31
  : (context.callableNodes.get(operation.ownerCallableId)?.node_id ??
32
32
  null),
33
33
  properties: { method: operation.method },
34
- }, context.state));
34
+ });
35
35
  const projectRelease = (context, operation, resourceNodes) => {
36
36
  const callSite = semanticCallSiteAt(context, operation.location);
37
37
  const resources = operation.linkedResourceIds.flatMap((identifier) => {
@@ -1,4 +1,4 @@
1
- import { addSemanticGraphNode, addSemanticGraphRelation, constructSemanticGraphNode, } from "./JavaScriptSemanticGraphConstruction.js";
1
+ import { retainSemanticGraphNode, addSemanticGraphRelation, } from "./JavaScriptSemanticGraphConstruction.js";
2
2
  /** Project bounded literal values and object slots for exact query seeds. */
3
3
  export const projectSemanticValues = (context) => {
4
4
  for (const binding of context.ir.bindings) {
@@ -55,16 +55,16 @@ const projectValue = (input) => {
55
55
  });
56
56
  }
57
57
  };
58
- const addLiteralNode = (context, binding, value, role) => addSemanticGraphNode(context.state, constructSemanticGraphNode(context.file, {
58
+ const addLiteralNode = (context, binding, value, role) => retainSemanticGraphNode(context.state, context.file, {
59
59
  kind: "literal",
60
60
  roleKey: `literal:${binding.bindingId}:${role}:${JSON.stringify(value)}`,
61
61
  location: binding.definitions[0]?.location ?? null,
62
62
  label: JSON.stringify(value),
63
63
  functionNodeId: context.bindingNodes.get(binding.bindingId)?.function_node_id ?? null,
64
64
  properties: { value },
65
- }, context.state));
65
+ });
66
66
  /** Create one canonical property slot for a binding/property identity. */
67
- export const semanticPropertySlot = (context, objectBindingId, name) => addSemanticGraphNode(context.state, constructSemanticGraphNode(context.file, {
67
+ export const semanticPropertySlot = (context, objectBindingId, name) => retainSemanticGraphNode(context.state, context.file, {
68
68
  kind: "property-slot",
69
69
  roleKey: `property:${objectBindingId}:${name}`,
70
70
  location: null,
@@ -74,4 +74,4 @@ export const semanticPropertySlot = (context, objectBindingId, name) => addSeman
74
74
  name,
75
75
  object_binding_id: objectBindingId,
76
76
  },
77
- }, context.state));
77
+ });
@@ -1,14 +1,14 @@
1
1
  import { readFile } from "node:fs/promises";
2
- import { AnalysisError } from "../domain/analysisErrorBase.js";
3
- import { AnalysisInputError } from "../domain/analysisErrorCore.js";
4
- import { projectAnalysisError } from "../domain/analysisErrorProjection.js";
5
- import { createEvidence, parseEvidence } from "../domain/evidence.js";
6
- import { jsonValueSchema } from "../domain/jsonValue.js";
7
- import { projectInputIssues } from "../domain/inputIssueProjection.js";
8
- import { processTraceSpecificationSchema } from "../domain/processTraceComparison.js";
9
- import { processScenarioSchema } from "../domain/processScenario.js";
10
- import { compareProcessCaptures, parseProcessCapture, } from "../domain/processCapture.js";
11
- import { captureProcessScenario } from "./ProcessHarness.js";
2
+ import { AnalysisError } from "../../domain/analysisErrorBase.js";
3
+ import { AnalysisInputError } from "../../domain/analysisErrorCore.js";
4
+ import { projectAnalysisError } from "../../domain/analysisErrorProjection.js";
5
+ import { createEvidence, parseEvidence } from "../../domain/evidence.js";
6
+ import { jsonValueSchema } from "../../domain/jsonValue.js";
7
+ import { projectInputIssues } from "../../domain/inputIssueProjection.js";
8
+ import { processTraceSpecificationSchema } from "../../domain/process/processTraceComparison.js";
9
+ import { processScenarioSchema } from "../../domain/process/processScenario.js";
10
+ import { compareProcessCaptures, parseProcessCapture, } from "../../domain/process/processCapture.js";
11
+ import { captureProcessScenario } from "../../process/capture/ProcessHarness.js";
12
12
  import { PROCESS_PROVIDER, createProcessCaptureEvidence, } from "./ProcessEvidence.js";
13
13
  /** Identify the process workflow's typed diagnostic result for CLI exit status. */
14
14
  export const isProcessCliFailure = (value) => typeof value === "object" &&
@@ -1,7 +1,7 @@
1
- import { createEvidence } from "../domain/evidence.js";
2
- import { PROCESS_PROVIDER } from "../domain/processEvidenceProvider.js";
3
- import { jsonValueSchema } from "../domain/jsonValue.js";
4
- export { PROCESS_PROVIDER } from "../domain/processEvidenceProvider.js";
1
+ import { createEvidence } from "../../domain/evidence.js";
2
+ import { PROCESS_PROVIDER } from "../../domain/process/processEvidenceProvider.js";
3
+ import { jsonValueSchema } from "../../domain/jsonValue.js";
4
+ export { PROCESS_PROVIDER } from "../../domain/process/processEvidenceProvider.js";
5
5
  /** Project one process scenario into secret-free Evidence parameters. */
6
6
  const processEvidenceParameters = (scenario) => ({
7
7
  executable_name: scenario.executable.split("/").at(-1) ?? scenario.executable,
@@ -1,8 +1,9 @@
1
1
  import { createAnalysisExecution, } from "../application/AnalysisProvider.js";
2
2
  import { inspectBundleKeyedArchive } from "./apple/KeyedArchiveReader.js";
3
+ import { traceDylibResolution } from "./apple/DylibResolutionReader.js";
3
4
  import { basename, dirname } from "node:path";
4
- import { inventoryArtifact } from "../application/ArtifactInventory.js";
5
- import { extractArtifact } from "../application/ArtifactExtraction.js";
5
+ import { inventoryArtifact } from "./inventory/ArtifactInventory.js";
6
+ import { extractArtifact } from "./extraction/ArtifactExtraction.js";
6
7
  import { analyzeInterfaceBuilderBundle } from "./apple/InterfaceBuilderAnalysis.js";
7
8
  import { analyzeAppleAssetCatalogs } from "./apple/AppleAssetCatalogAnalysis.js";
8
9
  import { ARTIFACT_ANALYSIS_OPERATIONS, artifactInventoryInputSchema, artifactExtractionExecutionSchema, } from "../contracts/artifactToolContracts.js";
@@ -15,7 +16,7 @@ import { ArtifactReaderFailure } from "./ArtifactReader.js";
15
16
  import { ARTIFACT_PROVIDER_IDENTITY as IDENTITY, artifactCapabilities, } from "./ArtifactProviderMetadata.js";
16
17
  import { createEvidence } from "../domain/evidence.js";
17
18
  import { createArtifactInspection } from "../domain/artifactInspection.js";
18
- import { resolveArtifactIntegrityPolicy } from "../application/ArtifactInventory/policy.js";
19
+ import { resolveArtifactIntegrityPolicy } from "./inventory/policy.js";
19
20
  /** Read-only inventory and exclusively owned extraction provider. */
20
21
  export class ArtifactProvider {
21
22
  #capabilities;
@@ -98,6 +99,8 @@ class ArtifactClient {
98
99
  if (operation === "inspect_asset_catalog") {
99
100
  return await this.inspectAssetCatalog(parameters, options);
100
101
  }
102
+ if (operation === "trace_dylib_resolution")
103
+ return await this.traceDylibResolution(parameters, options);
101
104
  if (operation === "extract_artifact") {
102
105
  const parsed = artifactExtractionExecutionSchema.parse(parameters);
103
106
  const result = await extractArtifact({
@@ -154,6 +157,30 @@ class ArtifactClient {
154
157
  })),
155
158
  }));
156
159
  }
160
+ async traceDylibResolution(parameters, options) {
161
+ if (this.target.kind !== "executable" || this.target.format !== "mach-o")
162
+ throw new ArtifactReaderFailure("unavailable", "trace_dylib_resolution requires an active Mach-O or .app bundle target");
163
+ // Only a target opened from an app bundle directory carries its Info.plist;
164
+ // a regular file whose name ends in .app is a standalone image.
165
+ const bundle = this.target.bundleInfoPlist === undefined
166
+ ? undefined
167
+ : this.target.sourcePath;
168
+ const result = await traceDylibResolution({
169
+ rootPath: bundle ?? dirname(this.target.path),
170
+ targetPath: this.target.path,
171
+ targetSha256: this.target.sha256,
172
+ enumerateRoots: bundle !== undefined,
173
+ parameters,
174
+ ...(options?.signal === undefined ? {} : { signal: options.signal }),
175
+ });
176
+ return ok(createAnalysisExecution(result, IDENTITY, {
177
+ limitations: result.limitations,
178
+ locations: result.images.map(({ path }) => ({
179
+ kind: "artifact-path",
180
+ path,
181
+ })),
182
+ }));
183
+ }
157
184
  async inspectArtifact(parameters, options) {
158
185
  const parsed = artifactInventoryInputSchema.parse(parameters);
159
186
  const inventoryParameters = {
@@ -11,7 +11,8 @@ export const artifactCapabilities = (platform = process.platform) => Object.free
11
11
  effects: Object.freeze({
12
12
  mutatesArtifact: false,
13
13
  launchesProcess: operation !== "decode_interface_builder" &&
14
- operation !== "inspect_keyed_archive",
14
+ operation !== "inspect_keyed_archive" &&
15
+ operation !== "trace_dylib_resolution",
15
16
  mayShowUi: false,
16
17
  mayAccessNetwork: false,
17
18
  mayWriteFilesystem: operation === "extract_artifact" ||
@@ -39,6 +39,9 @@ export class ZipArtifactReader {
39
39
  this.#reader = new ZipReader(this.#source, {
40
40
  checkSignature: true,
41
41
  checkOverlappingEntry: true,
42
+ // REA applies its own provider-neutral path validation in scanReader and
43
+ // must preserve that typed failure instead of zip.js rejecting first.
44
+ filenameValidation: "tolerant",
42
45
  });
43
46
  }
44
47
  async *entries(signal) {
@@ -0,0 +1,259 @@
1
+ import { createHash } from "node:crypto";
2
+ import { constants } from "node:fs";
3
+ import { lstat, open, readlink, realpath } from "node:fs/promises";
4
+ import { join, relative, sep } from "node:path";
5
+ import { resolveTreePath } from "../../domain/apple/dyldPaths.js";
6
+ import { dylibResolutionInputSchema, dylibResolutionResultSchema, traceDylibLoading, } from "../../domain/apple/dylibResolution.js";
7
+ import { ArtifactReaderFailure } from "../ArtifactReader.js";
8
+ import { DirectoryArtifactReader } from "../DirectoryArtifactReader.js";
9
+ import { hasMachoMagic, readMachoImage } from "./MachoLoadCommandReader.js";
10
+ const HASH_CHUNK_BYTES = 1024 * 1024;
11
+ /** Lazily probed, symlink-preserving view of one analyzed directory. */
12
+ class FilesystemTreeView {
13
+ root;
14
+ signal;
15
+ #entries = new Map();
16
+ #images = new Map();
17
+ #identities = new Map();
18
+ constructor(root, signal) {
19
+ this.root = root;
20
+ this.signal = signal;
21
+ }
22
+ entry(path) {
23
+ const cached = this.#entries.get(path);
24
+ if (cached !== undefined)
25
+ return cached;
26
+ const pending = this.#readEntry(path);
27
+ this.#entries.set(path, pending);
28
+ return pending;
29
+ }
30
+ image(path) {
31
+ const cached = this.#images.get(path);
32
+ if (cached !== undefined)
33
+ return cached;
34
+ const pending = readImage(join(this.root, path), this.signal).then(({ facts, identity }) => {
35
+ this.#identities.set(path, identity);
36
+ return facts;
37
+ });
38
+ this.#images.set(path, pending);
39
+ return pending;
40
+ }
41
+ /** Identity of the file whose header bytes `image()` parsed. */
42
+ identity(path) {
43
+ return this.#identities.get(path);
44
+ }
45
+ async #readEntry(path) {
46
+ cancelled(this.signal);
47
+ const absolute = join(this.root, path);
48
+ try {
49
+ const metadata = await lstat(absolute);
50
+ if (metadata.isSymbolicLink())
51
+ return { kind: "symlink", target: await readlink(absolute) };
52
+ if (metadata.isDirectory())
53
+ return { kind: "directory" };
54
+ return metadata.isFile() ? { kind: "file" } : undefined;
55
+ }
56
+ catch (cause) {
57
+ if (missing(cause))
58
+ return undefined;
59
+ throw cause;
60
+ }
61
+ }
62
+ }
63
+ /** Resolve dyld load paths for the active Mach-O or every executable in its bundle. */
64
+ export const traceDylibResolution = async (options) => {
65
+ const parsed = dylibResolutionInputSchema.safeParse(options.parameters);
66
+ if (!parsed.success)
67
+ throw new ArtifactReaderFailure("path", "roots must be normalized paths relative to the analyzed root");
68
+ // Canonicalization is inside the translated region: a revoked permission
69
+ // keeps its reason and path.
70
+ let root = options.rootPath;
71
+ try {
72
+ root = await realpath(options.rootPath);
73
+ const target = relative(root, await realpath(options.targetPath))
74
+ .split(sep)
75
+ .join("/");
76
+ const view = new FilesystemTreeView(root, options.signal);
77
+ const { roots: requested, unclassified } = parsed.data.roots !== undefined
78
+ ? { roots: parsed.data.roots, unclassified: [] }
79
+ : options.enumerateRoots
80
+ ? await executableRoots(root, view, options.signal)
81
+ : { roots: [target], unclassified: [] };
82
+ const roots = await requireMachoRoots(view, requested);
83
+ const trace = await traceDylibLoading(view, {
84
+ roots,
85
+ unclassified,
86
+ ...(parsed.data.architecture === undefined
87
+ ? {}
88
+ : { architecture: parsed.data.architecture }),
89
+ ...(options.signal === undefined ? {} : { signal: options.signal }),
90
+ });
91
+ // Each digest must describe the same file whose headers were parsed.
92
+ const digests = new Map();
93
+ for (const { path } of trace.images)
94
+ digests.set(path, await fileSha256(join(root, path), {
95
+ path,
96
+ expected: view.identity(path),
97
+ ...(options.signal === undefined ? {} : { signal: options.signal }),
98
+ }));
99
+ const targetDigest = digests.get(target) ??
100
+ (await fileSha256(join(root, target), {
101
+ path: target,
102
+ expected: undefined,
103
+ ...(options.signal === undefined ? {} : { signal: options.signal }),
104
+ }));
105
+ if (targetDigest !== options.targetSha256)
106
+ throw new ArtifactReaderFailure("integrity", `Active target digest changed: expected ${options.targetSha256}, observed ${targetDigest}`);
107
+ return dylibResolutionResultSchema.parse({
108
+ ...trace,
109
+ root_path: options.rootPath,
110
+ target_sha256: options.targetSha256,
111
+ images: trace.images.map((image) => ({
112
+ ...image,
113
+ sha256: digests.get(image.path),
114
+ })),
115
+ });
116
+ }
117
+ catch (cause) {
118
+ if (options.signal?.aborted === true)
119
+ throw new ArtifactReaderFailure("cancelled", "Dylib resolution was cancelled", { cause });
120
+ const denied = permissionDenied(cause);
121
+ if (denied !== undefined)
122
+ throw new ArtifactReaderFailure("unavailable", `Permission denied (${denied.code}) reading ${deniedPath(root, denied.path)}`, { cause });
123
+ throw cause;
124
+ }
125
+ };
126
+ /**
127
+ * Every Mach-O in the bundle with an executable slice is its own process root.
128
+ * Mach-O files that do not parse are returned separately: whether they are
129
+ * executables is unknown, so they make coverage partial instead of vanishing.
130
+ */
131
+ const executableRoots = async (root, view, signal) => {
132
+ const roots = [];
133
+ const unclassified = [];
134
+ for await (const entry of new DirectoryArtifactReader(root).entries(signal)) {
135
+ if (entry.kind !== "file" ||
136
+ !(await startsWithMachoMagic(entry.adapterKey)))
137
+ continue;
138
+ const facts = await view.image(entry.path);
139
+ if (facts.status === "malformed" || facts.status === "unsupported")
140
+ unclassified.push(entry.path);
141
+ else if (facts.status === "parsed" &&
142
+ facts.slices.some(({ file_type: type }) => type === "execute"))
143
+ roots.push(entry.path);
144
+ }
145
+ return {
146
+ roots: roots.sort(compare),
147
+ unclassified: unclassified.sort(compare),
148
+ };
149
+ };
150
+ const compare = (left, right) => left < right ? -1 : left > right ? 1 : 0;
151
+ /**
152
+ * Resolve each root segment by segment, as dependency candidates are, so a
153
+ * symlink in an intermediate directory cannot lead outside the analyzed root.
154
+ */
155
+ const requireMachoRoots = async (view, roots) => {
156
+ const resolved = [];
157
+ for (const path of roots) {
158
+ const lookup = await resolveTreePath(view, path);
159
+ if (lookup.kind === "escapes")
160
+ throw new ArtifactReaderFailure("path", `Root ${path} resolves outside the analyzed root`);
161
+ if (lookup.kind !== "file")
162
+ throw new ArtifactReaderFailure("path", `Root ${path} is not a regular file in the analyzed root`);
163
+ if ((await view.image(lookup.path)).status === "not-mach-o")
164
+ throw new ArtifactReaderFailure("format", `Root ${path} is not a Mach-O image`);
165
+ if (!resolved.includes(lookup.path))
166
+ resolved.push(lookup.path);
167
+ }
168
+ return resolved;
169
+ };
170
+ const startsWithMachoMagic = async (path) => {
171
+ const handle = await open(path, constants.O_RDONLY | constants.O_NOFOLLOW);
172
+ try {
173
+ const buffer = new Uint8Array(4);
174
+ const { bytesRead } = await handle.read(buffer, 0, 4, 0);
175
+ return hasMachoMagic(buffer.subarray(0, bytesRead));
176
+ }
177
+ finally {
178
+ await handle.close();
179
+ }
180
+ };
181
+ const identityOf = (metadata) => [
182
+ metadata.dev,
183
+ metadata.ino,
184
+ metadata.size,
185
+ metadata.mtimeNs,
186
+ metadata.ctimeNs,
187
+ ].join(":");
188
+ const readImage = async (path, signal) => {
189
+ cancelled(signal);
190
+ const handle = await open(path, constants.O_RDONLY | constants.O_NOFOLLOW);
191
+ try {
192
+ const metadata = await handle.stat({ bigint: true });
193
+ const size = Number(metadata.size);
194
+ const facts = await readMachoImage(async (offset, length) => {
195
+ cancelled(signal);
196
+ const buffer = new Uint8Array(Math.max(0, Math.min(length, size - offset)));
197
+ const { bytesRead } = await handle.read(buffer, 0, buffer.length, offset);
198
+ return buffer.subarray(0, bytesRead);
199
+ }, size);
200
+ return { facts, identity: identityOf(metadata) };
201
+ }
202
+ finally {
203
+ await handle.close();
204
+ }
205
+ };
206
+ /** Hash a file, failing if it is not the file (or the version) that was parsed. */
207
+ const fileSha256 = async (absolute, options) => {
208
+ const { signal } = options;
209
+ const handle = await open(absolute, constants.O_RDONLY | constants.O_NOFOLLOW);
210
+ const unchanged = async () => {
211
+ if (options.expected !== undefined &&
212
+ identityOf(await handle.stat({ bigint: true })) !== options.expected)
213
+ throw new ArtifactReaderFailure("integrity", `${options.path} changed while its dyld load commands were traced`);
214
+ };
215
+ try {
216
+ await unchanged();
217
+ const hash = createHash("sha256");
218
+ const buffer = new Uint8Array(HASH_CHUNK_BYTES);
219
+ for (;;) {
220
+ cancelled(signal);
221
+ const { bytesRead } = await handle.read(buffer, 0, buffer.length, null);
222
+ if (bytesRead === 0)
223
+ break;
224
+ hash.update(buffer.subarray(0, bytesRead));
225
+ }
226
+ await unchanged();
227
+ return hash.digest("hex");
228
+ }
229
+ finally {
230
+ await handle.close();
231
+ }
232
+ };
233
+ const cancelled = (signal) => {
234
+ if (signal?.aborted === true)
235
+ throw new ArtifactReaderFailure("cancelled", "Dylib resolution was cancelled");
236
+ };
237
+ /** A denied path relative to the analyzed root, or absolute when outside it. */
238
+ const deniedPath = (root, path) => {
239
+ if (path === undefined)
240
+ return "a file in the analyzed root";
241
+ const inside = relative(root, path);
242
+ return inside === "" || inside.startsWith("..") ? path : inside;
243
+ };
244
+ /** Host permission denials, kept distinct from malformed or missing files. */
245
+ const permissionDenied = (cause) => {
246
+ if (!(cause instanceof Error) || !("code" in cause))
247
+ return undefined;
248
+ if (cause.code !== "EACCES" && cause.code !== "EPERM")
249
+ return undefined;
250
+ return {
251
+ code: cause.code,
252
+ path: "path" in cause && typeof cause.path === "string"
253
+ ? cause.path
254
+ : undefined,
255
+ };
256
+ };
257
+ const missing = (cause) => cause instanceof Error &&
258
+ "code" in cause &&
259
+ (cause.code === "ENOENT" || cause.code === "ENOTDIR");