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,408 @@
1
+ import { createHash } from "node:crypto";
2
+ import { basename } from "node:path";
3
+ import canonicalize from "canonicalize";
4
+ import { z } from "zod";
5
+ import { evidenceSchema, parseEvidence } from "./evidence.js";
6
+ import { functionDossierSchema } from "./hopperValues.js";
7
+ import { managedNativeBoundaryInspectionSchema, } from "./managedArtifact.js";
8
+ import { inspectMachoSchema } from "./nativeInspection.js";
9
+ const digestSchema = z.string().regex(/^[a-f0-9]{64}$/u);
10
+ const evidenceIdSchema = z.string().regex(/^ev_[a-f0-9]{64}$/u);
11
+ const tokenSchema = z.string().regex(/^0x[0-9a-f]{8}$/u);
12
+ const boundedTextSchema = z.string().min(1).max(4_096);
13
+ const nativeVerificationLimitsSchema = z.strictObject({
14
+ max_native_observations: z.number().int().min(1).max(50).default(20),
15
+ max_candidates_per_import: z.number().int().min(1).max(100).default(25),
16
+ });
17
+ /** Authenticated managed boundary Evidence plus native observations. */
18
+ export const managedNativeVerificationInputSchema = z
19
+ .strictObject({
20
+ managed_boundaries: evidenceSchema,
21
+ native_observations: z.array(evidenceSchema).min(1).max(50),
22
+ limits: nativeVerificationLimitsSchema.default({
23
+ max_native_observations: 20,
24
+ max_candidates_per_import: 25,
25
+ }),
26
+ unknown_registry_approved: z.literal(true).optional(),
27
+ })
28
+ .superRefine((input, context) => {
29
+ const ids = new Set();
30
+ for (const [index, evidence] of input.native_observations.entries()) {
31
+ if (evidence.evidence_id === input.managed_boundaries.evidence_id) {
32
+ context.addIssue({
33
+ code: "custom",
34
+ path: ["native_observations", index],
35
+ message: "Native observation Evidence must be distinct from managed boundary Evidence",
36
+ });
37
+ }
38
+ if (ids.has(evidence.evidence_id)) {
39
+ context.addIssue({
40
+ code: "custom",
41
+ path: ["native_observations", index],
42
+ message: "Native observation Evidence IDs must be unique",
43
+ });
44
+ }
45
+ ids.add(evidence.evidence_id);
46
+ }
47
+ });
48
+ const nativeSymbolSchema = z.strictObject({
49
+ evidence_id: evidenceIdSchema,
50
+ operation: z.string().min(1),
51
+ name: z.string().min(1),
52
+ address: z.string().nullable(),
53
+ module_name: z.string().nullable(),
54
+ module_path: z.string().nullable(),
55
+ source: z.enum(["macho-export", "function-dossier"]),
56
+ });
57
+ const matchStatusSchema = z.enum([
58
+ "verified",
59
+ "inferred",
60
+ "unresolved",
61
+ "contradicted",
62
+ ]);
63
+ const matchBasisSchema = z.enum([
64
+ "exact-export-name",
65
+ "exact-function-name",
66
+ "decorated-name-candidate",
67
+ "module-mismatch",
68
+ "no-native-candidate",
69
+ "unsupported-native-evidence",
70
+ ]);
71
+ const pinvokeVerificationSchema = z.strictObject({
72
+ item_id: z.string().regex(/^mnv_pinvoke_[a-f0-9]{64}$/u),
73
+ managed: z.strictObject({
74
+ token: tokenSchema,
75
+ member_token: tokenSchema.nullable(),
76
+ member_name: z.string().nullable(),
77
+ import_name: z.string().min(1),
78
+ import_scope_name: z.string().nullable(),
79
+ no_mangle: z.boolean(),
80
+ char_set: z.enum(["not-specified", "ansi", "unicode", "auto", "unknown"]),
81
+ call_convention: z.enum([
82
+ "not-specified",
83
+ "winapi",
84
+ "cdecl",
85
+ "stdcall",
86
+ "thiscall",
87
+ "fastcall",
88
+ "unknown",
89
+ ]),
90
+ declaration_verification: z.literal("managed-declaration-only"),
91
+ }),
92
+ status: matchStatusSchema,
93
+ basis: matchBasisSchema,
94
+ confidence: z.enum(["observed", "inferred", "unknown"]),
95
+ matched_native: nativeSymbolSchema.nullable(),
96
+ candidates: z.array(nativeSymbolSchema).max(100),
97
+ evidence_links: z.array(evidenceIdSchema).min(1).max(51),
98
+ limitations: z.array(boundedTextSchema).max(100),
99
+ });
100
+ /** Provider-neutral managed/native verification result. */
101
+ export const managedNativeVerificationResultSchema = z.strictObject({
102
+ schema_version: z.literal(1),
103
+ verification_id: z.string().regex(/^mnv_[a-f0-9]{64}$/u),
104
+ algorithm: z.strictObject({
105
+ name: z.literal("rea-managed-native-verification"),
106
+ version: z.literal(1),
107
+ token_identity: z.literal("build-local"),
108
+ token_to_address_mapping: z.literal("not-inferred"),
109
+ }),
110
+ managed_boundary: z.strictObject({
111
+ evidence_id: evidenceIdSchema,
112
+ artifact_sha256: digestSchema,
113
+ artifact_path: z.string().min(1),
114
+ mvid: z.string().uuid().nullable(),
115
+ metadata_status: z.enum(["absent", "complete", "partial", "malformed"]),
116
+ pinvoke_imports_total: z.number().int().min(0),
117
+ native_implementations_total: z.number().int().min(0),
118
+ coverage_state: z.enum(["complete", "partial", "unavailable"]),
119
+ }),
120
+ native_observations: z.strictObject({
121
+ total: z.number().int().min(1),
122
+ accepted: z.number().int().min(0),
123
+ unsupported: z.number().int().min(0),
124
+ symbols: z.number().int().min(0),
125
+ truncated: z.boolean(),
126
+ }),
127
+ summary: z.strictObject({
128
+ verified: z.number().int().min(0),
129
+ inferred: z.number().int().min(0),
130
+ unresolved: z.number().int().min(0),
131
+ contradicted: z.number().int().min(0),
132
+ native_body_unresolved: z.number().int().min(0),
133
+ }),
134
+ pinvoke_imports: z.array(pinvokeVerificationSchema).max(50_000),
135
+ native_implementations: z.strictObject({
136
+ unresolved: z.number().int().min(0),
137
+ reason: boundedTextSchema,
138
+ }),
139
+ coverage: z.strictObject({
140
+ status: z.enum(["complete-within-inputs", "partial", "truncated"]),
141
+ omitted_native_observations: z.number().int().min(0),
142
+ omitted_candidates: z.number().int().min(0),
143
+ }),
144
+ evidence_links: z.array(evidenceIdSchema).min(2).max(51),
145
+ limitations: z.array(boundedTextSchema).max(1_000),
146
+ });
147
+ const sha256 = (value) => {
148
+ const serialized = canonicalize(value);
149
+ if (serialized === undefined)
150
+ throw new TypeError("Managed/native verification canonicalization failed");
151
+ return createHash("sha256").update(serialized).digest("hex");
152
+ };
153
+ /** Verify managed P/Invoke declarations against authenticated native Evidence. */
154
+ export const verifyManagedNativeBoundaries = (input) => {
155
+ const managedEvidence = parseEvidence(input.managed_boundaries);
156
+ if (managedEvidence.operation !== "inspect_managed_native_boundaries")
157
+ throw new TypeError("Evidence operation is not inspect_managed_native_boundaries");
158
+ const managed = managedNativeBoundaryInspectionSchema.parse(managedEvidence.normalized_result);
159
+ const native = collectNativeSymbols(input.native_observations.slice(0, input.limits.max_native_observations));
160
+ const verifiedPinvokes = managed.pinvoke_imports.items.map((item) => verifyPinvoke(item, managedEvidence.evidence_id, native.symbols, native.accepted > 0, input));
161
+ const pinvokeImports = verifiedPinvokes.map(({ verification }) => verification);
162
+ const counts = countStatuses(pinvokeImports);
163
+ const omittedNativeObservations = Math.max(0, input.native_observations.length - input.limits.max_native_observations);
164
+ const omittedCandidates = verifiedPinvokes.reduce((sum, item) => sum + item.omittedCandidates, 0);
165
+ const coverageStatus = omittedNativeObservations > 0 || omittedCandidates > 0
166
+ ? "truncated"
167
+ : managed.coverage.state === "complete" && native.unsupported === 0
168
+ ? "complete-within-inputs"
169
+ : "partial";
170
+ const withoutId = {
171
+ schema_version: 1,
172
+ algorithm: {
173
+ name: "rea-managed-native-verification",
174
+ version: 1,
175
+ token_identity: "build-local",
176
+ token_to_address_mapping: "not-inferred",
177
+ },
178
+ managed_boundary: {
179
+ evidence_id: managedEvidence.evidence_id,
180
+ artifact_sha256: managed.artifact.sha256,
181
+ artifact_path: managed.artifact.path,
182
+ mvid: managed.module?.mvid ?? null,
183
+ metadata_status: managed.metadata.status,
184
+ pinvoke_imports_total: managed.pinvoke_imports.total,
185
+ native_implementations_total: managed.native_implementations.total,
186
+ coverage_state: managed.coverage.state,
187
+ },
188
+ native_observations: {
189
+ total: input.native_observations.length,
190
+ accepted: native.accepted,
191
+ unsupported: native.unsupported,
192
+ symbols: native.symbols.length,
193
+ truncated: omittedNativeObservations > 0,
194
+ },
195
+ summary: {
196
+ ...counts,
197
+ native_body_unresolved: managed.native_implementations.items.filter(({ boundary_kind: kind }) => kind !== "pinvoke").length,
198
+ },
199
+ pinvoke_imports: pinvokeImports,
200
+ native_implementations: {
201
+ unresolved: managed.native_implementations.items.filter(({ boundary_kind: kind }) => kind !== "pinvoke").length,
202
+ reason: "Managed metadata tokens and RVAs are not translated to native provider addresses by this workflow; C++/CLI, ReadyToRun, and native-body mappings require explicit provider-supported bridge evidence.",
203
+ },
204
+ coverage: {
205
+ status: coverageStatus,
206
+ omitted_native_observations: omittedNativeObservations,
207
+ omitted_candidates: omittedCandidates,
208
+ },
209
+ evidence_links: [
210
+ managedEvidence.evidence_id,
211
+ ...input.native_observations
212
+ .slice(0, input.limits.max_native_observations)
213
+ .map(({ evidence_id: id }) => id),
214
+ ],
215
+ limitations: [
216
+ "P/Invoke verification checks declared import names against supplied native export or function-name Evidence only.",
217
+ "A matching native symbol verifies that a candidate export/function was observed; it does not prove CLR binding, marshaling behavior, call reachability, or runtime loading.",
218
+ "Missing supplied native symbols are reported as unresolved, not proof that a dependency cannot exist.",
219
+ "Managed metadata tokens and method RVAs are not interpreted as native addresses.",
220
+ ...(managed.coverage.state === "complete"
221
+ ? []
222
+ : ["Managed boundary input is partial or unavailable."]),
223
+ ...(native.unsupported === 0
224
+ ? []
225
+ : [
226
+ "Some native Evidence operations were unsupported by this workflow.",
227
+ ]),
228
+ ],
229
+ };
230
+ return managedNativeVerificationResultSchema.parse({
231
+ ...withoutId,
232
+ verification_id: `mnv_${sha256(withoutId)}`,
233
+ });
234
+ };
235
+ const collectNativeSymbols = (observations) => {
236
+ const symbols = [];
237
+ let accepted = 0;
238
+ let unsupported = 0;
239
+ for (const raw of observations) {
240
+ const evidence = parseEvidence(raw);
241
+ const extracted = symbolsForEvidence(evidence);
242
+ if (!extracted.supported) {
243
+ unsupported += 1;
244
+ }
245
+ else {
246
+ accepted += 1;
247
+ symbols.push(...extracted.symbols);
248
+ }
249
+ }
250
+ return { symbols, accepted, unsupported };
251
+ };
252
+ const symbolsForEvidence = (evidence) => {
253
+ if (evidence.operation === "inspect_macho") {
254
+ const result = inspectMachoSchema.parse(evidence.normalized_result);
255
+ return {
256
+ supported: true,
257
+ symbols: result.exports.items.map((symbol) => ({
258
+ evidence_id: evidence.evidence_id,
259
+ operation: evidence.operation,
260
+ name: symbol.name,
261
+ address: symbol.address,
262
+ module_name: moduleName(evidence),
263
+ module_path: modulePath(evidence),
264
+ source: "macho-export",
265
+ })),
266
+ };
267
+ }
268
+ if (evidence.operation === "analyze_function") {
269
+ const dossier = functionDossierSchema.parse(evidence.normalized_result);
270
+ return {
271
+ supported: true,
272
+ symbols: [
273
+ {
274
+ evidence_id: evidence.evidence_id,
275
+ operation: evidence.operation,
276
+ name: dossier.procedure.name,
277
+ address: dossier.procedure.address,
278
+ module_name: moduleName(evidence),
279
+ module_path: modulePath(evidence),
280
+ source: "function-dossier",
281
+ },
282
+ ],
283
+ };
284
+ }
285
+ return { supported: false, symbols: [] };
286
+ };
287
+ const verifyPinvoke = (item, managedEvidenceId, symbols, hasSupportedNativeEvidence, input) => {
288
+ const managed = {
289
+ token: item.token,
290
+ member_token: item.member_token,
291
+ member_name: item.member_name,
292
+ import_name: item.import_name,
293
+ import_scope_name: item.import_scope_name,
294
+ no_mangle: item.no_mangle,
295
+ char_set: item.char_set,
296
+ call_convention: item.call_convention,
297
+ declaration_verification: item.verification,
298
+ };
299
+ const names = candidateNames(managed);
300
+ const allCandidates = symbols.filter((symbol) => names.some((name) => sameSymbolName(name, symbol.name)));
301
+ const candidates = allCandidates.slice(0, input.limits.max_candidates_per_import);
302
+ const exact = candidates.find((symbol) => sameSymbolName(item.import_name, symbol.name) &&
303
+ moduleCompatible(item.import_scope_name, symbol));
304
+ const decorated = candidates.find((symbol) => moduleCompatible(item.import_scope_name, symbol));
305
+ const selected = exact ?? decorated ?? candidates[0] ?? null;
306
+ const basis = selected === null
307
+ ? hasSupportedNativeEvidence
308
+ ? "no-native-candidate"
309
+ : "unsupported-native-evidence"
310
+ : !moduleCompatible(item.import_scope_name, selected)
311
+ ? "module-mismatch"
312
+ : exact !== undefined
313
+ ? selected.source === "macho-export"
314
+ ? "exact-export-name"
315
+ : "exact-function-name"
316
+ : "decorated-name-candidate";
317
+ const status = selected === null
318
+ ? "unresolved"
319
+ : basis === "module-mismatch"
320
+ ? "contradicted"
321
+ : basis === "decorated-name-candidate"
322
+ ? "inferred"
323
+ : "verified";
324
+ const limitations = [
325
+ ...(item.import_scope_name === null
326
+ ? ["Managed declaration does not name an import scope/module."]
327
+ : []),
328
+ ...(status === "inferred"
329
+ ? [
330
+ "Native symbol matched a decorated candidate rather than the exact declared import name.",
331
+ ]
332
+ : []),
333
+ ...(status === "contradicted"
334
+ ? [
335
+ "A symbol name candidate exists, but the supplied native Evidence module identity does not match the managed import scope.",
336
+ ]
337
+ : []),
338
+ ...(status === "unresolved"
339
+ ? [
340
+ "No supplied native export/function Evidence matched this declared import.",
341
+ ]
342
+ : []),
343
+ ];
344
+ return {
345
+ verification: pinvokeVerificationSchema.parse({
346
+ item_id: `mnv_pinvoke_${sha256({
347
+ managedEvidenceId,
348
+ token: item.token,
349
+ importName: item.import_name,
350
+ importScope: item.import_scope_name,
351
+ candidates,
352
+ })}`,
353
+ managed,
354
+ status,
355
+ basis,
356
+ confidence: status === "verified"
357
+ ? "observed"
358
+ : status === "inferred"
359
+ ? "inferred"
360
+ : "unknown",
361
+ matched_native: selected,
362
+ candidates,
363
+ evidence_links: [
364
+ managedEvidenceId,
365
+ ...new Set(candidates.map(({ evidence_id: id }) => id)),
366
+ ],
367
+ limitations,
368
+ }),
369
+ omittedCandidates: Math.max(0, allCandidates.length - candidates.length),
370
+ };
371
+ };
372
+ const candidateNames = (managed) => {
373
+ const names = new Set([managed.import_name]);
374
+ if (!managed.no_mangle) {
375
+ if (managed.char_set === "unicode")
376
+ names.add(`${managed.import_name}W`);
377
+ if (managed.char_set === "ansi")
378
+ names.add(`${managed.import_name}A`);
379
+ if (managed.call_convention === "stdcall") {
380
+ names.add(`_${managed.import_name}`);
381
+ names.add(`_${managed.import_name}@0`);
382
+ }
383
+ }
384
+ return [...names];
385
+ };
386
+ const sameSymbolName = (left, right) => normalizeSymbol(left) === normalizeSymbol(right);
387
+ const normalizeSymbol = (value) => value.replace(/^_+/u, "").replace(/@\d+$/u, "").toLowerCase();
388
+ const moduleCompatible = (scope, symbol) => {
389
+ if (scope === null)
390
+ return true;
391
+ const expected = normalizeModule(scope);
392
+ return [symbol.module_name, symbol.module_path]
393
+ .filter((value) => value !== null)
394
+ .map(normalizeModule)
395
+ .some((value) => value === expected);
396
+ };
397
+ const normalizeModule = (value) => basename(value)
398
+ .toLowerCase()
399
+ .replace(/^lib/u, "")
400
+ .replace(/\.(dll|dylib|so|node|exe)$/u, "");
401
+ const moduleName = (evidence) => evidence.subject?.name ?? null;
402
+ const modulePath = (evidence) => evidence.subject?.local_path ?? null;
403
+ const countStatuses = (items) => ({
404
+ verified: items.filter(({ status }) => status === "verified").length,
405
+ inferred: items.filter(({ status }) => status === "inferred").length,
406
+ unresolved: items.filter(({ status }) => status === "unresolved").length,
407
+ contradicted: items.filter(({ status }) => status === "contradicted").length,
408
+ });
@@ -0,0 +1,183 @@
1
+ import { createHash } from "node:crypto";
2
+ import canonicalize from "canonicalize";
3
+ import { z } from "zod";
4
+ import { evidenceSchema, parseEvidence } from "./evidence.js";
5
+ import { managedMemberInspectionSchema } from "./managedArtifact.js";
6
+ const digestSchema = z.string().regex(/^[a-f0-9]{64}$/u);
7
+ const evidenceIdSchema = z.string().regex(/^ev_[a-f0-9]{64}$/u);
8
+ const tokenSchema = z.string().regex(/^0x[0-9a-f]{8}$/u);
9
+ const boundedTextSchema = z.string().min(1).max(65_536);
10
+ const boundedLabelSchema = z.string().min(1).max(512);
11
+ const decompilerSchema = z.strictObject({
12
+ name: boundedLabelSchema,
13
+ version: z.string().min(1).max(128).nullable(),
14
+ family: z.enum(["ilspy", "dnspy", "dnlib", "mono-cecil", "other"]),
15
+ executable_sha256: digestSchema.nullable(),
16
+ options: z.array(z.string().max(512)).max(50).default([]),
17
+ });
18
+ const managedReconstructionMethodInputSchema = z.strictObject({
19
+ token: tokenSchema,
20
+ signature_sha256: digestSchema,
21
+ normalized_il_sha256: digestSchema.nullable(),
22
+ reconstruction: z.strictObject({
23
+ kind: z.enum(["decompiled-csharp", "decompiled-il", "semantic-pseudocode"]),
24
+ language: z.enum(["csharp", "il", "pseudocode"]),
25
+ text: boundedTextSchema,
26
+ text_sha256: digestSchema.optional(),
27
+ source_path: z.string().min(1).max(4_096).nullable().optional(),
28
+ start_line: z.number().int().min(1).nullable().optional(),
29
+ end_line: z.number().int().min(1).nullable().optional(),
30
+ }),
31
+ });
32
+ export const managedReconstructionImportInputSchema = z.strictObject({
33
+ static_members: evidenceSchema,
34
+ decompiler: decompilerSchema,
35
+ methods: z.array(managedReconstructionMethodInputSchema).min(1).max(50),
36
+ notes: z.array(z.string().min(1).max(4_096)).max(100).default([]),
37
+ unknown_registry_approved: z.literal(true).optional(),
38
+ });
39
+ const importedMethodSchema = z.strictObject({
40
+ token: tokenSchema,
41
+ declaring_type: z.string().nullable(),
42
+ name: z.string(),
43
+ signature_sha256: digestSchema,
44
+ normalized_il_sha256: digestSchema.nullable(),
45
+ body_sha256: digestSchema.nullable(),
46
+ il_size: z.number().int().min(0),
47
+ reconstruction: z.strictObject({
48
+ kind: z.enum(["decompiled-csharp", "decompiled-il", "semantic-pseudocode"]),
49
+ language: z.enum(["csharp", "il", "pseudocode"]),
50
+ text: boundedTextSchema,
51
+ text_sha256: digestSchema,
52
+ text_length: z.number().int().min(1).max(65_536),
53
+ line_count: z.number().int().min(1),
54
+ source_path: z.string().min(1).max(4_096).nullable(),
55
+ start_line: z.number().int().min(1).nullable(),
56
+ end_line: z.number().int().min(1).nullable(),
57
+ }),
58
+ validation: z.strictObject({
59
+ matched_static_member: z.literal(true),
60
+ exact_build_required: z.literal(true),
61
+ canonical_observation: z.literal(false),
62
+ confidence_floor: z.literal("inference"),
63
+ }),
64
+ });
65
+ export const managedReconstructionImportResultSchema = z.strictObject({
66
+ schema_version: z.literal(1),
67
+ reconstruction_id: z.string().regex(/^mre_[a-f0-9]{64}$/u),
68
+ phase: z.literal("reconstruction-import"),
69
+ executed: z.literal(false),
70
+ static_observation: z.strictObject({
71
+ evidence_id: evidenceIdSchema,
72
+ artifact_sha256: digestSchema,
73
+ artifact_path: z.string().min(1),
74
+ byte_length: z.number().int().min(0),
75
+ module_name: z.string().nullable(),
76
+ mvid: z.string().uuid().nullable(),
77
+ metadata_status: z.enum(["absent", "complete", "partial", "malformed"]),
78
+ }),
79
+ decompiler: decompilerSchema.extend({
80
+ options_sha256: digestSchema,
81
+ }),
82
+ summary: z.strictObject({
83
+ imported_methods: z.number().int().min(1).max(50),
84
+ decompiled_csharp_methods: z.number().int().min(0).max(50),
85
+ decompiled_il_methods: z.number().int().min(0).max(50),
86
+ pseudocode_methods: z.number().int().min(0).max(50),
87
+ total_text_bytes: z.number().int().min(1),
88
+ }),
89
+ methods: z.array(importedMethodSchema).min(1).max(50),
90
+ notes: z.array(z.string().min(1).max(4_096)).max(100),
91
+ evidence_links: z.array(evidenceIdSchema).length(1),
92
+ limitations: z.array(z.string().min(1).max(4_096)).max(1_000),
93
+ });
94
+ const sha256Text = (value) => createHash("sha256").update(value).digest("hex");
95
+ const sha256Json = (value) => {
96
+ const serialized = canonicalize(value);
97
+ if (serialized === undefined)
98
+ throw new TypeError("Managed reconstruction canonicalization failed");
99
+ return createHash("sha256").update(serialized).digest("hex");
100
+ };
101
+ /** Authenticate decompiler reconstruction against static IL Evidence. */
102
+ export const importManagedReconstruction = (input) => {
103
+ const staticEvidence = parseEvidence(input.static_members);
104
+ if (staticEvidence.operation !== "inspect_managed_members")
105
+ throw new TypeError("Evidence operation is not inspect_managed_members");
106
+ const members = managedMemberInspectionSchema.parse(staticEvidence.normalized_result);
107
+ const methods = input.methods.map((candidate) => {
108
+ const observed = members.methods.items.find((item) => item.token === candidate.token &&
109
+ item.signature.raw_sha256 === candidate.signature_sha256 &&
110
+ item.body.normalized_il_sha256 === candidate.normalized_il_sha256);
111
+ if (observed === undefined)
112
+ throw new TypeError(`Reconstruction method lock ${candidate.token} does not match a static managed member observation`);
113
+ const textSha256 = sha256Text(candidate.reconstruction.text);
114
+ if (candidate.reconstruction.text_sha256 !== undefined &&
115
+ candidate.reconstruction.text_sha256 !== textSha256)
116
+ throw new TypeError(`Reconstruction text hash mismatch for ${candidate.token}`);
117
+ return {
118
+ token: observed.token,
119
+ declaring_type: observed.declaring_type,
120
+ name: observed.name,
121
+ signature_sha256: observed.signature.raw_sha256,
122
+ normalized_il_sha256: observed.body.normalized_il_sha256,
123
+ body_sha256: observed.body.il_sha256,
124
+ il_size: observed.body.il_size,
125
+ reconstruction: {
126
+ kind: candidate.reconstruction.kind,
127
+ language: candidate.reconstruction.language,
128
+ text: candidate.reconstruction.text,
129
+ text_sha256: textSha256,
130
+ text_length: candidate.reconstruction.text.length,
131
+ line_count: countLines(candidate.reconstruction.text),
132
+ source_path: candidate.reconstruction.source_path ?? null,
133
+ start_line: candidate.reconstruction.start_line ?? null,
134
+ end_line: candidate.reconstruction.end_line ?? null,
135
+ },
136
+ validation: {
137
+ matched_static_member: true,
138
+ exact_build_required: true,
139
+ canonical_observation: false,
140
+ confidence_floor: "inference",
141
+ },
142
+ };
143
+ });
144
+ const withoutId = {
145
+ schema_version: 1,
146
+ phase: "reconstruction-import",
147
+ executed: false,
148
+ static_observation: {
149
+ evidence_id: staticEvidence.evidence_id,
150
+ artifact_sha256: members.artifact.sha256,
151
+ artifact_path: members.artifact.path,
152
+ byte_length: members.artifact.byte_length,
153
+ module_name: members.module?.name ?? null,
154
+ mvid: members.module?.mvid ?? null,
155
+ metadata_status: members.metadata.status,
156
+ },
157
+ decompiler: {
158
+ ...input.decompiler,
159
+ options_sha256: sha256Json(input.decompiler.options),
160
+ },
161
+ summary: {
162
+ imported_methods: methods.length,
163
+ decompiled_csharp_methods: methods.filter(({ reconstruction }) => reconstruction.kind === "decompiled-csharp").length,
164
+ decompiled_il_methods: methods.filter(({ reconstruction }) => reconstruction.kind === "decompiled-il").length,
165
+ pseudocode_methods: methods.filter(({ reconstruction }) => reconstruction.kind === "semantic-pseudocode").length,
166
+ total_text_bytes: methods.reduce((total, { reconstruction }) => total + reconstruction.text_length, 0),
167
+ },
168
+ methods,
169
+ notes: input.notes,
170
+ evidence_links: [staticEvidence.evidence_id],
171
+ limitations: [
172
+ "Decompiler output is imported as reconstruction evidence and analyst inference; metadata and IL observations remain canonical.",
173
+ "This operation did not execute, load, reflect over, debug, instrument, patch, or decompile the target artifact.",
174
+ "The imported reconstruction is valid only for the exact artifact SHA-256, MVID, method signature, and normalized IL shape recorded here.",
175
+ "Obfuscated names, decompiler variables, and control-flow restructuring are not treated as proof without separate validation.",
176
+ ],
177
+ };
178
+ return managedReconstructionImportResultSchema.parse({
179
+ ...withoutId,
180
+ reconstruction_id: `mre_${sha256Json(withoutId)}`,
181
+ });
182
+ };
183
+ const countLines = (text) => text.length === 0 ? 0 : text.split(/\r\n|\r|\n/u).length;