rea-agents 1.7.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 (156) hide show
  1. package/README.md +38 -31
  2. package/dist/application/CapabilityInventory.js +18 -0
  3. package/dist/application/CrossVersionInvestigation.js +10 -6
  4. package/dist/application/Doctor.js +56 -6
  5. package/dist/application/ElectronBoundaryAnalysis.js +96 -0
  6. package/dist/application/ElectronBoundaryGraph.js +9 -0
  7. package/dist/application/ElectronBoundaryGraphContext.js +62 -0
  8. package/dist/application/ElectronBoundaryGraphIpc.js +242 -0
  9. package/dist/application/ElectronBoundaryGraphNative.js +86 -0
  10. package/dist/application/ElectronBoundaryGraphWindows.js +199 -0
  11. package/dist/application/EnhancedTools.js +38 -13
  12. package/dist/application/InvestigationProviders.js +30 -0
  13. package/dist/application/JavaScriptApplicationEvidence.js +26 -0
  14. package/dist/application/JavaScriptApplicationEvidenceGraph.js +89 -0
  15. package/dist/application/JavaScriptApplicationService.js +74 -0
  16. package/dist/application/JavaScriptApplicationWorkflowEvidence.js +29 -0
  17. package/dist/application/JavaScriptApplicationWorkflowService.js +90 -0
  18. package/dist/application/JavaScriptArtifactAnalysis.js +7 -1
  19. package/dist/application/JavaScriptArtifactGraphBuilder.js +59 -16
  20. package/dist/application/JavaScriptArtifactGraphEvidence.js +24 -14
  21. package/dist/application/JavaScriptArtifactReconstruction.js +3 -0
  22. package/dist/application/JavaScriptArtifactReconstructionInput.js +2 -83
  23. package/dist/application/JavaScriptReplayPlanning.js +201 -0
  24. package/dist/application/JavaScriptReplayService.js +266 -0
  25. package/dist/application/JavaScriptRuntimeReconciliationEvidence.js +25 -0
  26. package/dist/application/JavaScriptRuntimeReconciliationService.js +27 -0
  27. package/dist/application/ManagedApplicationGraphService.js +55 -0
  28. package/dist/application/ManagedMemberComparisonService.js +122 -0
  29. package/dist/application/ManagedNativeVerificationService.js +39 -0
  30. package/dist/application/ManagedReconstructionService.js +58 -0
  31. package/dist/application/ManagedRuntimeCorrelationService.js +103 -0
  32. package/dist/application/PermissionAuthority.js +1 -1
  33. package/dist/application/ProcessSampling.js +15 -11
  34. package/dist/application/ProjectPermissionStore.js +1 -0
  35. package/dist/application/runtime.js +2 -0
  36. package/dist/browser/CdpCaptureEvents.js +49 -0
  37. package/dist/browser/CdpElectronInspection.js +106 -162
  38. package/dist/browser/CdpElectronProvider.js +5 -1
  39. package/dist/browser/CdpElectronScriptEvents.js +69 -0
  40. package/dist/browser/CdpElectronScripts.js +94 -0
  41. package/dist/browser/CdpElectronWorkers.js +56 -0
  42. package/dist/browser/CdpPageCapture.js +8 -4
  43. package/dist/catalogIdentity.js +4 -1
  44. package/dist/cli.js +388 -0
  45. package/dist/cliApplicationCommands.js +62 -0
  46. package/dist/cliCommandNames.js +13 -0
  47. package/dist/cliElectronCommands.js +103 -1
  48. package/dist/cliJsonInput.js +54 -0
  49. package/dist/config.js +83 -0
  50. package/dist/contracts/applicationToolContracts.js +91 -0
  51. package/dist/contracts/artifactToolContracts.js +5 -9
  52. package/dist/contracts/browserToolContracts.js +14 -62
  53. package/dist/contracts/electronToolContracts.js +45 -19
  54. package/dist/contracts/errorSchemas.js +1 -0
  55. package/dist/contracts/investigationExamples.js +0 -6
  56. package/dist/contracts/javascriptApplicationWorkflowExamples.js +46 -0
  57. package/dist/contracts/javascriptRuntimeReconciliationExample.js +208 -0
  58. package/dist/contracts/managedToolContracts.js +153 -0
  59. package/dist/contracts/managedWorkflowExamples.js +346 -0
  60. package/dist/contracts/managedWorkflowToolContracts.js +203 -0
  61. package/dist/contracts/nativeToolContracts.js +2 -6
  62. package/dist/contracts/promptContracts.js +18 -2
  63. package/dist/contracts/toolContractExamples.js +10 -25
  64. package/dist/contracts/toolContracts.js +47 -45
  65. package/dist/contracts/toolEffects.js +160 -0
  66. package/dist/contracts/toolOutputSchemas.js +46 -31
  67. package/dist/doctorRuntime.js +42 -1
  68. package/dist/domain/artifactComparison.js +3 -4
  69. package/dist/domain/browserObservation.js +3 -0
  70. package/dist/domain/bundleComparison.js +4 -4
  71. package/dist/domain/electronObservation.js +13 -0
  72. package/dist/domain/electronStaticAnalysis.js +9 -0
  73. package/dist/domain/electronStaticAnalysisBrowser.js +194 -0
  74. package/dist/domain/electronStaticAnalysisIpc.js +171 -0
  75. package/dist/domain/electronStaticAnalysisNative.js +164 -0
  76. package/dist/domain/electronStaticAnalysisTypes.js +1 -0
  77. package/dist/domain/electronStaticAnalysisValues.js +103 -0
  78. package/dist/domain/errors.js +67 -1
  79. package/dist/domain/functionComparisonSchemas.js +3 -8
  80. package/dist/domain/inputIssueProjection.js +52 -0
  81. package/dist/domain/javascriptApplicationAnalysis.js +155 -0
  82. package/dist/domain/javascriptApplicationChangeGraph.js +231 -0
  83. package/dist/domain/javascriptApplicationEvidenceSchemas.js +18 -0
  84. package/dist/domain/javascriptApplicationGraph.js +2 -1
  85. package/dist/domain/javascriptApplicationGraphSchemas.js +7 -0
  86. package/dist/domain/javascriptApplicationVersionComparison.js +116 -0
  87. package/dist/domain/javascriptApplicationVersionComparisonSchemas.js +139 -0
  88. package/dist/domain/javascriptApplicationVersionItems.js +221 -0
  89. package/dist/domain/javascriptApplicationVersionKeys.js +170 -0
  90. package/dist/domain/javascriptFeatureSeed.js +140 -0
  91. package/dist/domain/javascriptFeatureTrace.js +232 -0
  92. package/dist/domain/javascriptFeatureTraceSchemas.js +136 -0
  93. package/dist/domain/javascriptFeatureTraversal.js +88 -0
  94. package/dist/domain/javascriptNativeHandoff.js +85 -0
  95. package/dist/domain/javascriptReplay.js +361 -0
  96. package/dist/domain/javascriptRuntimeLoadState.js +106 -0
  97. package/dist/domain/javascriptRuntimeReconciliation.js +42 -0
  98. package/dist/domain/javascriptRuntimeReconciliationGraph.js +169 -0
  99. package/dist/domain/javascriptRuntimeReconciliationMatching.js +247 -0
  100. package/dist/domain/javascriptRuntimeReconciliationParsing.js +160 -0
  101. package/dist/domain/javascriptRuntimeReconciliationResult.js +119 -0
  102. package/dist/domain/javascriptRuntimeReconciliationRuntime.js +316 -0
  103. package/dist/domain/javascriptRuntimeReconciliationSchemas.js +245 -0
  104. package/dist/domain/javascriptRuntimeStaticCandidates.js +191 -0
  105. package/dist/domain/javascriptStaticAnalysis.js +31 -43
  106. package/dist/domain/javascriptStaticAnalysisFindings.js +39 -0
  107. package/dist/domain/javascriptStaticAnalysisHelpers.js +10 -0
  108. package/dist/domain/javascriptStaticAnalysisState.js +6 -0
  109. package/dist/domain/managedApplicationGraph.js +556 -0
  110. package/dist/domain/managedArtifact.js +444 -0
  111. package/dist/domain/managedMemberComparison.js +565 -0
  112. package/dist/domain/managedNativeVerification.js +408 -0
  113. package/dist/domain/managedReconstruction.js +183 -0
  114. package/dist/domain/managedRuntimeCorrelation.js +175 -0
  115. package/dist/domain/reconstructionVerificationSchemas.js +1 -3
  116. package/dist/dotnet/ManagedArtifactInspector.js +318 -0
  117. package/dist/dotnet/ManagedMemberInspector.js +887 -0
  118. package/dist/dotnet/ManagedMetadataHeaps.js +131 -0
  119. package/dist/dotnet/ManagedMetadataInventory.js +368 -0
  120. package/dist/dotnet/ManagedMetadataLayout.js +322 -0
  121. package/dist/dotnet/ManagedNativeBoundaryInspector.js +391 -0
  122. package/dist/dotnet/ManagedPeReader.js +157 -0
  123. package/dist/dotnet/ManagedReaderFailure.js +10 -0
  124. package/dist/dotnet/ManagedStaticProvider.js +203 -0
  125. package/dist/generatedPackageMetadata.js +2 -2
  126. package/dist/ghidra/GhidraProvider.js +6 -0
  127. package/dist/main.js +14 -0
  128. package/dist/replay/JavaScriptReplayWorker.js +286 -0
  129. package/dist/replay/LinuxJavaScriptReplayRunner.js +434 -0
  130. package/dist/replay/LinuxRuntimeClosure.js +46 -0
  131. package/dist/replay/LinuxSeccompPolicy.js +40 -0
  132. package/dist/replay/ReplayWorkerProtocol.js +55 -0
  133. package/dist/replay/SystemJavaScriptReplayHost.js +182 -0
  134. package/dist/server/createServer.js +52 -0
  135. package/dist/server/registerApplicationTools.js +115 -0
  136. package/dist/server/registerArtifactComparisonTool.js +16 -9
  137. package/dist/server/registerBrowserTools.js +33 -8
  138. package/dist/server/registerBundleComparisonTool.js +17 -20
  139. package/dist/server/registerElectronTools.js +45 -3
  140. package/dist/server/registerEnhancedTools.js +58 -13
  141. package/dist/server/registerEvidenceTools.js +5 -1
  142. package/dist/server/registerFunctionComparisonTool.js +14 -7
  143. package/dist/server/registerInvestigationTools.js +21 -17
  144. package/dist/server/registerManagedTools.js +6 -0
  145. package/dist/server/registerManagedWorkflowTools.js +277 -0
  146. package/dist/server/registerOfficialTools.js +8 -8
  147. package/dist/server/registerProcessComparisonTool.js +25 -47
  148. package/dist/server/registerSessionStatusTool.js +7 -7
  149. package/dist/server/registerSessionTools.js +51 -86
  150. package/dist/server/sessionEvidence.js +14 -19
  151. package/dist/server/toolInputValidation.js +77 -0
  152. package/dist/server/toolRegistrationOptions.js +79 -2
  153. package/dist/server/toolResult.js +25 -17
  154. package/package.json +12 -4
  155. package/scripts/rea.mjs +1 -1
  156. package/skills/rea-analysis/SKILL.md +59 -3
