rea-agents 5.0.0 → 6.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 (275) hide show
  1. package/README.md +157 -650
  2. package/bridge/mitmproxy/capture.py +210 -0
  3. package/bridge/native/rea_lldb_tracer.py +303 -0
  4. package/bridge/process/ProcessRunTokenReader.swift +263 -0
  5. package/bridge/process/ProcessRunTokenReaderMain.swift +35 -0
  6. package/bridge/pwndbg/core_context.py +101 -0
  7. package/bridge/pwndbg/launch.py +39 -0
  8. package/bridge/pwndbg/launch_impl.py +34 -0
  9. package/bridge/pwntools/decoder_runtime.py +59 -0
  10. package/bridge/pwntools/layout.py +39 -0
  11. package/bridge/pwntools/layout_impl.py +309 -0
  12. package/bridge/pwntools/recorded_crash.py +39 -0
  13. package/bridge/pwntools/recorded_crash_impl.py +143 -0
  14. package/dist/android/JadxProvider.js +1 -0
  15. package/dist/application/CapabilityInventory.js +21 -0
  16. package/dist/application/DirectAnalysis.js +97 -23
  17. package/dist/application/EvidenceBundleFiles.js +1 -1
  18. package/dist/application/ReconstructionObligationCandidates.js +5 -1
  19. package/dist/application/WebNetworkCapturePort.js +1 -0
  20. package/dist/application/WebNetworkCaptureService.js +132 -0
  21. package/dist/application/WindowsCapabilities.js +1 -1
  22. package/dist/application/binary/AnalysisSnapshotCache.js +49 -6
  23. package/dist/application/binary/BinarySession.js +38 -10
  24. package/dist/application/binary/BinarySessionOpen.js +16 -3
  25. package/dist/application/binary/BinarySessionRecords.js +20 -0
  26. package/dist/application/binaryDiagnostics/BinaryLayoutPort.js +1 -0
  27. package/dist/application/binaryDiagnostics/BinaryLayoutService.js +57 -0
  28. package/dist/application/binaryDiagnostics/RecordedCrashPort.js +1 -0
  29. package/dist/application/binaryDiagnostics/RecordedCrashService.js +60 -0
  30. package/dist/application/evm/EvmInterfacePort.js +1 -0
  31. package/dist/application/evm/EvmInterfaceService.js +62 -0
  32. package/dist/application/javascript/JavaScriptApplicationEvidence.js +1 -2
  33. package/dist/application/javascript/JavaScriptApplicationService.js +15 -2
  34. package/dist/application/javascript/JavaScriptArtifactAnalysis.js +42 -23
  35. package/dist/application/javascript/JavaScriptArtifactGraphDocuments.js +26 -2
  36. package/dist/application/javascript/JavaScriptArtifactPathResolution.js +74 -24
  37. package/dist/application/javascript/JavaScriptArtifactReconstruction.js +23 -10
  38. package/dist/application/javascript/JavaScriptSemanticGraphAsyncProjection.js +7 -7
  39. package/dist/application/javascript/JavaScriptSemanticGraphBuilder.js +145 -70
  40. package/dist/application/javascript/JavaScriptSemanticGraphChildProcessProjection.js +14 -15
  41. package/dist/application/javascript/JavaScriptSemanticGraphConstruction.js +41 -1
  42. package/dist/application/javascript/JavaScriptSemanticGraphDataProjection.js +7 -7
  43. package/dist/application/javascript/JavaScriptSemanticGraphFlowProjection.js +5 -9
  44. package/dist/application/javascript/JavaScriptSemanticGraphObjectProjection.js +3 -3
  45. package/dist/application/javascript/JavaScriptSemanticGraphProjection.js +118 -12
  46. package/dist/application/javascript/JavaScriptSemanticGraphResourceProjection.js +3 -3
  47. package/dist/application/javascript/JavaScriptSemanticGraphValueProjection.js +5 -5
  48. package/dist/application/{ProcessCli.js → process/ProcessCli.js} +10 -10
  49. package/dist/application/{ProcessEvidence.js → process/ProcessEvidence.js} +4 -4
  50. package/dist/artifacts/ArtifactProvider.js +30 -3
  51. package/dist/artifacts/ArtifactProviderMetadata.js +2 -1
  52. package/dist/artifacts/ZipArtifactReader.js +3 -0
  53. package/dist/artifacts/apple/DylibResolutionReader.js +259 -0
  54. package/dist/artifacts/apple/MachoLoadCommandReader.js +294 -0
  55. package/dist/{application → artifacts/extraction}/ArtifactExtraction.js +10 -10
  56. package/dist/{application → artifacts/inventory}/ArtifactGraphConstruction.js +3 -3
  57. package/dist/{application → artifacts/inventory}/ArtifactInventory.js +4 -4
  58. package/dist/{application/ArtifactInventory → artifacts/inventory}/classify.js +2 -2
  59. package/dist/{application/ArtifactInventory → artifacts/inventory}/reader.js +5 -5
  60. package/dist/{application/ArtifactInventory → artifacts/inventory}/scanCanonical.js +3 -3
  61. package/dist/{application/ArtifactInventory → artifacts/inventory}/scanReader.js +5 -5
  62. package/dist/artifacts/readStableArtifact.js +20 -4
  63. package/dist/browser/CdpCaptureEventHandlers.js +90 -2
  64. package/dist/browser/CdpCaptureEvents.js +9 -0
  65. package/dist/browser/PlaywrightElectronActiveProvider.js +12 -3
  66. package/dist/browser/history/CaptureFailures.js +101 -0
  67. package/dist/browser/history/CaptureFormatError.js +10 -0
  68. package/dist/browser/history/CaptureRedaction.js +65 -0
  69. package/dist/browser/history/CaptureRelease.js +12 -0
  70. package/dist/browser/history/HarCapture.js +165 -0
  71. package/dist/browser/history/HarCaptureAdapter.js +20 -0
  72. package/dist/browser/history/HarCaptureProcess.js +57 -0
  73. package/dist/browser/history/HarJson.js +91 -0
  74. package/dist/browser/history/HarSchema.js +72 -0
  75. package/dist/browser/history/HistoricalCaptureDecoder.js +186 -0
  76. package/dist/browser/history/HistoricalCaptureFormatAdapter.js +1 -0
  77. package/dist/browser/history/MitmproxyCaptureAdapter.js +42 -0
  78. package/dist/cli/artifactCommands.js +26 -1
  79. package/dist/cli/binaryDiagnosticsCommands.js +39 -0
  80. package/dist/cli/coreAnalysisCommands.js +2 -2
  81. package/dist/cli/coreBinaryCommands.js +8 -8
  82. package/dist/cli/electronCommands.js +16 -9
  83. package/dist/cli/evmCommands.js +24 -0
  84. package/dist/cli/firmwareCommands.js +6 -3
  85. package/dist/cli/javascriptApplicationAnalysis.js +16 -3
  86. package/dist/cli/jsonOutput.js +133 -0
  87. package/dist/cli/nativeCallCommands.js +35 -0
  88. package/dist/cli/processCommands.js +1 -1
  89. package/dist/cli/streamedJsonOutput.js +56 -0
  90. package/dist/cli/utilityCommands.js +2 -0
  91. package/dist/cli/webNetworkCaptureCommands.js +40 -0
  92. package/dist/cli.js +9 -3
  93. package/dist/cliBrowserScenarioCommands.js +2 -2
  94. package/dist/cliCommandNames.js +6 -0
  95. package/dist/cliJsonInput.js +30 -1
  96. package/dist/cliOutput.js +21 -6
  97. package/dist/composition/binaryDiagnostics.js +8 -0
  98. package/dist/composition/evm.js +4 -0
  99. package/dist/composition/webNetworkCaptures.js +6 -0
  100. package/dist/contracts/artifactToolContracts.js +6 -0
  101. package/dist/contracts/browserCaptureToolInputSchema.js +43 -0
  102. package/dist/contracts/browserToolContracts.js +4 -3
  103. package/dist/contracts/errorSchemas.js +8 -1
  104. package/dist/contracts/evm/evmToolContracts.js +20 -0
  105. package/dist/contracts/investigationExamples.js +2 -2
  106. package/dist/contracts/managed/managedToolContracts.js +7 -3
  107. package/dist/contracts/native/binaryDiagnosticsToolContracts.js +42 -0
  108. package/dist/contracts/native/nativeToolContracts.js +17 -1
  109. package/dist/contracts/officialToolContracts.js +12 -9
  110. package/dist/contracts/process/processCaptureExample.js +2 -0
  111. package/dist/contracts/sessionLifecycleInputs.js +22 -5
  112. package/dist/contracts/sessionStatusContract.js +10 -2
  113. package/dist/contracts/sessionToolContracts.js +8 -1
  114. package/dist/contracts/sessionToolSchemas.js +10 -3
  115. package/dist/contracts/toolContractExamples.js +1 -1
  116. package/dist/contracts/toolContracts.js +6 -0
  117. package/dist/contracts/toolEffects.js +30 -0
  118. package/dist/contracts/toolOutputSchemaGroups.js +5 -1
  119. package/dist/contracts/toolOutputSchemaPrimitives.js +1 -1
  120. package/dist/contracts/webNetworkCaptureToolContracts.js +24 -0
  121. package/dist/domain/analysisErrorBase.js +15 -0
  122. package/dist/domain/analysisErrorCore.js +59 -5
  123. package/dist/domain/analysisErrorPresentation.js +29 -1
  124. package/dist/domain/analysisErrorProjection.js +52 -2
  125. package/dist/domain/analysisSnapshot.js +166 -2
  126. package/dist/domain/apple/dyldPaths.js +81 -0
  127. package/dist/domain/apple/dylibResolution.js +367 -0
  128. package/dist/domain/apple/dylibResolutionFindings.js +75 -0
  129. package/dist/domain/artifactPathSyntax.js +1 -1
  130. package/dist/domain/browserObservation.js +1 -1
  131. package/dist/domain/browserObservationSchemas.js +13 -0
  132. package/dist/domain/browserScenarioValues.js +9 -2
  133. package/dist/domain/callPathSchemas.js +1 -1
  134. package/dist/domain/canonicalDigest.js +13 -1
  135. package/dist/domain/changedBehavior.js +3 -3
  136. package/dist/domain/comparisonStatus.js +2 -30
  137. package/dist/domain/conformanceTrustGate.js +0 -16
  138. package/dist/domain/evidence.js +17 -5
  139. package/dist/domain/evm/evmInterface.js +63 -0
  140. package/dist/domain/explicitSensitiveFailure.js +92 -0
  141. package/dist/domain/explicitSensitiveValues.js +10 -0
  142. package/dist/domain/firmware/firmwareAnalysis.js +8 -1
  143. package/dist/domain/javascript/electronActiveObservation.js +9 -4
  144. package/dist/domain/javascript/htmlArtifactReferences.js +40 -0
  145. package/dist/domain/javascript/javascriptExportShapeComparison.js +21 -5
  146. package/dist/domain/javascript/javascriptExportShapeVariants.js +0 -10
  147. package/dist/domain/javascript/javascriptSemanticGraph.js +21 -5
  148. package/dist/domain/jsonValue.js +4 -0
  149. package/dist/domain/localPath.js +2 -0
  150. package/dist/domain/native/binaryLayout.js +357 -0
  151. package/dist/domain/native/nativeCallObservation.js +164 -0
  152. package/dist/domain/native/objcSwiftMetadata.js +0 -39
  153. package/dist/domain/native/recordedCrash.js +266 -0
  154. package/dist/domain/native/recordedCrashNoteCoverage.js +73 -0
  155. package/dist/domain/{processCapture.js → process/processCapture.js} +139 -2
  156. package/dist/domain/{processCaptureExample.js → process/processCaptureExample.js} +5 -0
  157. package/dist/domain/process/processCaptureParsing.js +54 -0
  158. package/dist/domain/{processCaptureValidation.js → process/processCaptureValidation.js} +18 -2
  159. package/dist/domain/{processComparison.js → process/processComparison.js} +1 -1
  160. package/dist/domain/{processObservation.js → process/processObservation.js} +1 -1
  161. package/dist/domain/{processTraceSpecification.js → process/processTraceSpecification.js} +1 -1
  162. package/dist/domain/providerSelectionError.js +4 -2
  163. package/dist/domain/reconstructionVerification.js +3 -3
  164. package/dist/domain/reconstructionVerificationSchemas.js +1 -1
  165. package/dist/domain/residualUnknown.js +3 -2
  166. package/dist/domain/staticRuntimeCorrelation.js +3 -3
  167. package/dist/domain/webCaptureDiff.js +1 -0
  168. package/dist/domain/webNetworkCapture.js +118 -0
  169. package/dist/domain/webNetworkCaptureCoordinates.js +58 -0
  170. package/dist/dotnet/ManagedExceptionRegionValidation.js +41 -0
  171. package/dist/dotnet/ManagedMemberInspector.js +4 -2
  172. package/dist/dotnet/ManagedMemberInspectorCore.js +38 -32
  173. package/dist/dotnet/ManagedMemberInstructionDecoder.js +23 -7
  174. package/dist/dotnet/ManagedMemberRows.js +14 -3
  175. package/dist/dotnet/ManagedMetadataHeaps.js +20 -2
  176. package/dist/dotnet/ManagedMetadataInventory.js +38 -28
  177. package/dist/dotnet/ManagedMetadataInventoryRows.js +24 -10
  178. package/dist/dotnet/ManagedMethodBodyReader.js +7 -1
  179. package/dist/dotnet/ManagedNativeBoundaryHelpers.js +15 -3
  180. package/dist/dotnet/ManagedNativeBoundaryInspector.js +3 -1
  181. package/dist/dotnet/ManagedPeReader.js +11 -0
  182. package/dist/evm/EvmBytecodeCarrier.js +21 -0
  183. package/dist/evm/EvmWorkerLimits.js +40 -0
  184. package/dist/evm/EvmoleFailures.js +126 -0
  185. package/dist/evm/EvmoleInterfaceAdapter.js +53 -0
  186. package/dist/evm/EvmoleInterfaceProvider.js +273 -0
  187. package/dist/evm/EvmoleInterfaceWorker.js +87 -0
  188. package/dist/evm/EvmoleRelease.js +16 -0
  189. package/dist/firmware/FirmwareCommand.js +1 -0
  190. package/dist/generatedPackageMetadata.js +2 -2
  191. package/dist/ghidra/GhidraLauncher.js +1 -0
  192. package/dist/ghidra/GhidraProvider.js +2 -2
  193. package/dist/ghidra/GhidraProviderCapabilities.js +1 -1
  194. package/dist/hopper/BridgeLauncher.js +2 -0
  195. package/dist/hopper/LinuxPrivateDisplayProbe.js +4 -2
  196. package/dist/javascript/recovery/WakaruCommand.js +3 -0
  197. package/dist/javascript/sourceMaps/SourceMapCommand.js +1 -0
  198. package/dist/native/CommandRunner.js +2 -1
  199. package/dist/native/LldbCallTracer.js +278 -0
  200. package/dist/native/NativeCallObservation.js +135 -0
  201. package/dist/native/NativeMacOSProvider.js +19 -3
  202. package/dist/native/NativeMacOSProviderMetadata.js +8 -3
  203. package/dist/native/pwndbg/PwndbgCoreContext.js +160 -0
  204. package/dist/native/pwntools/PwntoolsDecoder.js +203 -0
  205. package/dist/native/pwntools/PwntoolsFailures.js +180 -0
  206. package/dist/native/pwntools/PwntoolsLayoutProvider.js +36 -0
  207. package/dist/native/pwntools/PwntoolsRecordedCrashProvider.js +75 -0
  208. package/dist/native/pwntools/PwntoolsRelease.js +19 -0
  209. package/dist/native/pwntools/PwntoolsResourceLimits.js +76 -0
  210. package/dist/native/pwntools/RecordedCrashSourceBindings.js +46 -0
  211. package/dist/native/pwntools/RecordedCrashStructureBindings.js +101 -0
  212. package/dist/process/DarwinProcessRunTokenReader.js +294 -0
  213. package/dist/process/OwnedCommand.js +113 -0
  214. package/dist/process/ProcessOwnership.js +263 -33
  215. package/dist/process/ProcessOwnershipObservation.js +161 -24
  216. package/dist/process/ProviderProcess.js +30 -3
  217. package/dist/{application → process/capture}/ProcessCaptureCapability.js +15 -0
  218. package/dist/{application → process/capture}/ProcessCaptureError.js +17 -1
  219. package/dist/process/capture/ProcessCaptureLifecycle.js +615 -0
  220. package/dist/{application → process/capture}/ProcessHarness.js +195 -29
  221. package/dist/{application → process/capture}/ProcessSampling.js +2 -2
  222. package/dist/server/createServer.js +22 -0
  223. package/dist/server/promptCompletion.js +10 -3
  224. package/dist/server/registerBinaryDiagnosticsTools.js +17 -0
  225. package/dist/server/registerEvidenceTools.js +1 -1
  226. package/dist/server/registerEvmTools.js +17 -0
  227. package/dist/server/registerProcessComparisonTool.js +1 -1
  228. package/dist/server/registerRecordedCrashTools.js +17 -0
  229. package/dist/server/registerSessionTools.js +2 -2
  230. package/dist/server/registerWebNetworkCaptureTool.js +28 -0
  231. package/dist/server/sessionAvailabilityPolicy.js +3 -0
  232. package/dist/server/sessionToolPolicies.js +1 -1
  233. package/dist/server/toolResult.js +1 -1
  234. package/native/windows/build/manifest.json +2 -2
  235. package/native/windows/build/rea-windows-x64.node +0 -0
  236. package/package.json +37 -9
  237. package/scripts/rea.mjs +11 -3
  238. package/skills/reverse-engineer-anything/SKILL.md +23 -3
  239. package/skills/reverse-engineer-anything/references/native-and-artifacts.md +29 -0
  240. package/third_party/README.md +60 -0
  241. package/third_party/evmole/LICENSE +21 -0
  242. package/third_party/evmole/README.md +22 -0
  243. package/third_party/pwndbg/LICENSE.md +21 -0
  244. package/third_party/pwndbg/README.md +23 -0
  245. package/third_party/pwntools/README.md +40 -0
  246. package/dist/application/AuthorizedArtifactInventory.js +0 -7
  247. package/dist/application/ProcessCaptureLifecycle.js +0 -299
  248. package/dist/application/StoreFileLock.js +0 -124
  249. package/dist/contracts/processCaptureExample.js +0 -2
  250. package/dist/domain/bytecodeProvider.js +0 -178
  251. package/dist/domain/customProtocolCapture.js +0 -137
  252. package/dist/domain/eventProcessTree.js +0 -225
  253. package/dist/domain/mobileApplicationInvestigation.js +0 -159
  254. package/dist/domain/packageAnalysis.js +0 -123
  255. package/dist/domain/peInspection.js +0 -197
  256. package/dist/domain/processCaptureParsing.js +0 -21
  257. package/dist/domain/protocolCapture.js +0 -217
  258. package/dist/domain/runtimeIdentification.js +0 -212
  259. package/dist/generatedMcpToolCatalog.js +0 -37
  260. /package/dist/application/{ArtifactExtractionDestination.js → artifacts/ArtifactExtractionDestination.js} +0 -0
  261. /package/dist/{application/ArtifactInventory → artifacts/inventory}/policy.js +0 -0
  262. /package/dist/{application/ArtifactInventory → artifacts/inventory}/types.js +0 -0
  263. /package/dist/domain/{processEvidenceProvider.js → process/processEvidenceProvider.js} +0 -0
  264. /package/dist/domain/{processScenario.js → process/processScenario.js} +0 -0
  265. /package/dist/domain/{processTraceComparison.js → process/processTraceComparison.js} +0 -0
  266. /package/dist/domain/{processTraceDimensionProjection.js → process/processTraceDimensionProjection.js} +0 -0
  267. /package/dist/domain/{processTraceEvaluation.js → process/processTraceEvaluation.js} +0 -0
  268. /package/dist/{application → process/capture}/FilesystemSnapshot.js +0 -0
  269. /package/dist/{application → process/capture}/ProcessCaptureEnvironment.js +0 -0
  270. /package/dist/{application → process/capture}/ProcessCaptureJournal.js +0 -0
  271. /package/dist/{application → process/capture}/ProcessFilesystemEffects.js +0 -0
  272. /package/dist/{application → process/capture}/ProcessNormalization.js +0 -0
  273. /package/dist/{application → process/capture}/ProcessScenarioRuntimeValidation.js +0 -0
  274. /package/dist/{application → process/capture}/ProcessTimer.js +0 -0
  275. /package/dist/{application → process/capture}/TerminalRenderer.js +0 -0
