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,887 @@
1
+ import { managedMemberInspectionSchema, } from "../domain/managedArtifact.js";
2
+ import { readManagedPeLayout, } from "./ManagedPeReader.js";
3
+ import { managedTableRowCounts, readManagedMetadataInventory, } from "./ManagedMetadataInventory.js";
4
+ import { metadataRowOffset, readManagedMetadataLayout, } from "./ManagedMetadataLayout.js";
5
+ import { MetadataRowCursor, metadataToken, readMetadataBlob, readMetadataString, sha256Bytes, } from "./ManagedMetadataHeaps.js";
6
+ import { ManagedReaderFailure, managedFailure, } from "./ManagedReaderFailure.js";
7
+ const emptyPage = (offset, limit) => ({
8
+ items: [],
9
+ offset,
10
+ limit,
11
+ total: 0,
12
+ returned: 0,
13
+ dropped: 0,
14
+ complete: offset === 0,
15
+ });
16
+ const page = (all, offset, limit) => {
17
+ const items = all.slice(offset, offset + limit);
18
+ return {
19
+ items,
20
+ offset,
21
+ limit,
22
+ total: all.length,
23
+ returned: items.length,
24
+ dropped: all.length - items.length,
25
+ complete: offset === 0 && offset + limit >= all.length,
26
+ };
27
+ };
28
+ const rowCursor = (bytes, layout, table, row) => {
29
+ const descriptor = layout.table(table);
30
+ const start = metadataRowOffset(layout, table, row);
31
+ if (descriptor === undefined)
32
+ throw managedFailure("invalid-row", `metadata.${String(table)}`, "Metadata table is absent", start);
33
+ return new MetadataRowCursor(bytes, start, start + descriptor.rowSize, `metadata.${descriptor.name}`);
34
+ };
35
+ const codedToken = (raw, bits, tables) => {
36
+ if (raw === 0)
37
+ return null;
38
+ const tag = raw & (2 ** bits - 1);
39
+ const row = Math.floor(raw / 2 ** bits);
40
+ const table = tables[tag];
41
+ return table === undefined || row === 0 ? null : metadataToken(table, row);
42
+ };
43
+ const rowRange = (table, start, nextStart) => {
44
+ const total = table?.rowCount ?? 0;
45
+ if (total === 0 || start === 0)
46
+ return { first: null, last: null, count: 0 };
47
+ const end = Math.min(nextStart === 0 ? total + 1 : nextStart, total + 1);
48
+ if (start >= end)
49
+ return { first: null, last: null, count: 0 };
50
+ return { first: start, last: end - 1, count: end - start };
51
+ };
52
+ const fullName = (namespace, name) => namespace.length === 0 ? name : `${namespace}.${name}`;
53
+ const readTypeRange = (bytes, layout, row, maxBytes) => {
54
+ const methods = layout.table(6);
55
+ const fields = layout.table(4);
56
+ const cursor = rowCursor(bytes, layout, 2, row);
57
+ cursor.readUInt32();
58
+ const name = readMetadataString(bytes, layout, cursor.readIndex(layout.stringIndexSize), maxBytes);
59
+ const namespace = readMetadataString(bytes, layout, cursor.readIndex(layout.stringIndexSize), maxBytes);
60
+ cursor.readIndex(layout.codedIndexSize("TypeDefOrRef"));
61
+ const fieldStart = cursor.readIndex(layout.tableIndexSize(4));
62
+ const methodStart = cursor.readIndex(layout.tableIndexSize(6));
63
+ let nextField = (fields?.rowCount ?? 0) + 1;
64
+ let nextMethod = (methods?.rowCount ?? 0) + 1;
65
+ const typeTable = layout.table(2);
66
+ if (typeTable !== undefined && row < typeTable.rowCount) {
67
+ const next = rowCursor(bytes, layout, 2, row + 1);
68
+ next.readUInt32();
69
+ next.readIndex(layout.stringIndexSize);
70
+ next.readIndex(layout.stringIndexSize);
71
+ next.readIndex(layout.codedIndexSize("TypeDefOrRef"));
72
+ nextField = next.readIndex(layout.tableIndexSize(4));
73
+ nextMethod = next.readIndex(layout.tableIndexSize(6));
74
+ }
75
+ return {
76
+ token: metadataToken(2, row),
77
+ fullName: fullName(namespace, name),
78
+ fieldStart,
79
+ fieldEnd: nextField,
80
+ methodStart,
81
+ methodEnd: nextMethod,
82
+ };
83
+ };
84
+ const typeRanges = (bytes, layout, maxBytes) => {
85
+ const typeTable = layout.table(2);
86
+ const ranges = [];
87
+ for (let row = 1; row <= (typeTable?.rowCount ?? 0); row += 1)
88
+ ranges.push(readTypeRange(bytes, layout, row, maxBytes));
89
+ return ranges;
90
+ };
91
+ const declaringType = (ranges, table, row) => {
92
+ for (const range of ranges) {
93
+ const start = table === "field" ? range.fieldStart : range.methodStart;
94
+ const end = table === "field" ? range.fieldEnd : range.methodEnd;
95
+ if (row >= start && row < end)
96
+ return { token: range.token, fullName: range.fullName };
97
+ }
98
+ return null;
99
+ };
100
+ const parseTypes = (bytes, layout, ranges, maxBytes) => {
101
+ const typeTable = layout.table(2);
102
+ const fields = layout.table(4);
103
+ const methods = layout.table(6);
104
+ const items = [];
105
+ for (let row = 1; row <= (typeTable?.rowCount ?? 0); row += 1) {
106
+ const cursor = rowCursor(bytes, layout, 2, row);
107
+ const flags = cursor.readUInt32();
108
+ const name = readMetadataString(bytes, layout, cursor.readIndex(layout.stringIndexSize), maxBytes);
109
+ const namespace = readMetadataString(bytes, layout, cursor.readIndex(layout.stringIndexSize), maxBytes);
110
+ const extendsRaw = cursor.readIndex(layout.codedIndexSize("TypeDefOrRef"));
111
+ const fieldStart = cursor.readIndex(layout.tableIndexSize(4));
112
+ const methodStart = cursor.readIndex(layout.tableIndexSize(6));
113
+ const nextRange = ranges[row] ?? null;
114
+ const fieldRange = rowRange(fields, fieldStart, nextRange?.fieldStart ?? 0);
115
+ const methodRange = rowRange(methods, methodStart, nextRange?.methodStart ?? 0);
116
+ items.push({
117
+ token: metadataToken(2, row),
118
+ row_offset: cursor.start,
119
+ namespace,
120
+ name,
121
+ full_name: fullName(namespace, name),
122
+ flags,
123
+ extends_token: codedToken(extendsRaw, 2, [2, 1, 27]),
124
+ field_list: {
125
+ first_row: fieldRange.first,
126
+ last_row: fieldRange.last,
127
+ count: fieldRange.count,
128
+ },
129
+ method_list: {
130
+ first_row: methodRange.first,
131
+ last_row: methodRange.last,
132
+ count: methodRange.count,
133
+ },
134
+ });
135
+ }
136
+ return items;
137
+ };
138
+ const readCompressed = (bytes, offset) => {
139
+ const first = bytes[offset];
140
+ if (first === undefined)
141
+ throw new RangeError("truncated compressed integer");
142
+ if ((first & 0x80) === 0)
143
+ return { value: first, next: offset + 1 };
144
+ const second = bytes[offset + 1];
145
+ if ((first & 0xc0) === 0x80 && second !== undefined)
146
+ return { value: ((first & 0x3f) << 8) | second, next: offset + 2 };
147
+ const b1 = bytes[offset + 1];
148
+ const b2 = bytes[offset + 2];
149
+ const b3 = bytes[offset + 3];
150
+ if ((first & 0xe0) === 0xc0 &&
151
+ b1 !== undefined &&
152
+ b2 !== undefined &&
153
+ b3 !== undefined)
154
+ return {
155
+ value: (first & 0x1f) * 0x01_00_00_00 + b1 * 0x01_00_00 + b2 * 0x0100 + b3,
156
+ next: offset + 4,
157
+ };
158
+ throw new RangeError("reserved compressed integer");
159
+ };
160
+ const ELEMENT_TYPES = new Map([
161
+ [0x01, "void"],
162
+ [0x02, "bool"],
163
+ [0x03, "char"],
164
+ [0x04, "i1"],
165
+ [0x05, "u1"],
166
+ [0x06, "i2"],
167
+ [0x07, "u2"],
168
+ [0x08, "i4"],
169
+ [0x09, "u4"],
170
+ [0x0a, "i8"],
171
+ [0x0b, "u8"],
172
+ [0x0c, "r4"],
173
+ [0x0d, "r8"],
174
+ [0x0e, "string"],
175
+ [0x18, "native-int"],
176
+ [0x19, "native-uint"],
177
+ [0x1c, "object"],
178
+ ]);
179
+ const readTypeSignature = (blob, offset) => {
180
+ const kind = blob[offset];
181
+ if (kind === undefined)
182
+ throw new RangeError("truncated type signature");
183
+ const named = ELEMENT_TYPES.get(kind);
184
+ if (named !== undefined)
185
+ return { value: named, next: offset + 1 };
186
+ if (kind === 0x0f) {
187
+ const inner = readTypeSignature(blob, offset + 1);
188
+ return { value: `${inner.value}&`, next: inner.next };
189
+ }
190
+ if (kind === 0x10) {
191
+ const inner = readTypeSignature(blob, offset + 1);
192
+ return { value: `${inner.value}*`, next: inner.next };
193
+ }
194
+ if (kind === 0x11 || kind === 0x12) {
195
+ const token = readCompressed(blob, offset + 1);
196
+ return {
197
+ value: `${kind === 0x11 ? "valuetype" : "class"}:${String(token.value)}`,
198
+ next: token.next,
199
+ };
200
+ }
201
+ if (kind === 0x1d) {
202
+ const inner = readTypeSignature(blob, offset + 1);
203
+ return { value: `${inner.value}[]`, next: inner.next };
204
+ }
205
+ if (kind === 0x1e) {
206
+ const variable = readCompressed(blob, offset + 1);
207
+ return { value: `mvar:${String(variable.value)}`, next: variable.next };
208
+ }
209
+ if (kind === 0x13) {
210
+ const variable = readCompressed(blob, offset + 1);
211
+ return { value: `var:${String(variable.value)}`, next: variable.next };
212
+ }
213
+ throw new RangeError(`unsupported element type 0x${kind.toString(16)}`);
214
+ };
215
+ const callingConvention = (value) => {
216
+ const base = value & 0x0f;
217
+ const flags = [
218
+ (value & 0x20) === 0 ? null : "has-this",
219
+ (value & 0x40) === 0 ? null : "explicit-this",
220
+ (value & 0x10) === 0 ? null : "generic",
221
+ ].filter((flag) => flag !== null);
222
+ const name = base === 0
223
+ ? "default"
224
+ : base === 5
225
+ ? "vararg"
226
+ : base === 6
227
+ ? "field"
228
+ : `unknown:${String(base)}`;
229
+ return flags.length === 0 ? name : `${name} ${flags.join(" ")}`;
230
+ };
231
+ const signature = (blob) => {
232
+ const raw = {
233
+ raw_length: blob.length,
234
+ raw_sha256: sha256Bytes(blob),
235
+ };
236
+ try {
237
+ if (blob.length === 0)
238
+ throw new RangeError("empty signature");
239
+ const first = blob[0] ?? 0;
240
+ if ((first & 0x0f) === 6) {
241
+ const fieldType = readTypeSignature(blob, 1);
242
+ return {
243
+ ...raw,
244
+ kind: "field",
245
+ parse_status: fieldType.next === blob.length ? "decoded" : "partial",
246
+ calling_convention: callingConvention(first),
247
+ generic_parameter_count: null,
248
+ parameter_count: null,
249
+ return_type: null,
250
+ parameter_types: [],
251
+ field_type: fieldType.value,
252
+ issue: fieldType.next === blob.length ? null : "Trailing signature data",
253
+ };
254
+ }
255
+ let offset = 1;
256
+ let genericParameterCount = null;
257
+ if ((first & 0x10) !== 0) {
258
+ const generic = readCompressed(blob, offset);
259
+ genericParameterCount = generic.value;
260
+ offset = generic.next;
261
+ }
262
+ const parameterCount = readCompressed(blob, offset);
263
+ offset = parameterCount.next;
264
+ const returnType = readTypeSignature(blob, offset);
265
+ offset = returnType.next;
266
+ const parameters = [];
267
+ for (let index = 0; index < parameterCount.value; index += 1) {
268
+ const parameter = readTypeSignature(blob, offset);
269
+ parameters.push(parameter.value);
270
+ offset = parameter.next;
271
+ }
272
+ return {
273
+ ...raw,
274
+ kind: "method",
275
+ parse_status: offset === blob.length ? "decoded" : "partial",
276
+ calling_convention: callingConvention(first),
277
+ generic_parameter_count: genericParameterCount,
278
+ parameter_count: parameterCount.value,
279
+ return_type: returnType.value,
280
+ parameter_types: parameters,
281
+ field_type: null,
282
+ issue: offset === blob.length ? null : "Trailing signature data",
283
+ };
284
+ }
285
+ catch (cause) {
286
+ return {
287
+ ...raw,
288
+ kind: "unknown",
289
+ parse_status: cause instanceof RangeError ? "unsupported" : "malformed",
290
+ calling_convention: null,
291
+ generic_parameter_count: null,
292
+ parameter_count: null,
293
+ return_type: null,
294
+ parameter_types: [],
295
+ field_type: null,
296
+ issue: cause instanceof Error ? cause.message : "Signature parse failed",
297
+ };
298
+ }
299
+ };
300
+ const opcode = (code) => OPCODES.get(code);
301
+ const OPCODES = new Map([
302
+ [0x00, { name: "nop", operand: "none" }],
303
+ [0x02, { name: "ldarg.0", operand: "none" }],
304
+ [0x03, { name: "ldarg.1", operand: "none" }],
305
+ [0x04, { name: "ldarg.2", operand: "none" }],
306
+ [0x05, { name: "ldarg.3", operand: "none" }],
307
+ [0x06, { name: "ldloc.0", operand: "none" }],
308
+ [0x07, { name: "ldloc.1", operand: "none" }],
309
+ [0x08, { name: "ldloc.2", operand: "none" }],
310
+ [0x09, { name: "ldloc.3", operand: "none" }],
311
+ [0x0a, { name: "stloc.0", operand: "none" }],
312
+ [0x0b, { name: "stloc.1", operand: "none" }],
313
+ [0x0c, { name: "stloc.2", operand: "none" }],
314
+ [0x0d, { name: "stloc.3", operand: "none" }],
315
+ [0x0e, { name: "ldarg.s", operand: "short-var" }],
316
+ [0x0f, { name: "ldarga.s", operand: "short-var" }],
317
+ [0x11, { name: "ldloc.s", operand: "short-var" }],
318
+ [0x13, { name: "stloc.s", operand: "short-var" }],
319
+ [0x14, { name: "ldnull", operand: "none" }],
320
+ [0x15, { name: "ldc.i4.m1", operand: "none" }],
321
+ [0x16, { name: "ldc.i4.0", operand: "none" }],
322
+ [0x17, { name: "ldc.i4.1", operand: "none" }],
323
+ [0x18, { name: "ldc.i4.2", operand: "none" }],
324
+ [0x19, { name: "ldc.i4.3", operand: "none" }],
325
+ [0x1a, { name: "ldc.i4.4", operand: "none" }],
326
+ [0x1b, { name: "ldc.i4.5", operand: "none" }],
327
+ [0x1c, { name: "ldc.i4.6", operand: "none" }],
328
+ [0x1d, { name: "ldc.i4.7", operand: "none" }],
329
+ [0x1e, { name: "ldc.i4.8", operand: "none" }],
330
+ [0x1f, { name: "ldc.i4.s", operand: "short-i" }],
331
+ [0x20, { name: "ldc.i4", operand: "i" }],
332
+ [0x25, { name: "dup", operand: "none" }],
333
+ [0x26, { name: "pop", operand: "none" }],
334
+ [0x28, { name: "call", operand: "method" }],
335
+ [0x2a, { name: "ret", operand: "none" }],
336
+ [0x2b, { name: "br.s", operand: "short-branch" }],
337
+ [0x2c, { name: "brfalse.s", operand: "short-branch" }],
338
+ [0x2d, { name: "brtrue.s", operand: "short-branch" }],
339
+ [0x38, { name: "br", operand: "branch" }],
340
+ [0x39, { name: "brfalse", operand: "branch" }],
341
+ [0x3a, { name: "brtrue", operand: "branch" }],
342
+ [0x45, { name: "switch", operand: "switch" }],
343
+ [0x58, { name: "add", operand: "none" }],
344
+ [0x59, { name: "sub", operand: "none" }],
345
+ [0x5a, { name: "mul", operand: "none" }],
346
+ [0x5b, { name: "div", operand: "none" }],
347
+ [0x6f, { name: "callvirt", operand: "method" }],
348
+ [0x70, { name: "cpobj", operand: "type" }],
349
+ [0x72, { name: "ldstr", operand: "string" }],
350
+ [0x73, { name: "newobj", operand: "method" }],
351
+ [0x74, { name: "castclass", operand: "type" }],
352
+ [0x75, { name: "isinst", operand: "type" }],
353
+ [0x7b, { name: "ldfld", operand: "field" }],
354
+ [0x7c, { name: "ldflda", operand: "field" }],
355
+ [0x7d, { name: "stfld", operand: "field" }],
356
+ [0x7e, { name: "ldsfld", operand: "field" }],
357
+ [0x7f, { name: "ldsflda", operand: "field" }],
358
+ [0x80, { name: "stsfld", operand: "field" }],
359
+ [0x8c, { name: "box", operand: "type" }],
360
+ [0x8d, { name: "newarr", operand: "type" }],
361
+ [0xa5, { name: "unbox.any", operand: "type" }],
362
+ [0xd0, { name: "ldtoken", operand: "token" }],
363
+ [0xdd, { name: "leave", operand: "branch" }],
364
+ [0xde, { name: "leave.s", operand: "short-branch" }],
365
+ [0xfe09, { name: "ldarg", operand: "var" }],
366
+ [0xfe0a, { name: "ldarga", operand: "var" }],
367
+ [0xfe0c, { name: "ldloc", operand: "var" }],
368
+ [0xfe0e, { name: "stloc", operand: "var" }],
369
+ ]);
370
+ const tokenKind = (token) => {
371
+ if (token.startsWith("0x06") ||
372
+ token.startsWith("0x0a") ||
373
+ token.startsWith("0x2b"))
374
+ return "method";
375
+ if (token.startsWith("0x04"))
376
+ return "field";
377
+ if (token.startsWith("0x70"))
378
+ return "string";
379
+ if (token.startsWith("0x11"))
380
+ return "signature";
381
+ if (token.startsWith("0x01") ||
382
+ token.startsWith("0x02") ||
383
+ token.startsWith("0x1b"))
384
+ return "type";
385
+ return "token";
386
+ };
387
+ const readOperand = (il, offset, kind) => {
388
+ switch (kind) {
389
+ case "none":
390
+ return { next: offset, anchorKind: "none", value: null };
391
+ case "short-var":
392
+ return {
393
+ next: offset + 1,
394
+ anchorKind: "variable",
395
+ value: String(il.readUInt8(offset)),
396
+ };
397
+ case "var":
398
+ return {
399
+ next: offset + 2,
400
+ anchorKind: "variable",
401
+ value: String(il.readUInt16LE(offset)),
402
+ };
403
+ case "short-i":
404
+ return {
405
+ next: offset + 1,
406
+ anchorKind: "constant",
407
+ value: String(il.readInt8(offset)),
408
+ };
409
+ case "i":
410
+ return {
411
+ next: offset + 4,
412
+ anchorKind: "constant",
413
+ value: String(il.readInt32LE(offset)),
414
+ };
415
+ case "short-branch":
416
+ return {
417
+ next: offset + 1,
418
+ anchorKind: "branch",
419
+ value: String(offset + 1 + il.readInt8(offset)),
420
+ };
421
+ case "branch":
422
+ return {
423
+ next: offset + 4,
424
+ anchorKind: "branch",
425
+ value: String(offset + 4 + il.readInt32LE(offset)),
426
+ };
427
+ case "switch": {
428
+ const count = il.readUInt32LE(offset);
429
+ return {
430
+ next: offset + 4 + count * 4,
431
+ anchorKind: "switch",
432
+ value: String(count),
433
+ };
434
+ }
435
+ case "method":
436
+ case "field":
437
+ case "type":
438
+ case "string":
439
+ case "token": {
440
+ const raw = il.readUInt32LE(offset);
441
+ const token = `0x${raw.toString(16).padStart(8, "0")}`;
442
+ return {
443
+ next: offset + 4,
444
+ anchorKind: kind === "token" ? tokenKind(token) : kind,
445
+ value: token,
446
+ };
447
+ }
448
+ default:
449
+ return { next: offset, anchorKind: "unknown", value: null };
450
+ }
451
+ };
452
+ const decodeInstructions = (il, limit) => {
453
+ const parsed = [];
454
+ let offset = 0;
455
+ let issue = null;
456
+ try {
457
+ while (offset < il.length && parsed.length < limit) {
458
+ const start = offset;
459
+ const first = il.readUInt8(offset);
460
+ offset += 1;
461
+ const code = first === 0xfe ? 0xfe00 + il.readUInt8(offset++) : first;
462
+ const descriptor = opcode(code) ?? {
463
+ name: `unknown.0x${code.toString(16)}`,
464
+ operand: "none",
465
+ };
466
+ const operand = readOperand(il, offset, descriptor.operand);
467
+ if (operand.next < offset || operand.next > il.length) {
468
+ issue = `Instruction ${descriptor.name} at IL offset ${String(start)} leaves method body`;
469
+ break;
470
+ }
471
+ offset = operand.next;
472
+ parsed.push({
473
+ offset: start,
474
+ opcode: descriptor.name,
475
+ operandKind: operand.anchorKind,
476
+ operand: operand.value,
477
+ });
478
+ }
479
+ }
480
+ catch (cause) {
481
+ issue =
482
+ cause instanceof Error ? cause.message : "Instruction decode failed";
483
+ }
484
+ const truncated = offset < il.length && issue === null ? 1 : 0;
485
+ return { parsed, count: parsed.length, truncated, issue };
486
+ };
487
+ const parseExceptionRegions = (bytes, offset, methodEnd) => {
488
+ if (offset >= methodEnd)
489
+ return [];
490
+ const kind = bytes.readUInt8(offset);
491
+ if ((kind & 0x3f) !== 1)
492
+ return [];
493
+ const fat = (kind & 0x40) !== 0;
494
+ const size = fat
495
+ ? bytes.readUInt32LE(offset) >>> 8
496
+ : bytes.readUInt8(offset + 1);
497
+ const start = fat ? offset + 4 : offset + 4;
498
+ const clauseSize = fat ? 24 : 12;
499
+ if (size < 4 || offset > methodEnd - size)
500
+ return [];
501
+ const count = Math.floor((size - 4) / clauseSize);
502
+ const regions = [];
503
+ for (let index = 0; index < count; index += 1) {
504
+ const clause = start + index * clauseSize;
505
+ if (fat) {
506
+ const flags = bytes.readUInt32LE(clause);
507
+ const extra = bytes.readUInt32LE(clause + 20);
508
+ regions.push({
509
+ flags,
510
+ try_offset: bytes.readUInt32LE(clause + 4),
511
+ try_length: bytes.readUInt32LE(clause + 8),
512
+ handler_offset: bytes.readUInt32LE(clause + 12),
513
+ handler_length: bytes.readUInt32LE(clause + 16),
514
+ class_token: flags === 0 ? `0x${extra.toString(16).padStart(8, "0")}` : null,
515
+ filter_offset: flags === 1 ? extra : null,
516
+ });
517
+ }
518
+ else {
519
+ const flags = bytes.readUInt16LE(clause);
520
+ const extra = bytes.readUInt32LE(clause + 8);
521
+ regions.push({
522
+ flags,
523
+ try_offset: bytes.readUInt16LE(clause + 2),
524
+ try_length: bytes.readUInt8(clause + 4),
525
+ handler_offset: bytes.readUInt16LE(clause + 5),
526
+ handler_length: bytes.readUInt8(clause + 7),
527
+ class_token: flags === 0 ? `0x${extra.toString(16).padStart(8, "0")}` : null,
528
+ filter_offset: flags === 1 ? extra : null,
529
+ });
530
+ }
531
+ }
532
+ return regions;
533
+ };
534
+ const methodBody = (bytes, pe, rva, limits) => {
535
+ if (rva === 0)
536
+ return {
537
+ status: "absent",
538
+ header_format: "none",
539
+ rva,
540
+ file_offset: null,
541
+ max_stack: null,
542
+ init_locals: null,
543
+ local_var_sig_token: null,
544
+ il_size: 0,
545
+ il_sha256: null,
546
+ normalized_il_sha256: null,
547
+ instruction_count: 0,
548
+ decoded_instruction_count: 0,
549
+ truncated_instructions: 0,
550
+ opcode_counts: {},
551
+ anchors: [],
552
+ exception_regions: [],
553
+ issue: null,
554
+ };
555
+ try {
556
+ const offset = pe.rvaToOffset(rva, 1, "method.body");
557
+ const first = bytes.readUInt8(offset);
558
+ const tiny = (first & 3) === 2;
559
+ const fat = (first & 3) === 3;
560
+ if (!tiny && !fat)
561
+ throw new RangeError("Unsupported method body header");
562
+ const headerSize = tiny ? 1 : (first >> 4) * 4;
563
+ const flags = tiny ? 0 : bytes.readUInt16LE(offset);
564
+ const maxStack = tiny ? 8 : bytes.readUInt16LE(offset + 2);
565
+ const ilSize = tiny ? first >> 2 : bytes.readUInt32LE(offset + 4);
566
+ const localSig = tiny ? 0 : bytes.readUInt32LE(offset + 8);
567
+ if (ilSize > limits.maxMethodBodyBytes)
568
+ return {
569
+ status: "too-large",
570
+ header_format: tiny ? "tiny" : "fat",
571
+ rva,
572
+ file_offset: offset,
573
+ max_stack: maxStack,
574
+ init_locals: tiny ? false : (flags & 0x10) !== 0,
575
+ local_var_sig_token: localSig === 0 ? null : `0x${localSig.toString(16).padStart(8, "0")}`,
576
+ il_size: ilSize,
577
+ il_sha256: null,
578
+ normalized_il_sha256: null,
579
+ instruction_count: 0,
580
+ decoded_instruction_count: 0,
581
+ truncated_instructions: 0,
582
+ opcode_counts: {},
583
+ anchors: [],
584
+ exception_regions: [],
585
+ issue: `Method body exceeds max_method_body_bytes ${String(limits.maxMethodBodyBytes)}`,
586
+ };
587
+ const ilOffset = offset + headerSize;
588
+ if (ilOffset > bytes.length - ilSize)
589
+ throw new RangeError("Method IL bytes leave artifact");
590
+ const il = bytes.subarray(ilOffset, ilOffset + ilSize);
591
+ const decoded = decodeInstructions(il, limits.maxMethodInstructions);
592
+ const opcodeCounts = {};
593
+ for (const instruction of decoded.parsed)
594
+ opcodeCounts[instruction.opcode] =
595
+ (opcodeCounts[instruction.opcode] ?? 0) + 1;
596
+ const anchors = decoded.parsed
597
+ .map((instruction, index) => ({ instruction, index }))
598
+ .filter(({ instruction }) => instruction.operandKind !== "none")
599
+ .slice(0, limits.instructionAnchorLimit)
600
+ .map(({ instruction }) => ({
601
+ il_offset: instruction.offset,
602
+ opcode: instruction.opcode,
603
+ operand_kind: instruction.operandKind,
604
+ operand: instruction.operand,
605
+ }));
606
+ const normalized = Buffer.from(JSON.stringify(decoded.parsed.map(({ opcode, operandKind, operand }) => [
607
+ opcode,
608
+ operandKind,
609
+ operand,
610
+ ])), "utf8");
611
+ const methodEnd = ilOffset + ilSize;
612
+ const sectionOffset = (methodEnd + 3) & ~3;
613
+ return {
614
+ status: decoded.issue === null ? "present" : "malformed",
615
+ header_format: tiny ? "tiny" : "fat",
616
+ rva,
617
+ file_offset: offset,
618
+ max_stack: maxStack,
619
+ init_locals: tiny ? false : (flags & 0x10) !== 0,
620
+ local_var_sig_token: localSig === 0 ? null : `0x${localSig.toString(16).padStart(8, "0")}`,
621
+ il_size: ilSize,
622
+ il_sha256: sha256Bytes(il),
623
+ normalized_il_sha256: sha256Bytes(normalized),
624
+ instruction_count: decoded.count,
625
+ decoded_instruction_count: decoded.parsed.length,
626
+ truncated_instructions: decoded.truncated,
627
+ opcode_counts: opcodeCounts,
628
+ anchors,
629
+ exception_regions: fat && (flags & 8) !== 0
630
+ ? parseExceptionRegions(bytes, sectionOffset, bytes.length)
631
+ : [],
632
+ issue: decoded.issue,
633
+ };
634
+ }
635
+ catch (cause) {
636
+ return {
637
+ status: "malformed",
638
+ header_format: "unknown",
639
+ rva,
640
+ file_offset: null,
641
+ max_stack: null,
642
+ init_locals: null,
643
+ local_var_sig_token: null,
644
+ il_size: 0,
645
+ il_sha256: null,
646
+ normalized_il_sha256: null,
647
+ instruction_count: 0,
648
+ decoded_instruction_count: 0,
649
+ truncated_instructions: 0,
650
+ opcode_counts: {},
651
+ anchors: [],
652
+ exception_regions: [],
653
+ issue: cause instanceof Error ? cause.message : "Method body parse failed",
654
+ };
655
+ }
656
+ };
657
+ const parseFields = (bytes, layout, ranges, maxBytes) => {
658
+ const fields = [];
659
+ const core = new Map();
660
+ const table = layout.table(4);
661
+ for (let row = 1; row <= (table?.rowCount ?? 0); row += 1) {
662
+ const cursor = rowCursor(bytes, layout, 4, row);
663
+ const flags = cursor.readUInt16();
664
+ const name = readMetadataString(bytes, layout, cursor.readIndex(layout.stringIndexSize), maxBytes);
665
+ const sig = readMetadataBlob(bytes, layout, cursor.readIndex(layout.blobIndexSize), maxBytes);
666
+ const declared = declaringType(ranges, "field", row);
667
+ const token = metadataToken(4, row);
668
+ fields.push({
669
+ token,
670
+ row_offset: cursor.start,
671
+ declaring_type_token: declared?.token ?? null,
672
+ declaring_type: declared?.fullName ?? null,
673
+ name,
674
+ flags,
675
+ signature: signature(sig),
676
+ });
677
+ core.set(token, { token, name });
678
+ }
679
+ return { fields, core };
680
+ };
681
+ const parseMemberRefs = (bytes, layout, maxBytes) => {
682
+ const refs = [];
683
+ const core = new Map();
684
+ const table = layout.table(10);
685
+ for (let row = 1; row <= (table?.rowCount ?? 0); row += 1) {
686
+ const cursor = rowCursor(bytes, layout, 10, row);
687
+ const parentRaw = cursor.readIndex(layout.codedIndexSize("MemberRefParent"));
688
+ const name = readMetadataString(bytes, layout, cursor.readIndex(layout.stringIndexSize), maxBytes);
689
+ const sig = readMetadataBlob(bytes, layout, cursor.readIndex(layout.blobIndexSize), maxBytes);
690
+ const token = metadataToken(10, row);
691
+ refs.push({
692
+ token,
693
+ row_offset: cursor.start,
694
+ parent_token: codedToken(parentRaw, 3, [2, 1, 26, 6, 27]),
695
+ name,
696
+ signature: signature(sig),
697
+ });
698
+ core.set(token, { token, name });
699
+ }
700
+ return { refs, core };
701
+ };
702
+ const parseMethods = (bytes, layout, pe, ranges, maxBytes, limits) => {
703
+ const methods = [];
704
+ const core = new Map();
705
+ const table = layout.table(6);
706
+ for (let row = 1; row <= (table?.rowCount ?? 0); row += 1) {
707
+ const cursor = rowCursor(bytes, layout, 6, row);
708
+ const rva = cursor.readUInt32();
709
+ const implFlags = cursor.readUInt16();
710
+ const flags = cursor.readUInt16();
711
+ const name = readMetadataString(bytes, layout, cursor.readIndex(layout.stringIndexSize), maxBytes);
712
+ const sig = readMetadataBlob(bytes, layout, cursor.readIndex(layout.blobIndexSize), maxBytes);
713
+ cursor.readIndex(layout.tableIndexSize(8));
714
+ const declared = declaringType(ranges, "method", row);
715
+ const token = metadataToken(6, row);
716
+ methods.push({
717
+ token,
718
+ row_offset: cursor.start,
719
+ declaring_type_token: declared?.token ?? null,
720
+ declaring_type: declared?.fullName ?? null,
721
+ name,
722
+ rva,
723
+ impl_flags: implFlags,
724
+ flags,
725
+ signature: signature(sig),
726
+ body: methodBody(bytes, pe, rva, limits),
727
+ });
728
+ core.set(token, { token, name, declaringType: declared?.fullName ?? null });
729
+ }
730
+ return { methods, core };
731
+ };
732
+ const targetKind = (token) => token.startsWith("0x06")
733
+ ? "method-def"
734
+ : token.startsWith("0x0a")
735
+ ? "member-ref"
736
+ : token.startsWith("0x2b")
737
+ ? "method-spec"
738
+ : "unknown";
739
+ const edges = (methods, methodCore, fieldCore, refCore) => {
740
+ const callEdges = [];
741
+ const fieldAccesses = [];
742
+ for (const method of methods) {
743
+ for (const anchor of method.body.anchors) {
744
+ if (anchor.operand_kind === "method" && anchor.operand !== null) {
745
+ const named = methodCore.get(anchor.operand)?.name ??
746
+ refCore.get(anchor.operand)?.name ??
747
+ null;
748
+ callEdges.push({
749
+ caller_token: method.token,
750
+ caller: method.declaring_type === null
751
+ ? method.name
752
+ : `${method.declaring_type}.${method.name}`,
753
+ opcode: anchor.opcode,
754
+ target_token: anchor.operand,
755
+ target_kind: targetKind(anchor.operand),
756
+ target_name: named,
757
+ });
758
+ }
759
+ if (anchor.operand_kind === "field" && anchor.operand !== null) {
760
+ fieldAccesses.push({
761
+ method_token: method.token,
762
+ method: method.declaring_type === null
763
+ ? method.name
764
+ : `${method.declaring_type}.${method.name}`,
765
+ opcode: anchor.opcode,
766
+ field_token: anchor.operand,
767
+ field_name: fieldCore.get(anchor.operand)?.name ?? null,
768
+ });
769
+ }
770
+ }
771
+ }
772
+ return { callEdges, fieldAccesses };
773
+ };
774
+ const unavailable = (target, bytes, limits, issue) => managedMemberInspectionSchema.parse({
775
+ schema_version: 1,
776
+ artifact: {
777
+ path: target.path,
778
+ sha256: target.sha256,
779
+ byte_length: bytes.length,
780
+ format: "pe",
781
+ },
782
+ module: null,
783
+ metadata: {
784
+ status: issue === null ? "absent" : "malformed",
785
+ version: null,
786
+ table_row_counts: {},
787
+ },
788
+ identity_scope: {
789
+ token_identity: "build-local",
790
+ requires_artifact_sha256: target.sha256,
791
+ requires_mvid: null,
792
+ },
793
+ types: emptyPage(limits.typeOffset, limits.typeLimit),
794
+ fields: emptyPage(limits.fieldOffset, limits.fieldLimit),
795
+ methods: emptyPage(limits.methodOffset, limits.methodLimit),
796
+ member_refs: emptyPage(limits.memberRefOffset, limits.memberRefLimit),
797
+ call_edges: emptyPage(limits.edgeOffset, limits.edgeLimit),
798
+ field_accesses: emptyPage(limits.edgeOffset, limits.edgeLimit),
799
+ coverage: {
800
+ state: "unavailable",
801
+ issues: issue === null ? [] : [issue],
802
+ },
803
+ limitations: [
804
+ issue === null
805
+ ? "The PE has no admitted CLI metadata; managed member inspection is unavailable."
806
+ : "The CLI metadata could not be admitted; managed member inspection is unavailable.",
807
+ ],
808
+ });
809
+ const resourceDirectory = (pe) => {
810
+ if (pe.cli === null ||
811
+ pe.cli.resources.rva === 0 ||
812
+ pe.cli.resources.size === 0)
813
+ return null;
814
+ return {
815
+ offset: pe.rvaToOffset(pe.cli.resources.rva, pe.cli.resources.size, "cli.resources"),
816
+ size: pe.cli.resources.size,
817
+ };
818
+ };
819
+ /** Inspect metadata members and method bodies without loading target code. */
820
+ export const inspectManagedMembersBytes = (bytes, target, limits) => {
821
+ const pe = readManagedPeLayout(bytes);
822
+ if (pe.cli === null)
823
+ return unavailable(target, bytes, limits, pe.cliIssue);
824
+ const issues = [];
825
+ try {
826
+ const rootOffset = pe.rvaToOffset(pe.cli.metadata.rva, pe.cli.metadata.size, "cli.metadata");
827
+ if (pe.cli.metadata.size > limits.maxMetadataBytes)
828
+ throw managedFailure("limit-exceeded", "metadata.root", `CLI metadata size exceeds max_metadata_bytes ${String(limits.maxMetadataBytes)}`, rootOffset);
829
+ const layout = readManagedMetadataLayout(bytes, rootOffset, pe.cli.metadata.size, limits.maxTableRows);
830
+ const inventory = readManagedMetadataInventory(bytes, layout, {
831
+ referenceOffset: 0,
832
+ referenceLimit: 1,
833
+ resourceOffset: 0,
834
+ resourceLimit: 1,
835
+ attributeOffset: 0,
836
+ attributeLimit: 1,
837
+ maxHeapItemBytes: limits.maxHeapItemBytes,
838
+ }, resourceDirectory(pe));
839
+ issues.push(...inventory.issues);
840
+ const ranges = typeRanges(bytes, layout, limits.maxHeapItemBytes);
841
+ const types = parseTypes(bytes, layout, ranges, limits.maxHeapItemBytes);
842
+ const fields = parseFields(bytes, layout, ranges, limits.maxHeapItemBytes);
843
+ const memberRefs = parseMemberRefs(bytes, layout, limits.maxHeapItemBytes);
844
+ const methods = parseMethods(bytes, layout, pe, ranges, limits.maxHeapItemBytes, limits);
845
+ const related = edges(methods.methods, methods.core, fields.core, memberRefs.core);
846
+ return managedMemberInspectionSchema.parse({
847
+ schema_version: 1,
848
+ artifact: {
849
+ path: target.path,
850
+ sha256: target.sha256,
851
+ byte_length: bytes.length,
852
+ format: "pe",
853
+ },
854
+ module: inventory.module,
855
+ metadata: {
856
+ status: issues.length === 0 ? "complete" : "partial",
857
+ version: layout.version,
858
+ table_row_counts: managedTableRowCounts(layout),
859
+ },
860
+ identity_scope: {
861
+ token_identity: "build-local",
862
+ requires_artifact_sha256: target.sha256,
863
+ requires_mvid: inventory.module?.mvid ?? null,
864
+ },
865
+ types: page(types, limits.typeOffset, limits.typeLimit),
866
+ fields: page(fields.fields, limits.fieldOffset, limits.fieldLimit),
867
+ methods: page(methods.methods, limits.methodOffset, limits.methodLimit),
868
+ member_refs: page(memberRefs.refs, limits.memberRefOffset, limits.memberRefLimit),
869
+ call_edges: page(related.callEdges, limits.edgeOffset, limits.edgeLimit),
870
+ field_accesses: page(related.fieldAccesses, limits.edgeOffset, limits.edgeLimit),
871
+ coverage: {
872
+ state: issues.length === 0 ? "complete" : "partial",
873
+ issues,
874
+ },
875
+ limitations: [
876
+ "Metadata tokens are build-local coordinates and are only meaningful with the reported artifact SHA-256 and MVID.",
877
+ "CIL instruction anchors are decoded from file-backed method bodies only; no target assembly is loaded or executed.",
878
+ "Signatures are decoded for common ECMA-335 primitive, class, valuetype, pointer, byref, array, and generic variable forms; unsupported forms retain raw signature hashes.",
879
+ ],
880
+ });
881
+ }
882
+ catch (cause) {
883
+ if (cause instanceof ManagedReaderFailure)
884
+ return unavailable(target, bytes, limits, cause.issue);
885
+ throw cause;
886
+ }
887
+ };