rea-agents 1.6.0 → 2.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 (238) hide show
  1. package/README.md +157 -66
  2. package/bridge/ghidra/ReaGhidraBridge.java +2075 -0
  3. package/bridge/hopper_bridge.py +13 -3
  4. package/dist/application/AbortablePromise.js +25 -0
  5. package/dist/application/AnalysisProvider.js +3 -0
  6. package/dist/application/AnalysisProviderEvaluation.js +60 -0
  7. package/dist/application/AnalysisProviderRegistry.js +228 -0
  8. package/dist/application/AnalysisSnapshotCache.js +41 -21
  9. package/dist/application/AnalysisSnapshotFiles.js +5 -6
  10. package/dist/application/BinarySession.js +218 -101
  11. package/dist/application/CapabilityInventory.js +18 -0
  12. package/dist/application/CompositeProvider.js +20 -8
  13. package/dist/application/CrossVersionInvestigation.js +10 -6
  14. package/dist/application/DirectAnalysis.js +49 -35
  15. package/dist/application/Doctor.js +110 -8
  16. package/dist/application/ElectronBoundaryAnalysis.js +96 -0
  17. package/dist/application/ElectronBoundaryGraph.js +9 -0
  18. package/dist/application/ElectronBoundaryGraphContext.js +62 -0
  19. package/dist/application/ElectronBoundaryGraphIpc.js +242 -0
  20. package/dist/application/ElectronBoundaryGraphNative.js +86 -0
  21. package/dist/application/ElectronBoundaryGraphWindows.js +199 -0
  22. package/dist/application/EnhancedTools.js +39 -14
  23. package/dist/application/InvestigationProviders.js +41 -0
  24. package/dist/application/JavaScriptApplicationEvidence.js +26 -0
  25. package/dist/application/JavaScriptApplicationEvidenceGraph.js +89 -0
  26. package/dist/application/JavaScriptApplicationService.js +74 -0
  27. package/dist/application/JavaScriptApplicationWorkflowEvidence.js +29 -0
  28. package/dist/application/JavaScriptApplicationWorkflowService.js +90 -0
  29. package/dist/application/JavaScriptArtifactAnalysis.js +305 -0
  30. package/dist/application/JavaScriptArtifactAnalysisTypes.js +1 -0
  31. package/dist/application/JavaScriptArtifactFiles.js +231 -0
  32. package/dist/application/JavaScriptArtifactGraphAccumulator.js +54 -0
  33. package/dist/application/JavaScriptArtifactGraphBuilder.js +144 -0
  34. package/dist/application/JavaScriptArtifactGraphContext.js +205 -0
  35. package/dist/application/JavaScriptArtifactGraphDocuments.js +108 -0
  36. package/dist/application/JavaScriptArtifactGraphEvidence.js +99 -0
  37. package/dist/application/JavaScriptArtifactGraphFindings.js +271 -0
  38. package/dist/application/JavaScriptArtifactGraphStructure.js +409 -0
  39. package/dist/application/JavaScriptArtifactReconstruction.js +77 -0
  40. package/dist/application/JavaScriptArtifactReconstructionInput.js +18 -0
  41. package/dist/application/JavaScriptReplayPlanning.js +201 -0
  42. package/dist/application/JavaScriptReplayService.js +266 -0
  43. package/dist/application/JavaScriptRuntimeReconciliationEvidence.js +25 -0
  44. package/dist/application/JavaScriptRuntimeReconciliationService.js +27 -0
  45. package/dist/application/MacHopper.js +20 -4
  46. package/dist/application/ManagedApplicationGraphService.js +55 -0
  47. package/dist/application/ManagedMemberComparisonService.js +122 -0
  48. package/dist/application/ManagedNativeVerificationService.js +39 -0
  49. package/dist/application/ManagedReconstructionService.js +58 -0
  50. package/dist/application/ManagedRuntimeCorrelationService.js +103 -0
  51. package/dist/application/PermissionAuthority.js +1 -1
  52. package/dist/application/ProcessCaptureLifecycle.js +1 -1
  53. package/dist/application/ProcessHarness.js +1 -1
  54. package/dist/application/ProcessSampling.js +16 -12
  55. package/dist/application/ProjectPermissionStore.js +1 -0
  56. package/dist/application/SessionProviderRouter.js +219 -0
  57. package/dist/application/Setup.js +84 -60
  58. package/dist/application/SetupClients.js +1 -1
  59. package/dist/application/SetupPlan.js +9 -2
  60. package/dist/application/SupportedClients.js +40 -16
  61. package/dist/application/Upgrade.js +1 -1
  62. package/dist/application/runtime.js +11 -6
  63. package/dist/artifacts/AsarArtifactReader.js +23 -3
  64. package/dist/browser/CdpBrowserProvider.js +4 -2
  65. package/dist/browser/CdpCaptureEvents.js +49 -0
  66. package/dist/browser/CdpElectronInspection.js +106 -162
  67. package/dist/browser/CdpElectronProvider.js +9 -3
  68. package/dist/browser/CdpElectronScriptEvents.js +69 -0
  69. package/dist/browser/CdpElectronScripts.js +94 -0
  70. package/dist/browser/CdpElectronWorkers.js +56 -0
  71. package/dist/browser/CdpEndpoint.js +14 -1
  72. package/dist/browser/CdpPageCapture.js +8 -4
  73. package/dist/catalogIdentity.js +6 -42
  74. package/dist/cli.js +446 -34
  75. package/dist/cliApplicationCommands.js +62 -0
  76. package/dist/cliBrowserAdvancedCommands.js +5 -4
  77. package/dist/cliBrowserCommands.js +5 -4
  78. package/dist/cliCommandNames.js +56 -0
  79. package/dist/cliElectronCommands.js +106 -3
  80. package/dist/cliEvidenceCommands.js +4 -3
  81. package/dist/cliInvestigationCommands.js +2 -1
  82. package/dist/cliJsonInput.js +54 -0
  83. package/dist/cliPolicyCommands.js +2 -1
  84. package/dist/cliProcessCommands.js +3 -2
  85. package/dist/config.js +101 -2
  86. package/dist/contracts/applicationToolContracts.js +91 -0
  87. package/dist/contracts/artifactToolContracts.js +5 -9
  88. package/dist/contracts/browserToolContracts.js +14 -62
  89. package/dist/contracts/electronToolContracts.js +45 -19
  90. package/dist/contracts/enhancedInputs.js +2 -2
  91. package/dist/contracts/errorSchemas.js +1 -0
  92. package/dist/contracts/investigationExamples.js +0 -6
  93. package/dist/contracts/javascriptApplicationWorkflowExamples.js +46 -0
  94. package/dist/contracts/javascriptRuntimeReconciliationExample.js +208 -0
  95. package/dist/contracts/managedToolContracts.js +153 -0
  96. package/dist/contracts/managedWorkflowExamples.js +346 -0
  97. package/dist/contracts/managedWorkflowToolContracts.js +203 -0
  98. package/dist/contracts/nativeToolContracts.js +2 -6
  99. package/dist/contracts/promptContracts.js +18 -2
  100. package/dist/contracts/providerSelection.js +28 -0
  101. package/dist/contracts/sessionLifecycleInputs.js +2 -0
  102. package/dist/contracts/toolContractExamples.js +10 -25
  103. package/dist/contracts/toolContracts.js +69 -65
  104. package/dist/contracts/toolEffects.js +160 -0
  105. package/dist/contracts/toolOutputSchemas.js +152 -42
  106. package/dist/doctorRuntime.js +47 -0
  107. package/dist/domain/analysisProfile.js +78 -0
  108. package/dist/domain/analysisSnapshot.js +92 -46
  109. package/dist/domain/artifactComparison.js +3 -4
  110. package/dist/domain/artifactGraph.js +1 -0
  111. package/dist/domain/binaryTarget.js +4 -29
  112. package/dist/domain/browserObservation.js +3 -0
  113. package/dist/domain/bundleComparison.js +4 -4
  114. package/dist/domain/electronObservation.js +13 -0
  115. package/dist/domain/electronStaticAnalysis.js +9 -0
  116. package/dist/domain/electronStaticAnalysisBrowser.js +194 -0
  117. package/dist/domain/electronStaticAnalysisIpc.js +171 -0
  118. package/dist/domain/electronStaticAnalysisNative.js +164 -0
  119. package/dist/domain/electronStaticAnalysisTypes.js +1 -0
  120. package/dist/domain/electronStaticAnalysisValues.js +103 -0
  121. package/dist/domain/errors.js +148 -7
  122. package/dist/domain/evidence.js +44 -5
  123. package/dist/domain/evidenceBundle.js +9 -7
  124. package/dist/domain/functionComparison.js +9 -3
  125. package/dist/domain/functionComparisonNormalization.js +37 -0
  126. package/dist/domain/functionComparisonSchemas.js +3 -8
  127. package/dist/domain/functionDossierEvidence.js +4 -1
  128. package/dist/domain/hopperValues.js +66 -9
  129. package/dist/domain/inputIssueProjection.js +52 -0
  130. package/dist/domain/investigationWorkspace.js +2 -1
  131. package/dist/domain/javascriptApplicationAnalysis.js +155 -0
  132. package/dist/domain/javascriptApplicationChangeGraph.js +231 -0
  133. package/dist/domain/javascriptApplicationEvidenceSchemas.js +379 -0
  134. package/dist/domain/javascriptApplicationGraph.js +282 -0
  135. package/dist/domain/javascriptApplicationGraphSchemas.js +139 -0
  136. package/dist/domain/javascriptApplicationVersionComparison.js +116 -0
  137. package/dist/domain/javascriptApplicationVersionComparisonSchemas.js +139 -0
  138. package/dist/domain/javascriptApplicationVersionItems.js +221 -0
  139. package/dist/domain/javascriptApplicationVersionKeys.js +170 -0
  140. package/dist/domain/javascriptAstFingerprint.js +143 -0
  141. package/dist/domain/javascriptFeatureSeed.js +140 -0
  142. package/dist/domain/javascriptFeatureTrace.js +232 -0
  143. package/dist/domain/javascriptFeatureTraceSchemas.js +136 -0
  144. package/dist/domain/javascriptFeatureTraversal.js +88 -0
  145. package/dist/domain/javascriptNativeHandoff.js +85 -0
  146. package/dist/domain/javascriptReplay.js +361 -0
  147. package/dist/domain/javascriptRuntimeLoadState.js +106 -0
  148. package/dist/domain/javascriptRuntimeReconciliation.js +42 -0
  149. package/dist/domain/javascriptRuntimeReconciliationGraph.js +169 -0
  150. package/dist/domain/javascriptRuntimeReconciliationMatching.js +247 -0
  151. package/dist/domain/javascriptRuntimeReconciliationParsing.js +160 -0
  152. package/dist/domain/javascriptRuntimeReconciliationResult.js +119 -0
  153. package/dist/domain/javascriptRuntimeReconciliationRuntime.js +316 -0
  154. package/dist/domain/javascriptRuntimeReconciliationSchemas.js +245 -0
  155. package/dist/domain/javascriptRuntimeStaticCandidates.js +191 -0
  156. package/dist/domain/javascriptStaticAnalysis.js +346 -0
  157. package/dist/domain/javascriptStaticAnalysisFindings.js +39 -0
  158. package/dist/domain/javascriptStaticAnalysisHelpers.js +274 -0
  159. package/dist/domain/javascriptStaticAnalysisState.js +23 -0
  160. package/dist/domain/javascriptStaticAnalysisTypes.js +1 -0
  161. package/dist/domain/managedApplicationGraph.js +556 -0
  162. package/dist/domain/managedArtifact.js +444 -0
  163. package/dist/domain/managedMemberComparison.js +565 -0
  164. package/dist/domain/managedNativeVerification.js +408 -0
  165. package/dist/domain/managedReconstruction.js +183 -0
  166. package/dist/domain/managedRuntimeCorrelation.js +175 -0
  167. package/dist/domain/reconstructionVerificationSchemas.js +1 -3
  168. package/dist/dotnet/ManagedArtifactInspector.js +318 -0
  169. package/dist/dotnet/ManagedMemberInspector.js +887 -0
  170. package/dist/dotnet/ManagedMetadataHeaps.js +131 -0
  171. package/dist/dotnet/ManagedMetadataInventory.js +368 -0
  172. package/dist/dotnet/ManagedMetadataLayout.js +322 -0
  173. package/dist/dotnet/ManagedNativeBoundaryInspector.js +391 -0
  174. package/dist/dotnet/ManagedPeReader.js +157 -0
  175. package/dist/dotnet/ManagedReaderFailure.js +10 -0
  176. package/dist/dotnet/ManagedStaticProvider.js +203 -0
  177. package/dist/generatedPackageMetadata.js +2 -2
  178. package/dist/ghidra/GhidraAnalysisProfile.js +37 -0
  179. package/dist/ghidra/GhidraClient.js +400 -0
  180. package/dist/ghidra/GhidraClientTypes.js +1 -0
  181. package/dist/ghidra/GhidraDefaults.js +20 -0
  182. package/dist/ghidra/GhidraDiagnostics.js +43 -0
  183. package/dist/ghidra/GhidraDoctor.js +59 -0
  184. package/dist/ghidra/GhidraFunctionValues.js +264 -0
  185. package/dist/ghidra/GhidraInstallation.js +239 -0
  186. package/dist/ghidra/GhidraInventoryValues.js +217 -0
  187. package/dist/ghidra/GhidraLauncher.js +139 -0
  188. package/dist/ghidra/GhidraProvider.js +366 -0
  189. package/dist/ghidra/GhidraRequestQueue.js +98 -0
  190. package/dist/ghidra/GhidraResponseBuffer.js +31 -0
  191. package/dist/ghidra/GhidraResponseRouter.js +24 -0
  192. package/dist/ghidra/GhidraSessionError.js +39 -0
  193. package/dist/ghidra/GhidraSessionValues.js +62 -0
  194. package/dist/ghidra/GhidraSocketConnection.js +46 -0
  195. package/dist/ghidra/protocol.js +69 -0
  196. package/dist/hopper/BridgeLauncher.js +19 -54
  197. package/dist/hopper/HopperAnalysisProfile.js +106 -0
  198. package/dist/hopper/HopperClient.js +154 -162
  199. package/dist/hopper/HopperProvider.js +99 -7
  200. package/dist/hopper/HopperSessionValues.js +27 -0
  201. package/dist/hopper/HopperSocketConnection.js +31 -0
  202. package/dist/main.js +14 -0
  203. package/dist/native/NativeMacOSProvider.js +3 -1
  204. package/dist/process/PendingOperations.js +59 -0
  205. package/dist/process/PrivateRuntimeRoot.js +33 -0
  206. package/dist/process/ProviderDeadline.js +95 -0
  207. package/dist/process/ProviderProcess.js +275 -0
  208. package/dist/replay/JavaScriptReplayWorker.js +286 -0
  209. package/dist/replay/LinuxJavaScriptReplayRunner.js +434 -0
  210. package/dist/replay/LinuxRuntimeClosure.js +46 -0
  211. package/dist/replay/LinuxSeccompPolicy.js +40 -0
  212. package/dist/replay/ReplayWorkerProtocol.js +55 -0
  213. package/dist/replay/SystemJavaScriptReplayHost.js +182 -0
  214. package/dist/server/createServer.js +53 -0
  215. package/dist/server/promptCompletion.js +9 -3
  216. package/dist/server/registerApplicationTools.js +115 -0
  217. package/dist/server/registerArtifactComparisonTool.js +16 -9
  218. package/dist/server/registerBrowserTools.js +33 -8
  219. package/dist/server/registerBundleComparisonTool.js +17 -20
  220. package/dist/server/registerElectronTools.js +45 -3
  221. package/dist/server/registerEnhancedTools.js +67 -19
  222. package/dist/server/registerEvidenceTools.js +5 -1
  223. package/dist/server/registerFunctionComparisonTool.js +14 -7
  224. package/dist/server/registerInvestigationTools.js +21 -17
  225. package/dist/server/registerManagedTools.js +6 -0
  226. package/dist/server/registerManagedWorkflowTools.js +277 -0
  227. package/dist/server/registerOfficialTools.js +11 -8
  228. package/dist/server/registerProcessComparisonTool.js +25 -47
  229. package/dist/server/registerSessionStatusTool.js +7 -7
  230. package/dist/server/registerSessionTools.js +57 -87
  231. package/dist/server/sessionEvidence.js +14 -19
  232. package/dist/server/toolInputValidation.js +77 -0
  233. package/dist/server/toolRegistrationOptions.js +79 -2
  234. package/dist/server/toolResult.js +25 -17
  235. package/package.json +17 -7
  236. package/scripts/rea.mjs +36 -2
  237. package/skills/rea-analysis/SKILL.md +59 -3
  238. /package/dist/{application → process}/ProcessOwnership.js +0 -0
