rea-agents 2.0.0 → 2.1.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 (246) hide show
  1. package/README.md +78 -32
  2. package/bridge/ghidra/ReaGhidraBridge.java +96 -11
  3. package/dist/application/ArtifactExtraction.js +1 -1
  4. package/dist/application/ArtifactInventory/classify.js +42 -0
  5. package/dist/application/ArtifactInventory/hash.js +43 -0
  6. package/dist/application/ArtifactInventory/reader.js +41 -0
  7. package/dist/application/ArtifactInventory/scanCanonical.js +155 -0
  8. package/dist/application/ArtifactInventory/scanReader.js +124 -0
  9. package/dist/application/ArtifactInventory/types.js +10 -0
  10. package/dist/application/ArtifactInventory.js +9 -351
  11. package/dist/application/AuthorizedArtifactInventory.js +2 -2
  12. package/dist/application/BinarySession.js +47 -270
  13. package/dist/application/BinarySessionExecution.js +28 -0
  14. package/dist/application/BinarySessionOpen.js +39 -0
  15. package/dist/application/BinarySessionRecords.js +149 -0
  16. package/dist/application/BinarySessionStatus.js +75 -0
  17. package/dist/application/CapabilityInventory.js +24 -2
  18. package/dist/application/ClientRegistrationStatus.js +1 -1
  19. package/dist/application/CrossVersionInventory.js +8 -2
  20. package/dist/application/CrossVersionInvestigation.js +1 -23
  21. package/dist/application/CrossVersionInvestigationCollections.js +23 -0
  22. package/dist/application/DirectAnalysis.js +47 -44
  23. package/dist/application/Doctor.js +156 -238
  24. package/dist/application/DoctorDiagnostics.js +180 -0
  25. package/dist/application/ElectronBoundaryAnalysis.js +23 -7
  26. package/dist/application/ElectronBoundaryGraphWindows.js +32 -8
  27. package/dist/application/EnhancedToolPagination.js +22 -0
  28. package/dist/application/EnhancedToolTypes.js +4 -0
  29. package/dist/application/EnhancedTools.js +20 -37
  30. package/dist/application/InstrumentedJavaScriptReplayHost.js +36 -0
  31. package/dist/application/InvestigationWorkspaceStore.js +24 -10
  32. package/dist/application/JavaScriptArtifactAnalysis.js +45 -6
  33. package/dist/application/JavaScriptArtifactFiles.js +6 -2
  34. package/dist/application/JavaScriptArtifactGraphBuilder.js +23 -1
  35. package/dist/application/JavaScriptArtifactGraphBundlers.js +225 -0
  36. package/dist/application/JavaScriptArtifactGraphContext.js +37 -36
  37. package/dist/application/JavaScriptArtifactGraphDocuments.js +27 -4
  38. package/dist/application/JavaScriptArtifactGraphFindings.js +54 -8
  39. package/dist/application/JavaScriptArtifactGraphStructure.js +29 -101
  40. package/dist/application/JavaScriptArtifactPathResolution.js +269 -0
  41. package/dist/application/JavaScriptArtifactReconstruction.js +1 -1
  42. package/dist/application/JavaScriptJsonModules.js +39 -0
  43. package/dist/application/JavaScriptModuleRelationships.js +262 -0
  44. package/dist/application/JavaScriptReplayPermission.js +16 -0
  45. package/dist/application/JavaScriptReplayService.js +182 -156
  46. package/dist/application/LinuxHopper.js +10 -0
  47. package/dist/application/MacHopper.js +22 -11
  48. package/dist/application/NodeRuntimeCharacterizationService.js +196 -0
  49. package/dist/application/PermissionAuthority.js +106 -63
  50. package/dist/application/ProcessCaptureCapability.js +1 -1
  51. package/dist/application/ProcessCapturePermission.js +13 -0
  52. package/dist/application/ProcessCli.js +2 -12
  53. package/dist/application/ProcessHarness.js +1 -1
  54. package/dist/application/ProjectPermissionStore.js +48 -9
  55. package/dist/application/RealHopperAssertions.js +76 -0
  56. package/dist/application/ReconstructionCoverageService.js +69 -0
  57. package/dist/application/ReconstructionCoverageWorkspaceStore.js +31 -0
  58. package/dist/application/RuntimeExecutableDiagnostics.js +172 -0
  59. package/dist/application/Setup.js +147 -259
  60. package/dist/application/SetupClientConfiguration.js +220 -0
  61. package/dist/application/SetupClients.js +24 -2
  62. package/dist/application/SetupPlan.js +123 -12
  63. package/dist/application/SetupSkill.js +26 -12
  64. package/dist/application/SupportedClients.js +1 -0
  65. package/dist/application/Uninstall.js +6 -3
  66. package/dist/application/WindowsCapabilities.js +86 -0
  67. package/dist/artifacts/AsarArtifactReader.js +11 -0
  68. package/dist/browser/BrowserProcessStartup.js +88 -0
  69. package/dist/browser/CdpCaptureDocuments.js +12 -8
  70. package/dist/browser/CdpCaptureEventBodyShapes.js +75 -0
  71. package/dist/browser/CdpCaptureEventHandlers.js +336 -0
  72. package/dist/browser/CdpCaptureEventHelpers.js +71 -0
  73. package/dist/browser/CdpCaptureEventState.js +1 -0
  74. package/dist/browser/CdpCaptureEventTypes.js +1 -0
  75. package/dist/browser/CdpCaptureEvents.js +29 -496
  76. package/dist/browser/CdpObservationSession.js +51 -8
  77. package/dist/browser/CdpPageCapture.js +12 -133
  78. package/dist/browser/CdpPageCaptureScripts.js +97 -0
  79. package/dist/browser/CdpPageCaptureWorkers.js +46 -0
  80. package/dist/browser/CdpWebMcpDiscovery.js +38 -14
  81. package/dist/browser/PngVisualDiff.js +9 -2
  82. package/dist/catalogIdentity.js +4 -0
  83. package/dist/cli/artifactCommands.js +82 -0
  84. package/dist/cli/coreAnalysisCommands.js +232 -0
  85. package/dist/cli/managedCommands.js +397 -0
  86. package/dist/cli/managedProjectGraphCommand.js +24 -0
  87. package/dist/cli/options.js +9 -0
  88. package/dist/cli/setupCommands.js +108 -0
  89. package/dist/cli/types.js +1 -0
  90. package/dist/cli/utilityCommands.js +122 -0
  91. package/dist/cli.js +10 -768
  92. package/dist/cliApplicationCommands.js +118 -4
  93. package/dist/cliBrowserAdvancedCommands.js +73 -16
  94. package/dist/cliBrowserCommands.js +39 -115
  95. package/dist/cliCommandNames.js +4 -0
  96. package/dist/cliElectronCommands.js +33 -62
  97. package/dist/cliEvidenceCommands.js +13 -2
  98. package/dist/cliInvestigationCommands.js +53 -10
  99. package/dist/cliObservationOptions.js +137 -0
  100. package/dist/cliPolicyCommands.js +80 -46
  101. package/dist/cliSetup.js +289 -0
  102. package/dist/config/environment.js +107 -0
  103. package/dist/config/parseConfig.js +228 -0
  104. package/dist/config/parsers.js +68 -0
  105. package/dist/config/permissions.js +30 -0
  106. package/dist/config/types.js +1 -0
  107. package/dist/config.js +1 -375
  108. package/dist/contracts/applicationToolContracts.js +134 -0
  109. package/dist/contracts/managedToolContracts.js +1 -1
  110. package/dist/contracts/managedWorkflowToolContracts.js +3 -3
  111. package/dist/contracts/providerSelection.js +2 -0
  112. package/dist/contracts/toolContracts.js +2 -2
  113. package/dist/contracts/toolEffects.js +14 -0
  114. package/dist/contracts/toolOutputSchemaGroups.js +232 -0
  115. package/dist/contracts/toolOutputSchemaPrimitives.js +327 -0
  116. package/dist/contracts/toolOutputSchemas.js +2 -550
  117. package/dist/domain/analysisErrorPresentation.js +234 -0
  118. package/dist/domain/analysisErrorProjection.js +284 -0
  119. package/dist/domain/artifactComparison.js +34 -15
  120. package/dist/domain/binaryTarget.js +87 -37
  121. package/dist/domain/browserObservation.js +1 -285
  122. package/dist/domain/browserObservationSchemas.js +286 -0
  123. package/dist/domain/electronStaticAnalysisBrowser.js +14 -2
  124. package/dist/domain/errors.js +4 -486
  125. package/dist/domain/functionComparison.js +4 -165
  126. package/dist/domain/functionComparisonDimensions.js +164 -0
  127. package/dist/domain/javascriptApplicationVersionItems.js +18 -6
  128. package/dist/domain/javascriptApplicationVersionKeys.js +8 -8
  129. package/dist/domain/javascriptExportInstrumentation.js +97 -0
  130. package/dist/domain/javascriptFeatureTraversal.js +19 -7
  131. package/dist/domain/javascriptRuntimeStaticCandidates.js +3 -1
  132. package/dist/domain/javascriptSemanticAnalysis.js +386 -0
  133. package/dist/domain/javascriptSemanticIr.js +30 -0
  134. package/dist/domain/javascriptSemanticProjection.js +366 -0
  135. package/dist/domain/javascriptSemanticState.js +40 -0
  136. package/dist/domain/javascriptSemanticTraversal.js +22 -0
  137. package/dist/domain/javascriptSemanticValues.js +352 -0
  138. package/dist/domain/javascriptStaticAnalysis.js +5 -221
  139. package/dist/domain/javascriptStaticAnalysisBundler.js +209 -0
  140. package/dist/domain/javascriptStaticAnalysisCalls.js +180 -0
  141. package/dist/domain/javascriptStaticAnalysisHelpers.js +71 -2
  142. package/dist/domain/knownPageCoverage.js +21 -0
  143. package/dist/domain/managedApplicationGraph.js +14 -364
  144. package/dist/domain/managedApplicationGraphCoverage.js +170 -0
  145. package/dist/domain/managedApplicationGraphNodeCoverage.js +6 -0
  146. package/dist/domain/managedApplicationGraphNodes.js +336 -0
  147. package/dist/domain/managedArtifact.js +16 -8
  148. package/dist/domain/managedMemberComparison.js +37 -391
  149. package/dist/domain/managedMemberComparisonCoverage.js +72 -0
  150. package/dist/domain/managedMemberComparisonMatch.js +356 -0
  151. package/dist/domain/managedNativeVerification.js +27 -389
  152. package/dist/domain/managedNativeVerificationMatch.js +272 -0
  153. package/dist/domain/managedNativeVerificationSchemas.js +141 -0
  154. package/dist/domain/managedReconstruction.js +82 -69
  155. package/dist/domain/managedRuntimeCorrelation.js +12 -3
  156. package/dist/domain/nodeRuntimeCharacterization.js +66 -0
  157. package/dist/domain/permissionPolicy.js +2 -0
  158. package/dist/domain/reconstructionCoverage.js +387 -0
  159. package/dist/domain/reconstructionCoverageEvaluation.js +212 -0
  160. package/dist/domain/reconstructionVerificationSchemas.js +8 -0
  161. package/dist/domain/runtimeCharacterization.js +85 -0
  162. package/dist/domain/webBundleAnalyzer.js +3 -409
  163. package/dist/domain/webBundleAnalyzerAst.js +77 -0
  164. package/dist/domain/webBundleAnalyzerInspection.js +307 -0
  165. package/dist/domain/webBundleAnalyzerResult.js +120 -0
  166. package/dist/dotnet/ManagedArtifactInspector.js +50 -29
  167. package/dist/dotnet/ManagedMemberInspector.js +51 -785
  168. package/dist/dotnet/ManagedMemberInspectorCore.js +297 -0
  169. package/dist/dotnet/ManagedMemberInstructionDecoder.js +427 -0
  170. package/dist/dotnet/ManagedMemberRows.js +120 -0
  171. package/dist/dotnet/ManagedMetadataInventory.js +42 -306
  172. package/dist/dotnet/ManagedMetadataInventoryRows.js +292 -0
  173. package/dist/dotnet/ManagedMetadataLayout.js +48 -111
  174. package/dist/dotnet/ManagedMetadataRowSizes.js +54 -0
  175. package/dist/dotnet/ManagedMethodBodyReader.js +143 -0
  176. package/dist/dotnet/ManagedNativeBoundaryHelpers.js +311 -0
  177. package/dist/dotnet/ManagedNativeBoundaryInspector.js +102 -350
  178. package/dist/dotnet/ManagedPeReader.js +59 -28
  179. package/dist/generatedPackageMetadata.js +2 -2
  180. package/dist/ghidra/GhidraClient.js +76 -91
  181. package/dist/ghidra/GhidraClientStartup.js +29 -0
  182. package/dist/ghidra/GhidraClientWire.js +34 -0
  183. package/dist/ghidra/GhidraDefaults.js +1 -1
  184. package/dist/ghidra/GhidraDiagnostics.js +6 -1
  185. package/dist/ghidra/GhidraInstallation.js +20 -15
  186. package/dist/ghidra/GhidraLauncher.js +93 -29
  187. package/dist/ghidra/GhidraProvider.js +61 -161
  188. package/dist/ghidra/GhidraProviderCapabilities.js +173 -0
  189. package/dist/ghidra/GhidraSessionValues.js +2 -0
  190. package/dist/ghidra/GhidraSocketConnection.js +44 -7
  191. package/dist/ghidra/GhidraTargetSnapshot.js +30 -0
  192. package/dist/ghidra/GhidraTransport.js +71 -0
  193. package/dist/identity.js +3 -2
  194. package/dist/main/messages.js +5 -0
  195. package/dist/main/reload.js +49 -0
  196. package/dist/main/shutdown.js +25 -0
  197. package/dist/main/startup.js +14 -0
  198. package/dist/main/state.js +26 -0
  199. package/dist/main/transport.js +61 -0
  200. package/dist/main/types.js +1 -0
  201. package/dist/main.js +35 -146
  202. package/dist/process/PrivateRuntimeRoot.js +3 -2
  203. package/dist/process/ProcessOwnership.js +35 -0
  204. package/dist/process/ProviderProcess.js +2 -0
  205. package/dist/replay/JavaScriptReplayWorker.js +64 -50
  206. package/dist/replay/LinuxJavaScriptReplayRunner.js +173 -390
  207. package/dist/replay/LinuxJavaScriptReplaySandbox.js +140 -0
  208. package/dist/replay/ReplayOutcome.js +95 -0
  209. package/dist/replay/ReplayProcessLifecycle.js +71 -0
  210. package/dist/server/ProcessCaptureElicitation.js +157 -0
  211. package/dist/server/createServer.js +119 -54
  212. package/dist/server/registerApplicationTools/characterization.js +57 -0
  213. package/dist/server/registerApplicationTools/compareVersions.js +34 -0
  214. package/dist/server/registerApplicationTools/controlledReplay.js +38 -0
  215. package/dist/server/registerApplicationTools/coverage.js +84 -0
  216. package/dist/server/registerApplicationTools/helpers.js +42 -0
  217. package/dist/server/registerApplicationTools/traceFeature.js +26 -0
  218. package/dist/server/registerApplicationTools/types.js +1 -0
  219. package/dist/server/registerApplicationTools.js +10 -112
  220. package/dist/server/registerBrowserTools.js +59 -83
  221. package/dist/server/registerElectronTools.js +31 -46
  222. package/dist/server/registerEnhancedTools.js +34 -42
  223. package/dist/server/registerEvidenceResources.js +54 -0
  224. package/dist/server/registerInvestigationTools/helpers.js +74 -0
  225. package/dist/server/registerInvestigationTools/types.js +1 -0
  226. package/dist/server/registerInvestigationTools.js +83 -116
  227. package/dist/server/registerManagedWorkflowTools/compareManagedMembers.js +65 -0
  228. package/dist/server/registerManagedWorkflowTools/contract.js +8 -0
  229. package/dist/server/registerManagedWorkflowTools/evidence.js +43 -0
  230. package/dist/server/registerManagedWorkflowTools/importManagedReconstruction.js +39 -0
  231. package/dist/server/registerManagedWorkflowTools/planManagedRuntimeCorrelation.js +39 -0
  232. package/dist/server/registerManagedWorkflowTools/projectManagedApplicationGraph.js +52 -0
  233. package/dist/server/registerManagedWorkflowTools/types.js +1 -0
  234. package/dist/server/registerManagedWorkflowTools/verifyManagedNativeBoundaries.js +75 -0
  235. package/dist/server/registerManagedWorkflowTools.js +10 -274
  236. package/dist/server/registerOfficialTools.js +73 -59
  237. package/dist/server/registerSessionRecordTools.js +137 -0
  238. package/dist/server/registerSessionStatusTool.js +2 -1
  239. package/dist/server/registerSessionTools.js +60 -164
  240. package/dist/server/sessionAvailabilityPolicy.js +10 -0
  241. package/dist/server/toolRegistrationOptions.js +101 -3
  242. package/dist/server/toolResult.js +5 -0
  243. package/package.json +10 -11
  244. package/skills/{rea-analysis → reverse-engineer-anything}/SKILL.md +20 -7
  245. package/scripts/prepare-node-pty.mjs +0 -35
  246. package/scripts/rebuild-native.mjs +0 -22