@@ -1,7 +1,7 @@
1
1
  import { z } from "zod";
2
2
  import { digestSchema } from "./../domain/digests.js";
3
3
  import { prefixedDigestSchema } from "./../domain/digests.js";
4
- import { PROCESS_COMPARISON_DIMENSIONS } from "./processComparison.js";
4
+ import { PROCESS_COMPARISON_DIMENSIONS } from "./process/processComparison.js";
5
5
  const evidenceIdSchema = prefixedDigestSchema("ev");
6
6
  const unknownIdSchema = prefixedDigestSchema("unk");
7
7
  const claimIdSchema = z.string().regex(/^[A-Za-z][A-Za-z0-9._-]*$/u);
@@ -147,9 +147,10 @@ export const recordUnknownInputSchema = residualUnknownObjectSchema
147
147
  .extend({
148
148
  supporting_evidence_ids: z.array(evidenceIdSchema).default([]),
149
149
  contradicting_evidence_ids: z.array(evidenceIdSchema).default([]),
150
- });
150
+ })
151
+ .strict();
151
152
  /** Explicit optimistic-concurrency update command for one unknown. */
152
- export const updateUnknownInputSchema = z.object({
153
+ export const updateUnknownInputSchema = z.strictObject({
153
154
  unknown_id: unknownIdSchema,
154
155
  expected_revision: z.number().int().min(1),
155
156
  status: residualUnknownObjectSchema.shape.status,
@@ -5,9 +5,9 @@ import { artifactComparisonResultSchema } from "./artifactComparison.js";
5
5
  import { uniqueSorted } from "./canonicalOrdering.js";
6
6
  import { evidenceSchema, parseEvidence } from "./evidence.js";
7
7
  import { functionComparisonResultSchema } from "./functionComparison.js";
8
- import { PROCESS_PROVIDER, isProcessEvidenceProvider, } from "./processEvidenceProvider.js";
9
- import { comparisonStatusSchema, processCaptureComparisonSchema, } from "./processCapture.js";
10
- import { PROCESS_COMPARISON_DIMENSIONS } from "./processComparison.js";
8
+ import { PROCESS_PROVIDER, isProcessEvidenceProvider, } from "./process/processEvidenceProvider.js";
9
+ import { comparisonStatusSchema, processCaptureComparisonSchema, } from "./process/processCapture.js";
10
+ import { PROCESS_COMPARISON_DIMENSIONS } from "./process/processComparison.js";
11
11
  import { prefixedDigestSchema } from "./../domain/digests.js";
12
12
  const evidenceIdSchema = prefixedDigestSchema("ev");
13
13
  const functionDimensionSchema = z.enum([
@@ -118,6 +118,7 @@ const networkMap = (inspection) => {
118
118
  status: request.status,
119
119
  mime_type: request.mime_type,
120
120
  encoded_data_length: request.encoded_data_length,
121
+ redirects: (request.redirects ?? []).map(({ request_timestamp: _requestTimestamp, redirect_event_timestamp: _redirectEventTimestamp, ...redirect }) => redirect),
121
122
  initiator: request.initiator,
122
123
  body_shapes: request.body_shapes,
123
124
  });
@@ -0,0 +1,118 @@
1
+ import { z } from "zod";
2
+ import { jsonValueSchema } from "./jsonValue.js";
3
+ /** Complete historical evidence budgets; exceeding a budget returns no partial capture. */
4
+ export const WEB_NETWORK_CAPTURE_LIMITS = {
5
+ inputBytes: 32 * 1024 * 1024,
6
+ outputBytes: 96 * 1024 * 1024,
7
+ timeoutMs: 30_000,
8
+ depth: 64,
9
+ };
10
+ /** Inspect an explicit retained capture without contacting its recorded destinations. */
11
+ export const inspectWebNetworkCaptureInputSchema = z.strictObject({
12
+ capture_path: z
13
+ .string()
14
+ .min(1)
15
+ .describe("Absolute local HAR or mitmproxy capture path."),
16
+ format: z.enum(["har", "mitmproxy"]),
17
+ record_ordinals: z
18
+ .array(z.number().int().nonnegative())
19
+ .min(1)
20
+ .optional()
21
+ .describe("Zero-based producer record ordinals, in caller-selected order. Omit to inspect every record."),
22
+ sensitive_values: z
23
+ .array(z.string().min(1))
24
+ .default([])
25
+ .describe("Literal text excluded from capture payloads, byte/numeric projections, producer coordinates, artifact paths/digests and diagnostic text. Declaration strings are never persisted in Evidence parameters. Canonical contract keys, enums and provider identities remain intact."),
26
+ });
27
+ const pointerSchema = z
28
+ .string()
29
+ .describe("RFC 6901 pointer in the producer's reported object.");
30
+ const redactionSchema = z.union([
31
+ z.strictObject({
32
+ pointer: pointerSchema,
33
+ reason: z.enum(["transport-credential", "explicit-sensitive-value"]),
34
+ }),
35
+ z.strictObject({
36
+ pointer: pointerSchema,
37
+ reason: z.literal("explicit-sensitive-value"),
38
+ scope: z
39
+ .literal("property-name")
40
+ .describe("An omitted property name; pointer identifies its actual parent, not an invented child coordinate."),
41
+ }),
42
+ ]);
43
+ const binaryBase = {
44
+ pointer: pointerSchema,
45
+ representation: z.enum(["producer-bytes", "har-base64-content"]),
46
+ };
47
+ const binarySchema = z.discriminatedUnion("state", [
48
+ z.strictObject({
49
+ ...binaryBase,
50
+ state: z.literal("retained"),
51
+ content_base64: z.string(),
52
+ bytes: z.number().int().nonnegative(),
53
+ sha256: z.string().regex(/^[0-9a-f]{64}$/),
54
+ }),
55
+ z.strictObject({
56
+ ...binaryBase,
57
+ state: z.literal("redacted"),
58
+ content_base64: z.null(),
59
+ bytes: z.null(),
60
+ sha256: z.null(),
61
+ }),
62
+ ]);
63
+ const numberSchema = z.strictObject({
64
+ pointer: pointerSchema,
65
+ producer_type: z.enum(["json-number", "integer", "float"]),
66
+ literal: z.string(),
67
+ });
68
+ /** Preserve producer representations with independent sidecars rather than invented browser IDs. */
69
+ export const webNetworkCaptureRecordSchema = z.strictObject({
70
+ ordinal: z.number().int().nonnegative(),
71
+ location: z.discriminatedUnion("kind", [
72
+ z.strictObject({
73
+ kind: z.literal("unknown"),
74
+ reason: z.literal("explicit-sensitive-value"),
75
+ }),
76
+ z.strictObject({ kind: z.literal("json-pointer"), pointer: pointerSchema }),
77
+ z.strictObject({
78
+ kind: z.literal("byte-range"),
79
+ offset: z.number().int().nonnegative(),
80
+ bytes: z.number().int().positive(),
81
+ }),
82
+ ]),
83
+ reported: jsonValueSchema,
84
+ binary_fields: z.array(binarySchema),
85
+ numeric_literals: z.array(numberSchema),
86
+ redactions: z.array(redactionSchema),
87
+ limitations: z.array(z.string()),
88
+ });
89
+ /** Historical capture inspection has file identity, producer provenance and explicit unknowns. */
90
+ export const webNetworkCaptureSchema = z.strictObject({
91
+ artifact: z.strictObject({
92
+ path: z
93
+ .string()
94
+ .describe("Selected artifact path; empty when explicitly excluded from Evidence."),
95
+ sha256: z
96
+ .string()
97
+ .regex(/^[0-9a-f]{64}$/)
98
+ .nullable()
99
+ .describe("Observed artifact SHA-256; null only when explicitly excluded by a sensitive declaration."),
100
+ bytes: z.number().int().nonnegative(),
101
+ }),
102
+ format: z.enum(["har", "mitmproxy"]),
103
+ decoder: z.strictObject({
104
+ id: z.string().min(1),
105
+ name: z.string().min(1),
106
+ version: z.string().min(1),
107
+ }),
108
+ container: z.strictObject({
109
+ reported: jsonValueSchema,
110
+ numeric_literals: z.array(numberSchema),
111
+ redactions: z.array(redactionSchema),
112
+ records_pointer: pointerSchema.nullable(),
113
+ }),
114
+ total_records: z.number().int().nonnegative(),
115
+ records: z.array(webNetworkCaptureRecordSchema),
116
+ runtime_attribution: z.literal("unknown"),
117
+ limitations: z.array(z.string()),
118
+ });
@@ -0,0 +1,58 @@
1
+ import { redactExplicitText } from "./explicitSensitiveValues.js";
2
+ /** Exclude marked source coordinates without inventing a substitute record identity. */
3
+ export const excludeCaptureCoordinates = (value, sensitiveValues) => {
4
+ if (sensitiveValues.length === 0)
5
+ return value;
6
+ let excluded = false;
7
+ const sensitive = (pointer) => sensitiveValues.some((literal) => pointer.includes(literal));
8
+ const sidecars = (items) => items.filter((item) => {
9
+ if (!sensitive(item.pointer))
10
+ return true;
11
+ excluded = true;
12
+ return false;
13
+ });
14
+ const records = value.records.map((record) => {
15
+ const location = (record.location.kind === "json-pointer" &&
16
+ sensitive(record.location.pointer)) ||
17
+ (record.location.kind === "byte-range" &&
18
+ (sensitive(String(record.location.offset)) ||
19
+ sensitive(String(record.location.bytes))))
20
+ ? {
21
+ kind: "unknown",
22
+ reason: "explicit-sensitive-value",
23
+ }
24
+ : record.location;
25
+ if (location !== record.location)
26
+ excluded = true;
27
+ return {
28
+ ...record,
29
+ location,
30
+ binary_fields: sidecars(record.binary_fields),
31
+ numeric_literals: sidecars(record.numeric_literals),
32
+ redactions: sidecars(record.redactions),
33
+ };
34
+ });
35
+ const recordsPointer = value.container.records_pointer;
36
+ const records_pointer = recordsPointer !== null && sensitive(recordsPointer)
37
+ ? null
38
+ : recordsPointer;
39
+ if (records_pointer !== recordsPointer)
40
+ excluded = true;
41
+ const container = {
42
+ ...value.container,
43
+ records_pointer,
44
+ numeric_literals: sidecars(value.container.numeric_literals),
45
+ redactions: sidecars(value.container.redactions),
46
+ };
47
+ return {
48
+ ...value,
49
+ container,
50
+ records,
51
+ limitations: excluded
52
+ ? [
53
+ ...value.limitations,
54
+ redactExplicitText("Marked source coordinates and their sidecars are excluded; affected record locations remain unknown.", sensitiveValues),
55
+ ]
56
+ : value.limitations,
57
+ };
58
+ };
@@ -0,0 +1,41 @@
1
+ const clauseFlagsIssue = (region) => [0, 1, 2, 4].includes(region.flags)
2
+ ? null
3
+ : `Exception clause has unsupported flags 0x${region.flags.toString(16)}`;
4
+ const methodExtentIssue = (start, length, ilSize, name) => start <= ilSize && length <= ilSize - start
5
+ ? null
6
+ : `Exception clause ${name} range leaves the method IL body`;
7
+ const instructionBoundaryIssue = (start, length, boundaries, name) => {
8
+ const end = start + length;
9
+ return boundaries.has(start) && boundaries.has(end)
10
+ ? null
11
+ : `Exception clause ${name} range does not align with CIL instruction boundaries`;
12
+ };
13
+ const filterOrderingIssue = (region, ilSize) => {
14
+ if (region.flags !== 1)
15
+ return null;
16
+ const offset = region.filter_offset;
17
+ return offset !== null && offset < ilSize && offset < region.handler_offset
18
+ ? null
19
+ : "Exception clause filter must start before its handler within the method IL body";
20
+ };
21
+ const clauseIssue = (region, ilSize, boundaries) => clauseFlagsIssue(region) ??
22
+ methodExtentIssue(region.try_offset, region.try_length, ilSize, "try") ??
23
+ instructionBoundaryIssue(region.try_offset, region.try_length, boundaries, "try") ??
24
+ methodExtentIssue(region.handler_offset, region.handler_length, ilSize, "handler") ??
25
+ instructionBoundaryIssue(region.handler_offset, region.handler_length, boundaries, "handler") ??
26
+ (region.flags === 1 && region.filter_offset !== null
27
+ ? instructionBoundaryIssue(region.filter_offset, 0, boundaries, "filter")
28
+ : null) ??
29
+ filterOrderingIssue(region, ilSize);
30
+ /** Validate EH extents and instruction boundaries while retaining each clause. */
31
+ export const validateExceptionRegionRanges = (parsed, ilSize, instructionOffsets) => {
32
+ if (parsed.status === "malformed")
33
+ return parsed;
34
+ const boundaries = new Set([...instructionOffsets, ilSize]);
35
+ for (const region of parsed.regions) {
36
+ const issue = clauseIssue(region, ilSize, boundaries);
37
+ if (issue !== null)
38
+ return { status: "malformed", regions: parsed.regions, issue };
39
+ }
40
+ return parsed;
41
+ };
@@ -68,9 +68,11 @@ export const inspectManagedMembersBytes = (bytes, target) => {
68
68
  const { layout, inventory } = readMemberInventory(bytes, pe);
69
69
  issues.push(...inventory.issues);
70
70
  const ranges = typeRanges(bytes, layout);
71
- const types = parseTypes(bytes, layout, ranges);
71
+ const parsedTypes = parseTypes(bytes, layout, ranges);
72
+ issues.push(...parsedTypes.issues);
72
73
  const fields = parseFields(bytes, layout, ranges);
73
74
  const memberRefs = parseMemberRefs(bytes, layout);
75
+ issues.push(...memberRefs.issues);
74
76
  const methods = parseMethods({
75
77
  bytes,
76
78
  layout,
@@ -97,7 +99,7 @@ export const inspectManagedMembersBytes = (bytes, target) => {
97
99
  requires_artifact_sha256: target.sha256,
98
100
  requires_mvid: inventory.module?.mvid ?? null,
99
101
  },
100
- types,
102
+ types: parsedTypes.types,
101
103
  fields: fields.fields,
102
104
  methods: methods.methods,
103
105
  member_refs: memberRefs.refs,
@@ -1,6 +1,6 @@
1
1
  import { managedFailure, ManagedReaderFailure, } from "./ManagedReaderFailure.js";
2
2
  import {} from "./ManagedMetadataLayout.js";
3
- import { metadataRowCursor, metadataCodedToken, metadataToken, readMetadataString, sha256Bytes, } from "./ManagedMetadataHeaps.js";
3
+ import { metadataRowCursor, metadataCodedToken, metadataCodedTokenInvalidReason, metadataToken, readMetadataString, sha256Bytes, } from "./ManagedMetadataHeaps.js";
4
4
  const rowRange = (table, start, nextStart) => {
5
5
  const total = table?.rowCount ?? 0;
6
6
  if (total === 0 || start === 0)
@@ -97,17 +97,27 @@ export const parseTypes = (bytes, layout, ranges) => {
97
97
  const fields = layout.table(4);
98
98
  const methods = layout.table(6);
99
99
  const items = [];
100
+ const issues = [];
100
101
  for (let row = 1; row <= (typeTable?.rowCount ?? 0); row += 1) {
101
102
  const cursor = metadataRowCursor(bytes, layout, 2, row);
102
103
  const flags = cursor.readUInt32();
103
104
  const name = readMetadataString(bytes, layout, cursor.readIndex(layout.stringIndexSize), layout.strings.size);
104
105
  const namespace = readMetadataString(bytes, layout, cursor.readIndex(layout.stringIndexSize), layout.strings.size);
106
+ const extendsOffset = cursor.offset;
105
107
  const extendsRaw = cursor.readIndex(layout.codedIndexSize("TypeDefOrRef"));
106
108
  const fieldStart = cursor.readIndex(layout.tableIndexSize(4));
107
109
  const methodStart = cursor.readIndex(layout.tableIndexSize(6));
108
110
  const nextRange = ranges[row] ?? null;
109
111
  const fieldRange = rowRange(fields, fieldStart, nextRange?.fieldStart ?? 0);
110
112
  const methodRange = rowRange(methods, methodStart, nextRange?.methodStart ?? 0);
113
+ const extendsReason = metadataCodedTokenInvalidReason(extendsRaw, 2, [2, 1, 27], layout.rowCounts);
114
+ if (extendsReason !== null)
115
+ issues.push({
116
+ code: "invalid-row",
117
+ scope: `metadata.TypeDef:${metadataToken(2, row)}`,
118
+ offset: extendsOffset,
119
+ detail: `TypeDef Extends coded index 0x${extendsRaw.toString(16)} is invalid: ${extendsReason}`,
120
+ });
111
121
  items.push({
112
122
  token: metadataToken(2, row),
113
123
  row_offset: cursor.start,
@@ -115,7 +125,7 @@ export const parseTypes = (bytes, layout, ranges) => {
115
125
  name,
116
126
  full_name: fullName(namespace, name),
117
127
  flags,
118
- extends_token: metadataCodedToken(extendsRaw, 2, [2, 1, 27]),
128
+ extends_token: metadataCodedToken(extendsRaw, 2, [2, 1, 27], layout.rowCounts),
119
129
  field_list: {
120
130
  first_row: fieldRange.first,
121
131
  last_row: fieldRange.last,
@@ -128,7 +138,7 @@ export const parseTypes = (bytes, layout, ranges) => {
128
138
  },
129
139
  });
130
140
  }
131
- return items;
141
+ return { types: items, issues };
132
142
  };
133
143
  const readCompressed = (bytes, offset) => {
134
144
  const first = bytes[offset];
@@ -171,42 +181,38 @@ const ELEMENT_TYPES = new Map([
171
181
  [0x19, "native-uint"],
172
182
  [0x1c, "object"],
173
183
  ]);
174
- const ELEMENT_TYPE_SUFFIXES = new Map([
175
- [0x0f, "*"],
176
- [0x10, "&"],
177
- ]);
178
184
  const readTypeSignature = (blob, offset) => {
179
- const kind = blob[offset];
185
+ const suffixes = [];
186
+ let cursor = offset;
187
+ let kind = blob[cursor];
188
+ while (kind === 0x0f || kind === 0x10 || kind === 0x1d) {
189
+ suffixes.push(kind === 0x0f ? "*" : kind === 0x10 ? "&" : "[]");
190
+ cursor += 1;
191
+ kind = blob[cursor];
192
+ }
180
193
  if (kind === undefined)
181
- throw managedFailure("invalid-blob", "signature", "truncated type signature", offset);
194
+ throw managedFailure("invalid-blob", "signature", "truncated type signature", cursor);
182
195
  const named = ELEMENT_TYPES.get(kind);
183
- if (named !== undefined)
184
- return { value: named, next: offset + 1 };
185
- const suffix = ELEMENT_TYPE_SUFFIXES.get(kind);
186
- if (suffix !== undefined) {
187
- const inner = readTypeSignature(blob, offset + 1);
188
- return { value: `${inner.value}${suffix}`, next: inner.next };
189
- }
190
- if (kind === 0x11 || kind === 0x12) {
191
- const token = readCompressed(blob, offset + 1);
192
- return {
193
- value: `${kind === 0x11 ? "valuetype" : "class"}:${String(token.value)}`,
194
- next: token.next,
195
- };
196
+ let value;
197
+ let next;
198
+ if (named !== undefined) {
199
+ value = named;
200
+ next = cursor + 1;
196
201
  }
197
- if (kind === 0x1d) {
198
- const inner = readTypeSignature(blob, offset + 1);
199
- return { value: `${inner.value}[]`, next: inner.next };
202
+ else if (kind === 0x11 || kind === 0x12) {
203
+ const token = readCompressed(blob, cursor + 1);
204
+ value = `${kind === 0x11 ? "valuetype" : "class"}:${String(token.value)}`;
205
+ next = token.next;
200
206
  }
201
- if (kind === 0x1e) {
202
- const variable = readCompressed(blob, offset + 1);
203
- return { value: `mvar:${String(variable.value)}`, next: variable.next };
207
+ else if (kind === 0x1e || kind === 0x13) {
208
+ const variable = readCompressed(blob, cursor + 1);
209
+ value = `${kind === 0x1e ? "mvar" : "var"}:${String(variable.value)}`;
210
+ next = variable.next;
204
211
  }
205
- if (kind === 0x13) {
206
- const variable = readCompressed(blob, offset + 1);
207
- return { value: `var:${String(variable.value)}`, next: variable.next };
212
+ else {
213
+ throw managedFailure("unsupported-signature", "signature", `unsupported element type 0x${kind.toString(16)}`, cursor);
208
214
  }
209
- throw managedFailure("unsupported-signature", "signature", `unsupported element type 0x${kind.toString(16)}`, offset);
215
+ return { value: value + suffixes.reverse().join(""), next };
210
216
  };
211
217
  const callingConvention = (value) => {
212
218
  const base = value & 0x0f;
@@ -1,4 +1,11 @@
1
1
  const opcode = (code) => OPCODES.get(code);
2
+ const PREFIX_OPCODES = new Set([
3
+ "unaligned.",
4
+ "volatile.",
5
+ "tail.",
6
+ "constrained.",
7
+ "readonly.",
8
+ ]);
2
9
  const OPCODES = new Map([
3
10
  [0x00, { name: "nop", operand: "none" }],
4
11
  [0x01, { name: "break", operand: "none" }],
@@ -344,7 +351,9 @@ const readOperand = (il, offset, kind) => {
344
351
  };
345
352
  export const decodeInstructions = (il) => {
346
353
  const parsed = [];
354
+ const instructionStarts = [];
347
355
  let offset = 0;
356
+ let prefixStart = null;
348
357
  let issue = null;
349
358
  try {
350
359
  while (offset < il.length) {
@@ -363,6 +372,13 @@ export const decodeInstructions = (il) => {
363
372
  break;
364
373
  }
365
374
  offset = operand.next;
375
+ if (PREFIX_OPCODES.has(descriptor.name)) {
376
+ prefixStart ??= start;
377
+ }
378
+ else {
379
+ instructionStarts.push(prefixStart ?? start);
380
+ prefixStart = null;
381
+ }
366
382
  parsed.push({
367
383
  offset: start,
368
384
  opcode: descriptor.name,
@@ -376,7 +392,7 @@ export const decodeInstructions = (il) => {
376
392
  cause instanceof Error ? cause.message : "Instruction decode failed";
377
393
  }
378
394
  const truncated = offset < il.length && issue === null ? 1 : 0;
379
- return { parsed, count: parsed.length, truncated, issue };
395
+ return { parsed, instructionStarts, count: parsed.length, truncated, issue };
380
396
  };
381
397
  export const parseExceptionRegions = (bytes, offset, methodEnd) => {
382
398
  const regions = [];
@@ -387,10 +403,10 @@ export const parseExceptionRegions = (bytes, offset, methodEnd) => {
387
403
  sectionOffset < 0 ||
388
404
  sectionOffset > methodEnd - 4 ||
389
405
  sectionOffset > bytes.length - 4)
390
- return malformedExceptionRegions("Exception section header leaves artifact");
406
+ return malformedExceptionRegions("Exception section header leaves artifact", regions);
391
407
  const kind = bytes.readUInt8(sectionOffset);
392
408
  if ((kind & 0x3f) !== 1)
393
- return malformedExceptionRegions(`Unsupported method data section kind ${String(kind & 0x3f)}`);
409
+ return malformedExceptionRegions(`Unsupported method data section kind ${String(kind & 0x3f)}`, regions);
394
410
  const fat = (kind & 0x40) !== 0;
395
411
  const size = fat
396
412
  ? bytes.readUInt8(sectionOffset + 1) |
@@ -401,12 +417,12 @@ export const parseExceptionRegions = (bytes, offset, methodEnd) => {
401
417
  if (size < 4 ||
402
418
  sectionOffset > methodEnd - size ||
403
419
  sectionOffset > bytes.length - size)
404
- return malformedExceptionRegions("Exception section size leaves artifact");
420
+ return malformedExceptionRegions("Exception section size leaves artifact", regions);
405
421
  if ((size - 4) % clauseSize !== 0)
406
- return malformedExceptionRegions("Exception section size does not contain whole clauses");
422
+ return malformedExceptionRegions("Exception section size does not contain whole clauses", regions);
407
423
  if (!fat &&
408
424
  (bytes[sectionOffset + 2] !== 0 || bytes[sectionOffset + 3] !== 0))
409
- return malformedExceptionRegions("Small exception section reserved bytes are nonzero");
425
+ return malformedExceptionRegions("Small exception section reserved bytes are nonzero", regions);
410
426
  const count = (size - 4) / clauseSize;
411
427
  regions.push(...readExceptionClauses(bytes, sectionOffset + 4, count, fat));
412
428
  hasMoreSections = (kind & 0x80) !== 0;
@@ -414,7 +430,7 @@ export const parseExceptionRegions = (bytes, offset, methodEnd) => {
414
430
  }
415
431
  return { status: "complete", regions };
416
432
  };
417
- const malformedExceptionRegions = (issue) => ({ status: "malformed", regions: [], issue });
433
+ const malformedExceptionRegions = (issue, regions = []) => ({ status: "malformed", regions, issue });
418
434
  const readExceptionClauses = (bytes, start, count, fat) => {
419
435
  const regions = [];
420
436
  const clauseSize = fat ? 24 : 12;
@@ -1,5 +1,5 @@
1
1
  import { createDeclaringTypeLookup, signature, } from "./ManagedMemberInspectorCore.js";
2
- import { metadataToken, metadataRowCursor, metadataCodedToken, readMetadataBlob, readMetadataString, } from "./ManagedMetadataHeaps.js";
2
+ import { metadataToken, metadataRowCursor, metadataCodedToken, metadataCodedTokenInvalidReason, readMetadataBlob, readMetadataString, } from "./ManagedMetadataHeaps.js";
3
3
  import { methodBody } from "./ManagedMethodBodyReader.js";
4
4
  export const parseFields = (bytes, layout, ranges) => {
5
5
  const fields = [];
@@ -29,6 +29,7 @@ export const parseFields = (bytes, layout, ranges) => {
29
29
  export const parseMemberRefs = (bytes, layout) => {
30
30
  const refs = [];
31
31
  const core = new Map();
32
+ const issues = [];
32
33
  const table = layout.table(10);
33
34
  for (let row = 1; row <= (table?.rowCount ?? 0); row += 1) {
34
35
  const cursor = metadataRowCursor(bytes, layout, 10, row);
@@ -36,16 +37,26 @@ export const parseMemberRefs = (bytes, layout) => {
36
37
  const name = readMetadataString(bytes, layout, cursor.readIndex(layout.stringIndexSize), layout.strings.size);
37
38
  const sig = readMetadataBlob(bytes, layout, cursor.readIndex(layout.blobIndexSize), layout.blob.size);
38
39
  const token = metadataToken(10, row);
40
+ const parentReason = metadataCodedTokenInvalidReason(parentRaw, 3, [2, 1, 26, 6, 27], layout.rowCounts);
41
+ if (parentReason !== null || parentRaw === 0)
42
+ issues.push({
43
+ code: "invalid-row",
44
+ scope: `metadata.MemberRef:${token}`,
45
+ offset: cursor.start,
46
+ detail: parentReason === null
47
+ ? "MemberRef parent coded index 0x0 is null, but the Class column must reference a row"
48
+ : `MemberRef parent coded index 0x${parentRaw.toString(16)} is invalid: ${parentReason}`,
49
+ });
39
50
  refs.push({
40
51
  token,
41
52
  row_offset: cursor.start,
42
- parent_token: metadataCodedToken(parentRaw, 3, [2, 1, 26, 6, 27]),
53
+ parent_token: metadataCodedToken(parentRaw, 3, [2, 1, 26, 6, 27], layout.rowCounts),
43
54
  name,
44
55
  signature: signature(sig),
45
56
  });
46
57
  core.set(token, { token, name });
47
58
  }
48
- return { refs, core };
59
+ return { refs, core, issues };
49
60
  };
50
61
  export const parseMethods = ({ bytes, layout, pe, ranges, }) => {
51
62
  const methods = [];
@@ -140,11 +140,29 @@ export const metadataRowCursor = (bytes, layout, table, row) => {
140
140
  return new MetadataRowCursor(bytes, start, start + descriptor.rowSize, `metadata.${descriptor.name}`);
141
141
  };
142
142
  /** Decode a metadata coded index without signed 32-bit coercion. */
143
- export const metadataCodedToken = (raw, bits, tables) => {
143
+ export const metadataCodedToken = (raw, bits, tables, rowCounts) => {
144
144
  if (raw === 0)
145
145
  return null;
146
146
  const tag = raw & (2 ** bits - 1);
147
147
  const row = Math.floor(raw / 2 ** bits);
148
148
  const table = tables[tag];
149
- return table === undefined || row === 0 ? null : metadataToken(table, row);
149
+ return table === undefined || row === 0 || row > (rowCounts[table] ?? 0)
150
+ ? null
151
+ : metadataToken(table, row);
152
+ };
153
+ /** Explain why a non-null coded index cannot name an admitted metadata row. */
154
+ export const metadataCodedTokenInvalidReason = (raw, bits, tables, rowCounts) => {
155
+ if (raw === 0)
156
+ return null;
157
+ const tag = raw & (2 ** bits - 1);
158
+ const row = Math.floor(raw / 2 ** bits);
159
+ const table = tables[tag];
160
+ if (table === undefined)
161
+ return `coded index tag ${tag} is not defined`;
162
+ if (row === 0)
163
+ return `coded index selects row zero in table ${table}`;
164
+ const count = rowCounts[table] ?? 0;
165
+ return row > count
166
+ ? `coded index selects row ${row} in table ${table}, which has ${count} rows`
167
+ : null;
150
168
  };