@@ -0,0 +1,217 @@
1
+ import { z } from "zod";
2
+ import { AnalysisInputError, AnalysisOutputError } from "../domain/errors.js";
3
+ import { jsonObjectSchema, jsonValueSchema, } from "../domain/jsonValue.js";
4
+ import { err, ok } from "../domain/result.js";
5
+ /** Read-only direct operations admitted by the Ghidra v1 adapter. */
6
+ export const GHIDRA_INVENTORY_OPERATIONS = [
7
+ "address_name",
8
+ "list_documents",
9
+ "list_names",
10
+ "list_procedures",
11
+ "list_segments",
12
+ "list_strings",
13
+ "procedure_address",
14
+ "resolve_containing_procedure",
15
+ "search_procedures",
16
+ "search_strings",
17
+ ];
18
+ const operationSet = new Set(GHIDRA_INVENTORY_OPERATIONS);
19
+ /** Narrow an application operation to the Ghidra inventory surface. */
20
+ export const isGhidraInventoryOperation = (operation) => operationSet.has(operation);
21
+ export const ghidraBoundedIdentifierSchema = z.string().min(1).max(4096);
22
+ const boundedIdentifier = ghidraBoundedIdentifierSchema;
23
+ const document = boundedIdentifier.nullable().default(null);
24
+ const explicitAddress = boundedIdentifier;
25
+ const filteredAddress = explicitAddress.nullable().default(null);
26
+ const pagination = {
27
+ offset: z.number().int().min(0).default(0),
28
+ limit: z.number().int().min(1).max(500).default(100),
29
+ };
30
+ const searchInput = {
31
+ pattern: z.string().min(1).max(256),
32
+ mode: z.enum(["literal", "regex"]).default("literal"),
33
+ case_sensitive: z.boolean().default(false),
34
+ offset: z.number().int().min(0).default(0),
35
+ limit: z.number().int().min(1).max(100).default(100),
36
+ document,
37
+ };
38
+ const inputSchemas = {
39
+ address_name: z.object({ document, address: explicitAddress }).strict(),
40
+ list_documents: z.object({}).strict(),
41
+ list_names: z
42
+ .object({ document, address: filteredAddress, ...pagination })
43
+ .strict(),
44
+ list_procedures: z.object({ document, ...pagination }).strict(),
45
+ list_segments: z.object({ document }).strict(),
46
+ list_strings: z
47
+ .object({ document, address: filteredAddress, ...pagination })
48
+ .strict(),
49
+ procedure_address: z
50
+ .object({ document, procedure: boundedIdentifier })
51
+ .strict(),
52
+ resolve_containing_procedure: z
53
+ .object({ document, address: explicitAddress })
54
+ .strict(),
55
+ search_procedures: z.object(searchInput).strict(),
56
+ search_strings: z.object(searchInput).strict(),
57
+ };
58
+ /** Validate and default one provider request before it crosses the socket. */
59
+ export const parseGhidraInventoryInput = (operation, value) => {
60
+ const parsed = inputSchemas[operation].safeParse(value);
61
+ if (!parsed.success)
62
+ return err(new AnalysisInputError(operation, { cause: parsed.error }));
63
+ return ok(jsonObjectSchema.parse(parsed.data));
64
+ };
65
+ export const ghidraCanonicalAddressSchema = z
66
+ .string()
67
+ .regex(/^(?:0x[0-9a-f]+|(?:[A-Za-z0-9._~-]|%[0-9A-F]{2})+:0x[0-9a-f]+)$/u);
68
+ const canonicalAddress = ghidraCanonicalAddressSchema;
69
+ const valueTruncation = { value_truncated: z.boolean() };
70
+ const symbolFacts = z
71
+ .object({
72
+ primary: z.boolean(),
73
+ dynamic: z.boolean(),
74
+ external: z.boolean(),
75
+ type: z.string().min(1),
76
+ source: z.enum(["default", "analysis", "ai", "imported", "user_defined"]),
77
+ })
78
+ .strict();
79
+ const procedureFacts = z
80
+ .object({
81
+ external: z.boolean(),
82
+ thunk: z.boolean(),
83
+ thunk_target: canonicalAddress.nullable(),
84
+ })
85
+ .strict();
86
+ const functionClassification = procedureFacts
87
+ .extend({ provenance: z.literal("ghidra-function-manager") })
88
+ .strict();
89
+ const stringFacts = z
90
+ .object({
91
+ encoding: z.string().min(1),
92
+ termination: z.enum(["missing", "present_or_not_required"]),
93
+ byte_length: z.number().int().min(0),
94
+ })
95
+ .strict();
96
+ const baseItem = {
97
+ address: canonicalAddress,
98
+ value: z.string(),
99
+ ...valueTruncation,
100
+ };
101
+ const symbolItem = z.object({ ...baseItem, symbol: symbolFacts }).strict();
102
+ const procedureItem = z
103
+ .object({ ...baseItem, procedure: procedureFacts })
104
+ .strict();
105
+ const stringItem = z.object({ ...baseItem, string: stringFacts }).strict();
106
+ const searchItem = z.object(baseItem).strict();
107
+ const pageSchema = (item, maximumLimit) => z
108
+ .object({
109
+ items: z.array(item),
110
+ offset: z.number().int().min(0),
111
+ limit: z.number().int().min(1).max(maximumLimit),
112
+ total: z.number().int().min(0),
113
+ next_offset: z.number().int().min(0).nullable(),
114
+ has_more: z.boolean(),
115
+ })
116
+ .strict()
117
+ .superRefine((page, context) => {
118
+ const next = page.offset + page.items.length;
119
+ const hasMore = next < page.total;
120
+ if (page.items.length > page.limit)
121
+ context.addIssue({
122
+ code: "custom",
123
+ path: ["items"],
124
+ message: "page exceeds its declared limit",
125
+ });
126
+ if (page.items.length > 0 && next > page.total)
127
+ context.addIssue({
128
+ code: "custom",
129
+ path: ["total"],
130
+ message: "page items exceed its exact total",
131
+ });
132
+ if (hasMore && page.items.length === 0)
133
+ context.addIssue({
134
+ code: "custom",
135
+ path: ["items"],
136
+ message: "page continuation must advance its offset",
137
+ });
138
+ if (page.has_more !== hasMore)
139
+ context.addIssue({
140
+ code: "custom",
141
+ path: ["has_more"],
142
+ message: "page continuation does not match its exact total",
143
+ });
144
+ if (page.next_offset !== (hasMore ? next : null))
145
+ context.addIssue({
146
+ code: "custom",
147
+ path: ["next_offset"],
148
+ message: "page continuation offset is inconsistent",
149
+ });
150
+ });
151
+ const availablePermissions = z
152
+ .object({
153
+ available: z.literal(true),
154
+ source: z.literal("ghidra-memory-block"),
155
+ })
156
+ .strict();
157
+ const memoryRegion = z
158
+ .object({
159
+ name: z.string(),
160
+ start: canonicalAddress,
161
+ end: canonicalAddress,
162
+ readable: z.boolean(),
163
+ writable: z.boolean(),
164
+ executable: z.boolean(),
165
+ permissions: availablePermissions,
166
+ provenance: z.literal("ghidra-memory-block"),
167
+ address_space: z.string().min(1),
168
+ image_base: canonicalAddress,
169
+ initialized: z.boolean(),
170
+ overlay: z.boolean(),
171
+ })
172
+ .strict();
173
+ const segment = memoryRegion
174
+ .extend({ sections: z.array(memoryRegion) })
175
+ .strict();
176
+ const containingProcedure = z.discriminatedUnion("found", [
177
+ z
178
+ .object({
179
+ query_address: canonicalAddress,
180
+ found: z.literal(true),
181
+ procedure: z
182
+ .object({
183
+ address: canonicalAddress,
184
+ name: z.string().min(1),
185
+ classification: functionClassification,
186
+ })
187
+ .strict(),
188
+ })
189
+ .strict(),
190
+ z
191
+ .object({
192
+ query_address: canonicalAddress,
193
+ found: z.literal(false),
194
+ procedure: z.null(),
195
+ reason: z.enum(["outside_segments", "not_in_procedure"]),
196
+ })
197
+ .strict(),
198
+ ]);
199
+ const resultSchemas = {
200
+ address_name: z.string().nullable(),
201
+ list_documents: z.array(z.string().min(1)).length(1),
202
+ list_names: pageSchema(symbolItem, 500),
203
+ list_procedures: pageSchema(procedureItem, 500),
204
+ list_segments: z.array(segment),
205
+ list_strings: pageSchema(stringItem, 500),
206
+ procedure_address: canonicalAddress,
207
+ resolve_containing_procedure: containingProcedure,
208
+ search_procedures: pageSchema(searchItem, 100),
209
+ search_strings: pageSchema(searchItem, 100),
210
+ };
211
+ /** Require exact, bounded Java-bridge output before creating Evidence. */
212
+ export const parseGhidraInventoryResult = (operation, value) => {
213
+ const parsed = resultSchemas[operation].safeParse(value);
214
+ return parsed.success
215
+ ? ok(jsonValueSchema.parse(parsed.data))
216
+ : err(new AnalysisOutputError(operation, "Ghidra bridge output did not match the inventory contract", { cause: parsed.error }));
217
+ };
@@ -0,0 +1,139 @@
1
+ import { mkdir } from "node:fs/promises";
2
+ import { basename, dirname, join } from "node:path";
3
+ import writeFileAtomic from "write-file-atomic";
4
+ import { AnalysisCancelledError } from "../domain/errors.js";
5
+ import { err, ok } from "../domain/result.js";
6
+ import { cleanupOwnedProcessGroup } from "../process/ProcessOwnership.js";
7
+ import { spawnOwnedProviderProcess, } from "../process/ProviderProcess.js";
8
+ import { GHIDRA_ANALYSIS_TIMEOUT_SECONDS, GHIDRA_MAX_CPU, GHIDRA_MAX_HEAP, } from "./GhidraDefaults.js";
9
+ import { ghidraJavaEnvironment } from "./GhidraInstallation.js";
10
+ /** Local launch failure retained as the cause of a provider-neutral error. */
11
+ export class GhidraLaunchError extends Error {
12
+ constructor(message, options) {
13
+ super(message, options);
14
+ this.name = "GhidraLaunchError";
15
+ }
16
+ }
17
+ /** Launch Ghidra without copying scripts into or modifying its installation. */
18
+ export class GhidraHeadlessLauncher {
19
+ options;
20
+ constructor(options) {
21
+ this.options = options;
22
+ }
23
+ async launch(session, options = {}) {
24
+ if (isAborted(options.signal))
25
+ return err(new AnalysisCancelledError("open_binary"));
26
+ const paths = ghidraRuntimePaths(session.runtimeRoot);
27
+ let started;
28
+ try {
29
+ await Promise.all([
30
+ paths.projectRoot,
31
+ paths.homeRoot,
32
+ paths.tempRoot,
33
+ paths.cacheRoot,
34
+ paths.configRoot,
35
+ paths.dataRoot,
36
+ ].map((path) => mkdir(path, { recursive: true, mode: 0o700 })));
37
+ await writeFileAtomic(paths.descriptorPath, `${JSON.stringify({
38
+ schema_version: 1,
39
+ socket_path: session.socketPath,
40
+ token: session.token,
41
+ run_id: session.runId,
42
+ provider_version: session.providerVersion,
43
+ profile_digest: session.profileDigest,
44
+ })}\n`, { encoding: "utf8", mode: 0o600 });
45
+ if (isAborted(options.signal))
46
+ return err(new AnalysisCancelledError("open_binary"));
47
+ started = await spawnOwnedProviderProcess({
48
+ command: this.options.analyzeHeadlessPath,
49
+ arguments: ghidraHeadlessArguments({
50
+ projectRoot: paths.projectRoot,
51
+ targetPath: session.targetPath,
52
+ bridgeScriptPath: this.options.bridgeScriptPath,
53
+ descriptorPath: paths.descriptorPath,
54
+ ghidraLogPath: paths.ghidraLogPath,
55
+ scriptLogPath: paths.scriptLogPath,
56
+ }),
57
+ runId: session.runId,
58
+ // analyzeHeadless is an interpreter-driven script. Parent identity and
59
+ // the per-process run token remain the cleanup authority.
60
+ expectedCommand: null,
61
+ env: ghidraLaunchEnvironment(paths, this.options.javaHome),
62
+ });
63
+ await writeFileAtomic(paths.ownershipPath, `${JSON.stringify({
64
+ schema_version: 1,
65
+ run_id: session.runId,
66
+ pid: started.ownership.leaderPid,
67
+ process_group_id: started.ownership.processGroupId,
68
+ parent_pid: process.pid,
69
+ launcher: this.options.analyzeHeadlessPath,
70
+ created_at: new Date().toISOString(),
71
+ })}\n`, { encoding: "utf8", mode: 0o600 });
72
+ if (isAborted(options.signal)) {
73
+ await cleanupOwnedProcessGroup(started.ownership);
74
+ return err(new AnalysisCancelledError("open_binary"));
75
+ }
76
+ const ownership = started.ownership;
77
+ return ok({
78
+ process: started.process,
79
+ ownsProcessLifetime: true,
80
+ cleanup: () => cleanupOwnedProcessGroup(ownership),
81
+ projectRoot: paths.projectRoot,
82
+ ghidraLogPath: paths.ghidraLogPath,
83
+ scriptLogPath: paths.scriptLogPath,
84
+ });
85
+ }
86
+ catch (cause) {
87
+ if (started !== undefined)
88
+ await cleanupOwnedProcessGroup(started.ownership).catch(() => undefined);
89
+ return isAborted(options.signal)
90
+ ? err(new AnalysisCancelledError("open_binary"))
91
+ : err(new GhidraLaunchError("Ghidra headless launch failed", { cause }));
92
+ }
93
+ }
94
+ }
95
+ /** Build the complete read-only headless invocation in deterministic order. */
96
+ export const ghidraHeadlessArguments = (options) => [
97
+ options.projectRoot,
98
+ "rea-project",
99
+ "-import",
100
+ options.targetPath,
101
+ "-readOnly",
102
+ "-deleteProject",
103
+ "-analysisTimeoutPerFile",
104
+ String(GHIDRA_ANALYSIS_TIMEOUT_SECONDS),
105
+ "-max-cpu",
106
+ String(GHIDRA_MAX_CPU),
107
+ "-log",
108
+ options.ghidraLogPath,
109
+ "-scriptlog",
110
+ options.scriptLogPath,
111
+ "-scriptPath",
112
+ dirname(options.bridgeScriptPath),
113
+ "-postScript",
114
+ basename(options.bridgeScriptPath),
115
+ options.descriptorPath,
116
+ ];
117
+ const ghidraRuntimePaths = (runtimeRoot) => ({
118
+ projectRoot: join(runtimeRoot, "project"),
119
+ homeRoot: join(runtimeRoot, "home"),
120
+ tempRoot: join(runtimeRoot, "tmp"),
121
+ cacheRoot: join(runtimeRoot, "cache"),
122
+ configRoot: join(runtimeRoot, "config"),
123
+ dataRoot: join(runtimeRoot, "data"),
124
+ descriptorPath: join(runtimeRoot, "session.json"),
125
+ ownershipPath: join(runtimeRoot, "ownership.json"),
126
+ ghidraLogPath: join(runtimeRoot, "ghidra.log"),
127
+ scriptLogPath: join(runtimeRoot, "script.log"),
128
+ });
129
+ const ghidraLaunchEnvironment = (paths, javaHome) => ({
130
+ ...ghidraJavaEnvironment(javaHome),
131
+ HOME: paths.homeRoot,
132
+ TMPDIR: paths.tempRoot,
133
+ XDG_CACHE_HOME: paths.cacheRoot,
134
+ XDG_CONFIG_HOME: paths.configRoot,
135
+ XDG_DATA_HOME: paths.dataRoot,
136
+ GHIDRA_HEADLESS_MAXMEM: GHIDRA_MAX_HEAP,
137
+ GHIDRA_HEADLESS_JAVA_OPTIONS: `-Duser.home=${paths.homeRoot} -Djava.io.tmpdir=${paths.tempRoot}`,
138
+ });
139
+ const isAborted = (signal) => signal?.aborted === true;