@@ -1,776 +1,10 @@
1
1
  import { managedMemberInspectionSchema, } from "../domain/managedArtifact.js";
2
2
  import { readManagedPeLayout, } from "./ManagedPeReader.js";
3
3
  import { managedTableRowCounts, readManagedMetadataInventory, } from "./ManagedMetadataInventory.js";
4
- import { metadataRowOffset, readManagedMetadataLayout, } from "./ManagedMetadataLayout.js";
5
- import { MetadataRowCursor, metadataToken, readMetadataBlob, readMetadataString, sha256Bytes, } from "./ManagedMetadataHeaps.js";
4
+ import { readManagedMetadataLayout } from "./ManagedMetadataLayout.js";
6
5
  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
- };
6
+ import { emptyPage, page, parseTypes, typeRanges, } from "./ManagedMemberInspectorCore.js";
7
+ import { edges, parseFields, parseMemberRefs, parseMethods, } from "./ManagedMemberRows.js";
774
8
  const unavailable = (target, bytes, limits, issue) => managedMemberInspectionSchema.parse({
775
9
  schema_version: 1,
776
10
  artifact: {
@@ -816,6 +50,36 @@ const resourceDirectory = (pe) => {
816
50
  size: pe.cli.resources.size,
817
51
  };
818
52
  };
53
+ const collectMethodBodyIssues = (methods, limits) => methods.methods.flatMap((method) => method.body.status === "partial"
54
+ ? [
55
+ {
56
+ code: "limit-exceeded",
57
+ scope: `method.${method.token}.body.instructions`,
58
+ offset: method.body.file_offset,
59
+ detail: method.body.issue ??
60
+ `Instruction decode reached max_method_instructions ${String(limits.maxMethodInstructions)}`,
61
+ },
62
+ ]
63
+ : []);
64
+ const readMemberInventory = (bytes, pe, limits) => {
65
+ const cli = pe.cli;
66
+ if (cli === null)
67
+ throw new TypeError("Managed inventory requires CLI metadata");
68
+ const rootOffset = pe.rvaToOffset(cli.metadata.rva, cli.metadata.size, "cli.metadata");
69
+ if (cli.metadata.size > limits.maxMetadataBytes)
70
+ throw managedFailure("limit-exceeded", "metadata.root", `CLI metadata size exceeds max_metadata_bytes ${String(limits.maxMetadataBytes)}`, rootOffset);
71
+ const layout = readManagedMetadataLayout(bytes, rootOffset, cli.metadata.size, limits.maxTableRows);
72
+ const inventory = readManagedMetadataInventory(bytes, layout, {
73
+ referenceOffset: 0,
74
+ referenceLimit: 1,
75
+ resourceOffset: 0,
76
+ resourceLimit: 1,
77
+ attributeOffset: 0,
78
+ attributeLimit: 1,
79
+ maxHeapItemBytes: limits.maxHeapItemBytes,
80
+ }, resourceDirectory(pe));
81
+ return { layout, inventory };
82
+ };
819
83
  /** Inspect metadata members and method bodies without loading target code. */
820
84
  export const inspectManagedMembersBytes = (bytes, target, limits) => {
821
85
  const pe = readManagedPeLayout(bytes);
@@ -823,25 +87,22 @@ export const inspectManagedMembersBytes = (bytes, target, limits) => {
823
87
  return unavailable(target, bytes, limits, pe.cliIssue);
824
88
  const issues = [];
825
89
  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));
90
+ const { layout, inventory } = readMemberInventory(bytes, pe, limits);
839
91
  issues.push(...inventory.issues);
840
92
  const ranges = typeRanges(bytes, layout, limits.maxHeapItemBytes);
841
93
  const types = parseTypes(bytes, layout, ranges, limits.maxHeapItemBytes);
842
94
  const fields = parseFields(bytes, layout, ranges, limits.maxHeapItemBytes);
843
95
  const memberRefs = parseMemberRefs(bytes, layout, limits.maxHeapItemBytes);
844
- const methods = parseMethods(bytes, layout, pe, ranges, limits.maxHeapItemBytes, limits);
96
+ const methods = parseMethods({
97
+ bytes,
98
+ layout,
99
+ pe,
100
+ ranges,
101
+ maxBytes: limits.maxHeapItemBytes,
102
+ limits,
103
+ });
104
+ const methodBodyIssues = collectMethodBodyIssues(methods, limits);
105
+ const coverageIssues = [...issues, ...methodBodyIssues];
845
106
  const related = edges(methods.methods, methods.core, fields.core, memberRefs.core);
846
107
  return managedMemberInspectionSchema.parse({
847
108
  schema_version: 1,
@@ -869,13 +130,18 @@ export const inspectManagedMembersBytes = (bytes, target, limits) => {
869
130
  call_edges: page(related.callEdges, limits.edgeOffset, limits.edgeLimit),
870
131
  field_accesses: page(related.fieldAccesses, limits.edgeOffset, limits.edgeLimit),
871
132
  coverage: {
872
- state: issues.length === 0 ? "complete" : "partial",
873
- issues,
133
+ state: coverageIssues.length === 0 ? "complete" : "partial",
134
+ issues: coverageIssues,
874
135
  },
875
136
  limitations: [
876
137
  "Metadata tokens are build-local coordinates and are only meaningful with the reported artifact SHA-256 and MVID.",
877
138
  "CIL instruction anchors are decoded from file-backed method bodies only; no target assembly is loaded or executed.",
878
139
  "Signatures are decoded for common ECMA-335 primitive, class, valuetype, pointer, byref, array, and generic variable forms; unsupported forms retain raw signature hashes.",
140
+ ...(methodBodyIssues.length > 0
141
+ ? [
142
+ "At least one CIL method body reached max_method_instructions; its decoded prefix is partial and has no normalized CIL identity.",
143
+ ]
144
+ : []),
879
145
  ],
880
146
  });
881
147
  }