@@ -0,0 +1,391 @@
1
+ import { managedNativeBoundaryInspectionSchema, } from "../domain/managedArtifact.js";
2
+ import { metadataRowOffset, readManagedMetadataLayout, } from "./ManagedMetadataLayout.js";
3
+ import { MetadataRowCursor, metadataToken, readMetadataString, } from "./ManagedMetadataHeaps.js";
4
+ import { readManagedMetadataInventory, managedTableRowCounts, } from "./ManagedMetadataInventory.js";
5
+ import { readManagedPeLayout, } from "./ManagedPeReader.js";
6
+ import { ManagedReaderFailure, managedFailure, } from "./ManagedReaderFailure.js";
7
+ const rowCursor = (bytes, layout, table, row) => {
8
+ const descriptor = layout.table(table);
9
+ const start = metadataRowOffset(layout, table, row);
10
+ if (descriptor === undefined)
11
+ throw managedFailure("invalid-row", `metadata.${String(table)}`, "Metadata table is absent", start);
12
+ return new MetadataRowCursor(bytes, start, start + descriptor.rowSize, `metadata.${descriptor.name}`);
13
+ };
14
+ const codedToken = (raw, tagBits, tables) => {
15
+ const tagMask = (1 << tagBits) - 1;
16
+ const table = tables[raw & tagMask];
17
+ const row = raw >> tagBits;
18
+ if (table === undefined || row === 0)
19
+ return null;
20
+ return metadataToken(table, row);
21
+ };
22
+ const flagsHex = (value) => `0x${value.toString(16).padStart(4, "0")}`;
23
+ const page = (items, offset, limit) => {
24
+ const safeOffset = Math.min(offset, items.length);
25
+ const selected = items.slice(safeOffset, safeOffset + limit);
26
+ return {
27
+ items: selected,
28
+ offset,
29
+ limit,
30
+ total: items.length,
31
+ returned: selected.length,
32
+ dropped: Math.max(0, items.length - safeOffset - selected.length),
33
+ complete: safeOffset === 0 && selected.length === items.length,
34
+ };
35
+ };
36
+ const parseModuleRefs = (bytes, layout, maxBytes) => {
37
+ const refs = [];
38
+ const table = layout.table(26);
39
+ for (let row = 1; row <= (table?.rowCount ?? 0); row += 1) {
40
+ const cursor = rowCursor(bytes, layout, 26, row);
41
+ refs.push({
42
+ token: metadataToken(26, row),
43
+ row_offset: cursor.start,
44
+ name: readMetadataString(bytes, layout, cursor.readIndex(layout.stringIndexSize), maxBytes),
45
+ });
46
+ }
47
+ return refs;
48
+ };
49
+ const parseFields = (bytes, layout, maxBytes) => {
50
+ const fields = new Map();
51
+ const table = layout.table(4);
52
+ for (let row = 1; row <= (table?.rowCount ?? 0); row += 1) {
53
+ const cursor = rowCursor(bytes, layout, 4, row);
54
+ const flags = cursor.readUInt16();
55
+ const name = readMetadataString(bytes, layout, cursor.readIndex(layout.stringIndexSize), maxBytes);
56
+ cursor.readIndex(layout.blobIndexSize);
57
+ const token = metadataToken(4, row);
58
+ fields.set(token, {
59
+ token,
60
+ rowOffset: cursor.start,
61
+ kind: "field",
62
+ name,
63
+ flags,
64
+ implFlags: null,
65
+ rva: null,
66
+ });
67
+ }
68
+ return fields;
69
+ };
70
+ const parseMethods = (bytes, layout, maxBytes) => {
71
+ const methods = new Map();
72
+ const table = layout.table(6);
73
+ for (let row = 1; row <= (table?.rowCount ?? 0); row += 1) {
74
+ const cursor = rowCursor(bytes, layout, 6, row);
75
+ const rva = cursor.readUInt32();
76
+ const implFlags = cursor.readUInt16();
77
+ const flags = cursor.readUInt16();
78
+ const name = readMetadataString(bytes, layout, cursor.readIndex(layout.stringIndexSize), maxBytes);
79
+ cursor.readIndex(layout.blobIndexSize);
80
+ cursor.readIndex(layout.tableIndexSize(8));
81
+ const token = metadataToken(6, row);
82
+ methods.set(token, {
83
+ token,
84
+ rowOffset: cursor.start,
85
+ kind: "method",
86
+ name,
87
+ flags,
88
+ implFlags,
89
+ rva,
90
+ });
91
+ }
92
+ return methods;
93
+ };
94
+ const parseImplMaps = (bytes, layout, maxBytes, modules, members) => {
95
+ const moduleNames = new Map(modules.map((module) => [module.token, module]));
96
+ const imports = [];
97
+ const table = layout.table(28);
98
+ for (let row = 1; row <= (table?.rowCount ?? 0); row += 1) {
99
+ const cursor = rowCursor(bytes, layout, 28, row);
100
+ const mappingFlags = cursor.readUInt16();
101
+ const memberToken = codedToken(cursor.readIndex(layout.codedIndexSize("MemberForwarded")), 1, [4, 6]);
102
+ const importName = readMetadataString(bytes, layout, cursor.readIndex(layout.stringIndexSize), maxBytes);
103
+ const importScopeRow = cursor.readIndex(layout.tableIndexSize(26));
104
+ const importScopeToken = importScopeRow === 0 ? null : metadataToken(26, importScopeRow);
105
+ const member = memberToken === null ? undefined : members.get(memberToken);
106
+ imports.push({
107
+ token: metadataToken(28, row),
108
+ row_offset: cursor.start,
109
+ mapping_flags: mappingFlags,
110
+ mapping_flags_hex: flagsHex(mappingFlags),
111
+ member_token: memberToken,
112
+ member_kind: member?.kind ?? "unknown",
113
+ member_name: member?.name ?? null,
114
+ import_name: importName,
115
+ import_scope_token: importScopeToken,
116
+ import_scope_name: importScopeToken === null
117
+ ? null
118
+ : (moduleNames.get(importScopeToken)?.name ?? null),
119
+ no_mangle: (mappingFlags & 0x0001) !== 0,
120
+ char_set: charSet(mappingFlags),
121
+ call_convention: callConvention(mappingFlags),
122
+ supports_last_error: (mappingFlags & 0x0040) !== 0,
123
+ best_fit: bestFit(mappingFlags),
124
+ throw_on_unmappable_char: throwOnUnmappable(mappingFlags),
125
+ verification: "managed-declaration-only",
126
+ });
127
+ }
128
+ return imports;
129
+ };
130
+ const charSet = (flags) => {
131
+ switch (flags & 0x0006) {
132
+ case 0x0000:
133
+ return "not-specified";
134
+ case 0x0002:
135
+ return "ansi";
136
+ case 0x0004:
137
+ return "unicode";
138
+ case 0x0006:
139
+ return "auto";
140
+ default:
141
+ return "unknown";
142
+ }
143
+ };
144
+ const callConvention = (flags) => {
145
+ switch (flags & 0x0700) {
146
+ case 0x0000:
147
+ return "not-specified";
148
+ case 0x0100:
149
+ return "winapi";
150
+ case 0x0200:
151
+ return "cdecl";
152
+ case 0x0300:
153
+ return "stdcall";
154
+ case 0x0400:
155
+ return "thiscall";
156
+ case 0x0500:
157
+ return "fastcall";
158
+ default:
159
+ return "unknown";
160
+ }
161
+ };
162
+ const bestFit = (flags) => {
163
+ switch (flags & 0x0030) {
164
+ case 0x0000:
165
+ return "assembly-default";
166
+ case 0x0010:
167
+ return "enabled";
168
+ case 0x0020:
169
+ return "disabled";
170
+ default:
171
+ return "unknown";
172
+ }
173
+ };
174
+ const throwOnUnmappable = (flags) => {
175
+ switch (flags & 0x3000) {
176
+ case 0x0000:
177
+ return "assembly-default";
178
+ case 0x1000:
179
+ return "enabled";
180
+ case 0x2000:
181
+ return "disabled";
182
+ default:
183
+ return "unknown";
184
+ }
185
+ };
186
+ const nativeImplementations = (methods, pinvokeTokens) => {
187
+ const implementations = [];
188
+ for (const method of methods) {
189
+ const implFlags = method.implFlags ?? 0;
190
+ const codeType = codeTypeFor(implFlags);
191
+ const managedKind = managedKindFor(implFlags);
192
+ const pinvokeDeclared = pinvokeTokens.has(method.token) || (method.flags & 0x2000) !== 0;
193
+ if (!pinvokeDeclared && codeType === "il" && managedKind === "managed")
194
+ continue;
195
+ implementations.push({
196
+ token: method.token,
197
+ row_offset: method.rowOffset,
198
+ name: method.name,
199
+ rva: method.rva ?? 0,
200
+ flags: method.flags,
201
+ impl_flags: implFlags,
202
+ code_type: codeType,
203
+ managed_kind: managedKind,
204
+ pinvoke_declared: pinvokeDeclared,
205
+ boundary_kind: boundaryKind(pinvokeDeclared, codeType, managedKind),
206
+ body_interpretation: codeType === "il" && managedKind === "managed" && !pinvokeDeclared
207
+ ? "managed-cil"
208
+ : method.rva === 0
209
+ ? "not-file-backed"
210
+ : "native-or-runtime",
211
+ });
212
+ }
213
+ return implementations;
214
+ };
215
+ const codeTypeFor = (implFlags) => {
216
+ switch (implFlags & 0x0003) {
217
+ case 0x0000:
218
+ return "il";
219
+ case 0x0001:
220
+ return "native";
221
+ case 0x0002:
222
+ return "optil";
223
+ case 0x0003:
224
+ return "runtime";
225
+ default:
226
+ return "unknown";
227
+ }
228
+ };
229
+ const managedKindFor = (implFlags) => {
230
+ switch (implFlags & 0x0004) {
231
+ case 0x0000:
232
+ return "managed";
233
+ case 0x0004:
234
+ return "unmanaged";
235
+ default:
236
+ return "unknown";
237
+ }
238
+ };
239
+ const boundaryKind = (pinvokeDeclared, codeType, managedKind) => {
240
+ if (pinvokeDeclared)
241
+ return "pinvoke";
242
+ if (codeType === "native")
243
+ return "native-body";
244
+ if (codeType === "runtime")
245
+ return "runtime-provided";
246
+ if (managedKind === "unmanaged")
247
+ return "unmanaged-method";
248
+ return "mixed-or-unknown";
249
+ };
250
+ const cliNative = (pe) => {
251
+ if (pe.cli === null)
252
+ return {
253
+ il_only: false,
254
+ requires_32bit: false,
255
+ strong_name_signed: false,
256
+ native_entry_point: false,
257
+ ready_to_run_signature: false,
258
+ managed_native_header_rva: 0,
259
+ managed_native_header_size: 0,
260
+ };
261
+ return {
262
+ il_only: (pe.cli.flags & 0x0000_0001) !== 0,
263
+ requires_32bit: (pe.cli.flags & 0x0000_0002) !== 0,
264
+ strong_name_signed: (pe.cli.flags & 0x0000_0008) !== 0,
265
+ native_entry_point: (pe.cli.flags & 0x0000_0010) !== 0,
266
+ ready_to_run_signature: pe.cli.readyToRunSignature,
267
+ managed_native_header_rva: pe.cli.managedNativeHeader.rva,
268
+ managed_native_header_size: pe.cli.managedNativeHeader.size,
269
+ };
270
+ };
271
+ const unavailable = (target, bytes, limits, issue) => managedNativeBoundaryInspectionSchema.parse({
272
+ schema_version: 1,
273
+ artifact: {
274
+ path: target.path,
275
+ sha256: target.sha256,
276
+ byte_length: bytes.length,
277
+ format: "pe",
278
+ },
279
+ module: null,
280
+ metadata: { status: "absent", version: null, table_row_counts: {} },
281
+ identity_scope: {
282
+ token_identity: "build-local",
283
+ requires_artifact_sha256: target.sha256,
284
+ requires_mvid: null,
285
+ },
286
+ cli_native: {
287
+ il_only: false,
288
+ requires_32bit: false,
289
+ strong_name_signed: false,
290
+ native_entry_point: false,
291
+ ready_to_run_signature: false,
292
+ managed_native_header_rva: 0,
293
+ managed_native_header_size: 0,
294
+ },
295
+ module_refs: page([], limits.moduleRefOffset, limits.moduleRefLimit),
296
+ pinvoke_imports: page([], limits.importOffset, limits.importLimit),
297
+ native_implementations: page([], limits.implementationOffset, limits.implementationLimit),
298
+ summary: {
299
+ module_ref_count: 0,
300
+ pinvoke_import_count: 0,
301
+ native_implementation_count: 0,
302
+ ready_to_run: false,
303
+ mixed_mode_or_native_header: false,
304
+ },
305
+ coverage: {
306
+ state: "unavailable",
307
+ issues: issue === null ? [] : [issue],
308
+ },
309
+ limitations: [
310
+ "The PE has no admitted CLI metadata; managed/native boundary inspection is unavailable.",
311
+ ],
312
+ });
313
+ /** Inspect PE/CLI managed/native boundary declarations without executing code. */
314
+ export const inspectManagedNativeBoundariesBytes = (bytes, target, limits) => {
315
+ const pe = readManagedPeLayout(bytes);
316
+ if (pe.cli === null)
317
+ return unavailable(target, bytes, limits, pe.cliIssue);
318
+ const issues = [];
319
+ try {
320
+ const rootOffset = pe.rvaToOffset(pe.cli.metadata.rva, pe.cli.metadata.size, "cli.metadata");
321
+ if (pe.cli.metadata.size > limits.maxMetadataBytes)
322
+ throw managedFailure("limit-exceeded", "metadata.root", `CLI metadata size exceeds max_metadata_bytes ${String(limits.maxMetadataBytes)}`, rootOffset);
323
+ const layout = readManagedMetadataLayout(bytes, rootOffset, pe.cli.metadata.size, limits.maxTableRows);
324
+ const inventory = readManagedMetadataInventory(bytes, layout, {
325
+ referenceOffset: 0,
326
+ referenceLimit: 1,
327
+ resourceOffset: 0,
328
+ resourceLimit: 1,
329
+ attributeOffset: 0,
330
+ attributeLimit: 1,
331
+ maxHeapItemBytes: limits.maxHeapItemBytes,
332
+ }, null);
333
+ issues.push(...inventory.issues);
334
+ const moduleRefs = parseModuleRefs(bytes, layout, limits.maxHeapItemBytes);
335
+ const fields = parseFields(bytes, layout, limits.maxHeapItemBytes);
336
+ const methods = parseMethods(bytes, layout, limits.maxHeapItemBytes);
337
+ const members = new Map([...fields, ...methods]);
338
+ const imports = parseImplMaps(bytes, layout, limits.maxHeapItemBytes, moduleRefs, members);
339
+ const implementations = nativeImplementations(methods.values(), new Set(imports
340
+ .map(({ member_token: token }) => token)
341
+ .filter((token) => token !== null)));
342
+ const native = cliNative(pe);
343
+ return managedNativeBoundaryInspectionSchema.parse({
344
+ schema_version: 1,
345
+ artifact: {
346
+ path: target.path,
347
+ sha256: target.sha256,
348
+ byte_length: bytes.length,
349
+ format: "pe",
350
+ },
351
+ module: inventory.module,
352
+ metadata: {
353
+ status: issues.length === 0 ? "complete" : "partial",
354
+ version: layout.version,
355
+ table_row_counts: managedTableRowCounts(layout),
356
+ },
357
+ identity_scope: {
358
+ token_identity: "build-local",
359
+ requires_artifact_sha256: target.sha256,
360
+ requires_mvid: inventory.module?.mvid ?? null,
361
+ },
362
+ cli_native: native,
363
+ module_refs: page(moduleRefs, limits.moduleRefOffset, limits.moduleRefLimit),
364
+ pinvoke_imports: page(imports, limits.importOffset, limits.importLimit),
365
+ native_implementations: page(implementations, limits.implementationOffset, limits.implementationLimit),
366
+ summary: {
367
+ module_ref_count: moduleRefs.length,
368
+ pinvoke_import_count: imports.length,
369
+ native_implementation_count: implementations.length,
370
+ ready_to_run: native.ready_to_run_signature,
371
+ mixed_mode_or_native_header: native.managed_native_header_rva !== 0 ||
372
+ native.managed_native_header_size !== 0 ||
373
+ native.native_entry_point,
374
+ },
375
+ coverage: {
376
+ state: issues.length === 0 ? "complete" : "partial",
377
+ issues,
378
+ },
379
+ limitations: [
380
+ "P/Invoke rows prove managed import declarations only; this inspection does not verify that a native library, export, thunk, or provider-qualified function exists.",
381
+ "Managed metadata tokens are build-local coordinates and are only meaningful with the reported artifact SHA-256 and MVID.",
382
+ "ReadyToRun, NativeAOT, C++/CLI, and IL2CPP native semantics require separately selected native-provider evidence; this tool does not translate managed tokens into native addresses.",
383
+ ],
384
+ });
385
+ }
386
+ catch (cause) {
387
+ if (cause instanceof ManagedReaderFailure)
388
+ return unavailable(target, bytes, limits, cause.issue);
389
+ throw cause;
390
+ }
391
+ };
@@ -0,0 +1,157 @@
1
+ import { managedFailure, ManagedReaderFailure, } from "./ManagedReaderFailure.js";
2
+ const requireRange = (bytes, offset, length, scope) => {
3
+ if (!Number.isSafeInteger(offset) ||
4
+ !Number.isSafeInteger(length) ||
5
+ offset < 0 ||
6
+ length < 0 ||
7
+ offset > bytes.length - length)
8
+ throw managedFailure("invalid-directory", scope, `${scope} leaves the artifact byte range`, Number.isSafeInteger(offset) && offset >= 0 ? offset : null);
9
+ };
10
+ const add = (left, right, scope) => {
11
+ const result = left + right;
12
+ if (!Number.isSafeInteger(result) || result < left)
13
+ throw managedFailure("invalid-directory", scope, `${scope} offset arithmetic overflowed`, left);
14
+ return result;
15
+ };
16
+ const architectureFor = (machine) => {
17
+ switch (machine) {
18
+ case 0x014c:
19
+ return "x86";
20
+ case 0x8664:
21
+ return "x86_64";
22
+ case 0x01c0:
23
+ return "arm";
24
+ case 0xaa64:
25
+ return "arm64";
26
+ default:
27
+ throw managedFailure("invalid-directory", "pe.machine", `Unsupported PE machine 0x${machine.toString(16).padStart(4, "0")}`);
28
+ }
29
+ };
30
+ const mapRva = (bytes, sections, rva, size, scope) => {
31
+ if (rva === 0 && size === 0)
32
+ return 0;
33
+ for (const section of sections) {
34
+ const mappedSize = Math.max(section.virtualSize, section.rawSize);
35
+ if (rva < section.virtualAddress)
36
+ continue;
37
+ const within = rva - section.virtualAddress;
38
+ if (within > mappedSize || size > mappedSize - within)
39
+ continue;
40
+ if (within > section.rawSize || size > section.rawSize - within)
41
+ throw managedFailure("invalid-directory", scope, `${scope} occupies virtual bytes without file-backed data`);
42
+ const offset = add(section.rawOffset, within, scope);
43
+ requireRange(bytes, offset, size, scope);
44
+ return offset;
45
+ }
46
+ throw managedFailure("invalid-directory", scope, `${scope} RVA is not covered by a PE section`);
47
+ };
48
+ const readDirectory = (bytes, offset) => {
49
+ requireRange(bytes, offset, 8, "pe.data-directory");
50
+ return {
51
+ rva: bytes.readUInt32LE(offset),
52
+ size: bytes.readUInt32LE(offset + 4),
53
+ };
54
+ };
55
+ const readCliHeader = (bytes, sections, directory) => {
56
+ if (directory.size < 72)
57
+ throw managedFailure("invalid-cli-header", "cli.header", "CLI header is shorter than the required 72 bytes");
58
+ const offset = mapRva(bytes, sections, directory.rva, 72, "cli.header");
59
+ const headerSize = bytes.readUInt32LE(offset);
60
+ if (headerSize < 72 || headerSize > directory.size)
61
+ throw managedFailure("invalid-cli-header", "cli.header", "CLI header size is outside its PE data directory", offset);
62
+ const metadata = readDirectory(bytes, offset + 8);
63
+ if (metadata.rva === 0 || metadata.size === 0)
64
+ throw managedFailure("invalid-cli-header", "cli.metadata", "CLI metadata directory is absent", offset + 8);
65
+ const flags = bytes.readUInt32LE(offset + 16);
66
+ const managedNativeHeader = readDirectory(bytes, offset + 64);
67
+ let readyToRunSignature = false;
68
+ if (managedNativeHeader.rva !== 0 && managedNativeHeader.size >= 4) {
69
+ const nativeOffset = mapRva(bytes, sections, managedNativeHeader.rva, 4, "cli.managed-native-header");
70
+ readyToRunSignature = bytes.readUInt32LE(nativeOffset) === 0x0052_5452;
71
+ }
72
+ return {
73
+ headerOffset: offset,
74
+ headerSize,
75
+ runtimeMajor: bytes.readUInt16LE(offset + 4),
76
+ runtimeMinor: bytes.readUInt16LE(offset + 6),
77
+ metadata,
78
+ flags,
79
+ entryPoint: bytes.readUInt32LE(offset + 20),
80
+ resources: readDirectory(bytes, offset + 24),
81
+ strongName: readDirectory(bytes, offset + 32),
82
+ managedNativeHeader,
83
+ readyToRunSignature,
84
+ };
85
+ };
86
+ /** Parse the file-backed PE and CLI layout without loading target code. */
87
+ export const readManagedPeLayout = (bytes) => {
88
+ requireRange(bytes, 0, 64, "pe.dos-header");
89
+ if (bytes[0] !== 0x4d || bytes[1] !== 0x5a)
90
+ throw managedFailure("invalid-directory", "pe.dos-header", "Artifact does not start with an MZ header", 0);
91
+ const peOffset = bytes.readUInt32LE(0x3c);
92
+ requireRange(bytes, peOffset, 24, "pe.coff-header");
93
+ if (bytes.readUInt32LE(peOffset) !== 0x0000_4550)
94
+ throw managedFailure("invalid-directory", "pe.signature", "PE signature is invalid", peOffset);
95
+ const coff = peOffset + 4;
96
+ const machine = bytes.readUInt16LE(coff);
97
+ const sectionCount = bytes.readUInt16LE(coff + 2);
98
+ if (sectionCount === 0 || sectionCount > 96)
99
+ throw managedFailure("invalid-directory", "pe.sections", "PE section count is outside the admitted range", coff + 2);
100
+ const optionalSize = bytes.readUInt16LE(coff + 16);
101
+ const characteristics = bytes.readUInt16LE(coff + 18);
102
+ const optionalOffset = coff + 20;
103
+ requireRange(bytes, optionalOffset, optionalSize, "pe.optional-header");
104
+ if (optionalSize < 96)
105
+ throw managedFailure("invalid-directory", "pe.optional-header", "PE optional header is too short", optionalOffset);
106
+ const magic = bytes.readUInt16LE(optionalOffset);
107
+ const optionalHeader = magic === 0x010b ? "pe32" : magic === 0x020b ? "pe32-plus" : undefined;
108
+ if (optionalHeader === undefined)
109
+ throw managedFailure("invalid-directory", "pe.optional-header", "PE optional header magic is unsupported", optionalOffset);
110
+ const directoryCountOffset = optionalOffset + (optionalHeader === "pe32" ? 92 : 108);
111
+ const directoryOffset = optionalOffset + (optionalHeader === "pe32" ? 96 : 112);
112
+ requireRange(bytes, directoryCountOffset, 4, "pe.data-directories");
113
+ const directoryCount = bytes.readUInt32LE(directoryCountOffset);
114
+ const sectionOffset = add(optionalOffset, optionalSize, "pe.sections");
115
+ requireRange(bytes, sectionOffset, sectionCount * 40, "pe.sections");
116
+ const sections = [];
117
+ for (let index = 0; index < sectionCount; index += 1) {
118
+ const offset = sectionOffset + index * 40;
119
+ const section = {
120
+ virtualSize: bytes.readUInt32LE(offset + 8),
121
+ virtualAddress: bytes.readUInt32LE(offset + 12),
122
+ rawSize: bytes.readUInt32LE(offset + 16),
123
+ rawOffset: bytes.readUInt32LE(offset + 20),
124
+ };
125
+ if (section.rawSize > 0)
126
+ requireRange(bytes, section.rawOffset, section.rawSize, "pe.section-data");
127
+ sections.push(section);
128
+ }
129
+ const cliDirectory = directoryCount <= 14 ||
130
+ optionalSize < directoryOffset - optionalOffset + 120
131
+ ? { rva: 0, size: 0 }
132
+ : readDirectory(bytes, directoryOffset + 14 * 8);
133
+ const cliDirectoryPresent = cliDirectory.rva !== 0 || cliDirectory.size !== 0;
134
+ let cli = null;
135
+ let cliIssue = null;
136
+ if (cliDirectoryPresent)
137
+ try {
138
+ cli = readCliHeader(bytes, sections, cliDirectory);
139
+ }
140
+ catch (cause) {
141
+ if (!(cause instanceof ManagedReaderFailure))
142
+ throw cause;
143
+ cliIssue = cause.issue;
144
+ }
145
+ return {
146
+ machine,
147
+ architecture: architectureFor(machine),
148
+ optionalHeader,
149
+ sectionCount,
150
+ characteristics,
151
+ sections,
152
+ cli,
153
+ cliDirectoryPresent,
154
+ cliIssue,
155
+ rvaToOffset: (rva, size, scope) => mapRva(bytes, sections, rva, size, scope),
156
+ };
157
+ };
@@ -0,0 +1,10 @@
1
+ /** Bounded format failure retained as caller-visible managed coverage. */
2
+ export class ManagedReaderFailure extends Error {
3
+ issue;
4
+ constructor(issue, options) {
5
+ super(issue.detail, options);
6
+ this.issue = issue;
7
+ }
8
+ }
9
+ /** Construct a typed managed parse failure at an exact byte location. */
10
+ export const managedFailure = (code, scope, detail, offset = null) => new ManagedReaderFailure({ code, scope, offset, detail });