rea-agents 1.7.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.
- package/README.md +112 -59
- package/bridge/ghidra/ReaGhidraBridge.java +96 -11
- package/dist/application/ArtifactExtraction.js +1 -1
- package/dist/application/ArtifactInventory/classify.js +42 -0
- package/dist/application/ArtifactInventory/hash.js +43 -0
- package/dist/application/ArtifactInventory/reader.js +41 -0
- package/dist/application/ArtifactInventory/scanCanonical.js +155 -0
- package/dist/application/ArtifactInventory/scanReader.js +124 -0
- package/dist/application/ArtifactInventory/types.js +10 -0
- package/dist/application/ArtifactInventory.js +9 -351
- package/dist/application/AuthorizedArtifactInventory.js +2 -2
- package/dist/application/BinarySession.js +47 -270
- package/dist/application/BinarySessionExecution.js +28 -0
- package/dist/application/BinarySessionOpen.js +39 -0
- package/dist/application/BinarySessionRecords.js +149 -0
- package/dist/application/BinarySessionStatus.js +75 -0
- package/dist/application/CapabilityInventory.js +42 -2
- package/dist/application/ClientRegistrationStatus.js +1 -1
- package/dist/application/CrossVersionInventory.js +8 -2
- package/dist/application/CrossVersionInvestigation.js +11 -29
- package/dist/application/CrossVersionInvestigationCollections.js +23 -0
- package/dist/application/DirectAnalysis.js +47 -44
- package/dist/application/Doctor.js +165 -197
- package/dist/application/DoctorDiagnostics.js +180 -0
- package/dist/application/ElectronBoundaryAnalysis.js +112 -0
- package/dist/application/ElectronBoundaryGraph.js +9 -0
- package/dist/application/ElectronBoundaryGraphContext.js +62 -0
- package/dist/application/ElectronBoundaryGraphIpc.js +242 -0
- package/dist/application/ElectronBoundaryGraphNative.js +86 -0
- package/dist/application/ElectronBoundaryGraphWindows.js +223 -0
- package/dist/application/EnhancedToolPagination.js +22 -0
- package/dist/application/EnhancedToolTypes.js +4 -0
- package/dist/application/EnhancedTools.js +58 -50
- package/dist/application/InstrumentedJavaScriptReplayHost.js +36 -0
- package/dist/application/InvestigationProviders.js +30 -0
- package/dist/application/InvestigationWorkspaceStore.js +24 -10
- package/dist/application/JavaScriptApplicationEvidence.js +26 -0
- package/dist/application/JavaScriptApplicationEvidenceGraph.js +89 -0
- package/dist/application/JavaScriptApplicationService.js +74 -0
- package/dist/application/JavaScriptApplicationWorkflowEvidence.js +29 -0
- package/dist/application/JavaScriptApplicationWorkflowService.js +90 -0
- package/dist/application/JavaScriptArtifactAnalysis.js +52 -7
- package/dist/application/JavaScriptArtifactFiles.js +6 -2
- package/dist/application/JavaScriptArtifactGraphBuilder.js +82 -17
- package/dist/application/JavaScriptArtifactGraphBundlers.js +225 -0
- package/dist/application/JavaScriptArtifactGraphContext.js +37 -36
- package/dist/application/JavaScriptArtifactGraphDocuments.js +27 -4
- package/dist/application/JavaScriptArtifactGraphEvidence.js +24 -14
- package/dist/application/JavaScriptArtifactGraphFindings.js +54 -8
- package/dist/application/JavaScriptArtifactGraphStructure.js +29 -101
- package/dist/application/JavaScriptArtifactPathResolution.js +269 -0
- package/dist/application/JavaScriptArtifactReconstruction.js +4 -1
- package/dist/application/JavaScriptArtifactReconstructionInput.js +2 -83
- package/dist/application/JavaScriptJsonModules.js +39 -0
- package/dist/application/JavaScriptModuleRelationships.js +262 -0
- package/dist/application/JavaScriptReplayPermission.js +16 -0
- package/dist/application/JavaScriptReplayPlanning.js +201 -0
- package/dist/application/JavaScriptReplayService.js +292 -0
- package/dist/application/JavaScriptRuntimeReconciliationEvidence.js +25 -0
- package/dist/application/JavaScriptRuntimeReconciliationService.js +27 -0
- package/dist/application/LinuxHopper.js +10 -0
- package/dist/application/MacHopper.js +22 -11
- package/dist/application/ManagedApplicationGraphService.js +55 -0
- package/dist/application/ManagedMemberComparisonService.js +122 -0
- package/dist/application/ManagedNativeVerificationService.js +39 -0
- package/dist/application/ManagedReconstructionService.js +58 -0
- package/dist/application/ManagedRuntimeCorrelationService.js +103 -0
- package/dist/application/NodeRuntimeCharacterizationService.js +196 -0
- package/dist/application/PermissionAuthority.js +106 -63
- package/dist/application/ProcessCaptureCapability.js +1 -1
- package/dist/application/ProcessCapturePermission.js +13 -0
- package/dist/application/ProcessCli.js +2 -12
- package/dist/application/ProcessHarness.js +1 -1
- package/dist/application/ProcessSampling.js +15 -11
- package/dist/application/ProjectPermissionStore.js +49 -9
- package/dist/application/RealHopperAssertions.js +76 -0
- package/dist/application/ReconstructionCoverageService.js +69 -0
- package/dist/application/ReconstructionCoverageWorkspaceStore.js +31 -0
- package/dist/application/RuntimeExecutableDiagnostics.js +172 -0
- package/dist/application/Setup.js +147 -259
- package/dist/application/SetupClientConfiguration.js +220 -0
- package/dist/application/SetupClients.js +24 -2
- package/dist/application/SetupPlan.js +123 -12
- package/dist/application/SetupSkill.js +26 -12
- package/dist/application/SupportedClients.js +1 -0
- package/dist/application/Uninstall.js +6 -3
- package/dist/application/WindowsCapabilities.js +86 -0
- package/dist/application/runtime.js +2 -0
- package/dist/artifacts/AsarArtifactReader.js +11 -0
- package/dist/browser/BrowserProcessStartup.js +88 -0
- package/dist/browser/CdpCaptureDocuments.js +12 -8
- package/dist/browser/CdpCaptureEventBodyShapes.js +75 -0
- package/dist/browser/CdpCaptureEventHandlers.js +336 -0
- package/dist/browser/CdpCaptureEventHelpers.js +71 -0
- package/dist/browser/CdpCaptureEventState.js +1 -0
- package/dist/browser/CdpCaptureEventTypes.js +1 -0
- package/dist/browser/CdpCaptureEvents.js +46 -464
- package/dist/browser/CdpElectronInspection.js +106 -162
- package/dist/browser/CdpElectronProvider.js +5 -1
- package/dist/browser/CdpElectronScriptEvents.js +69 -0
- package/dist/browser/CdpElectronScripts.js +94 -0
- package/dist/browser/CdpElectronWorkers.js +56 -0
- package/dist/browser/CdpObservationSession.js +51 -8
- package/dist/browser/CdpPageCapture.js +12 -129
- package/dist/browser/CdpPageCaptureScripts.js +97 -0
- package/dist/browser/CdpPageCaptureWorkers.js +46 -0
- package/dist/browser/CdpWebMcpDiscovery.js +38 -14
- package/dist/browser/PngVisualDiff.js +9 -2
- package/dist/catalogIdentity.js +8 -1
- package/dist/cli/artifactCommands.js +82 -0
- package/dist/cli/coreAnalysisCommands.js +232 -0
- package/dist/cli/managedCommands.js +397 -0
- package/dist/cli/managedProjectGraphCommand.js +24 -0
- package/dist/cli/options.js +9 -0
- package/dist/cli/setupCommands.js +108 -0
- package/dist/cli/types.js +1 -0
- package/dist/cli/utilityCommands.js +122 -0
- package/dist/cli.js +13 -383
- package/dist/cliApplicationCommands.js +176 -0
- package/dist/cliBrowserAdvancedCommands.js +73 -16
- package/dist/cliBrowserCommands.js +39 -115
- package/dist/cliCommandNames.js +17 -0
- package/dist/cliElectronCommands.js +100 -27
- package/dist/cliEvidenceCommands.js +13 -2
- package/dist/cliInvestigationCommands.js +53 -10
- package/dist/cliJsonInput.js +54 -0
- package/dist/cliObservationOptions.js +137 -0
- package/dist/cliPolicyCommands.js +80 -46
- package/dist/cliSetup.js +289 -0
- package/dist/config/environment.js +107 -0
- package/dist/config/parseConfig.js +228 -0
- package/dist/config/parsers.js +68 -0
- package/dist/config/permissions.js +30 -0
- package/dist/config/types.js +1 -0
- package/dist/config.js +1 -292
- package/dist/contracts/applicationToolContracts.js +225 -0
- package/dist/contracts/artifactToolContracts.js +5 -9
- package/dist/contracts/browserToolContracts.js +14 -62
- package/dist/contracts/electronToolContracts.js +45 -19
- package/dist/contracts/errorSchemas.js +1 -0
- package/dist/contracts/investigationExamples.js +0 -6
- package/dist/contracts/javascriptApplicationWorkflowExamples.js +46 -0
- package/dist/contracts/javascriptRuntimeReconciliationExample.js +208 -0
- package/dist/contracts/managedToolContracts.js +153 -0
- package/dist/contracts/managedWorkflowExamples.js +346 -0
- package/dist/contracts/managedWorkflowToolContracts.js +203 -0
- package/dist/contracts/nativeToolContracts.js +2 -6
- package/dist/contracts/promptContracts.js +18 -2
- package/dist/contracts/providerSelection.js +2 -0
- package/dist/contracts/toolContractExamples.js +10 -25
- package/dist/contracts/toolContracts.js +49 -47
- package/dist/contracts/toolEffects.js +174 -0
- package/dist/contracts/toolOutputSchemaGroups.js +232 -0
- package/dist/contracts/toolOutputSchemaPrimitives.js +327 -0
- package/dist/contracts/toolOutputSchemas.js +2 -535
- package/dist/doctorRuntime.js +42 -1
- package/dist/domain/analysisErrorPresentation.js +234 -0
- package/dist/domain/analysisErrorProjection.js +284 -0
- package/dist/domain/artifactComparison.js +37 -19
- package/dist/domain/binaryTarget.js +87 -37
- package/dist/domain/browserObservation.js +1 -282
- package/dist/domain/browserObservationSchemas.js +286 -0
- package/dist/domain/bundleComparison.js +4 -4
- package/dist/domain/electronObservation.js +13 -0
- package/dist/domain/electronStaticAnalysis.js +9 -0
- package/dist/domain/electronStaticAnalysisBrowser.js +206 -0
- package/dist/domain/electronStaticAnalysisIpc.js +171 -0
- package/dist/domain/electronStaticAnalysisNative.js +164 -0
- package/dist/domain/electronStaticAnalysisTypes.js +1 -0
- package/dist/domain/electronStaticAnalysisValues.js +103 -0
- package/dist/domain/errors.js +32 -448
- package/dist/domain/functionComparison.js +4 -165
- package/dist/domain/functionComparisonDimensions.js +164 -0
- package/dist/domain/functionComparisonSchemas.js +3 -8
- package/dist/domain/inputIssueProjection.js +52 -0
- package/dist/domain/javascriptApplicationAnalysis.js +155 -0
- package/dist/domain/javascriptApplicationChangeGraph.js +231 -0
- package/dist/domain/javascriptApplicationEvidenceSchemas.js +18 -0
- package/dist/domain/javascriptApplicationGraph.js +2 -1
- package/dist/domain/javascriptApplicationGraphSchemas.js +7 -0
- package/dist/domain/javascriptApplicationVersionComparison.js +116 -0
- package/dist/domain/javascriptApplicationVersionComparisonSchemas.js +139 -0
- package/dist/domain/javascriptApplicationVersionItems.js +233 -0
- package/dist/domain/javascriptApplicationVersionKeys.js +170 -0
- package/dist/domain/javascriptExportInstrumentation.js +97 -0
- package/dist/domain/javascriptFeatureSeed.js +140 -0
- package/dist/domain/javascriptFeatureTrace.js +232 -0
- package/dist/domain/javascriptFeatureTraceSchemas.js +136 -0
- package/dist/domain/javascriptFeatureTraversal.js +100 -0
- package/dist/domain/javascriptNativeHandoff.js +85 -0
- package/dist/domain/javascriptReplay.js +361 -0
- package/dist/domain/javascriptRuntimeLoadState.js +106 -0
- package/dist/domain/javascriptRuntimeReconciliation.js +42 -0
- package/dist/domain/javascriptRuntimeReconciliationGraph.js +169 -0
- package/dist/domain/javascriptRuntimeReconciliationMatching.js +247 -0
- package/dist/domain/javascriptRuntimeReconciliationParsing.js +160 -0
- package/dist/domain/javascriptRuntimeReconciliationResult.js +119 -0
- package/dist/domain/javascriptRuntimeReconciliationRuntime.js +316 -0
- package/dist/domain/javascriptRuntimeReconciliationSchemas.js +245 -0
- package/dist/domain/javascriptRuntimeStaticCandidates.js +193 -0
- package/dist/domain/javascriptSemanticAnalysis.js +386 -0
- package/dist/domain/javascriptSemanticIr.js +30 -0
- package/dist/domain/javascriptSemanticProjection.js +366 -0
- package/dist/domain/javascriptSemanticState.js +40 -0
- package/dist/domain/javascriptSemanticTraversal.js +22 -0
- package/dist/domain/javascriptSemanticValues.js +352 -0
- package/dist/domain/javascriptStaticAnalysis.js +26 -254
- package/dist/domain/javascriptStaticAnalysisBundler.js +209 -0
- package/dist/domain/javascriptStaticAnalysisCalls.js +180 -0
- package/dist/domain/javascriptStaticAnalysisFindings.js +39 -0
- package/dist/domain/javascriptStaticAnalysisHelpers.js +81 -2
- package/dist/domain/javascriptStaticAnalysisState.js +6 -0
- package/dist/domain/knownPageCoverage.js +21 -0
- package/dist/domain/managedApplicationGraph.js +206 -0
- package/dist/domain/managedApplicationGraphCoverage.js +170 -0
- package/dist/domain/managedApplicationGraphNodeCoverage.js +6 -0
- package/dist/domain/managedApplicationGraphNodes.js +336 -0
- package/dist/domain/managedArtifact.js +452 -0
- package/dist/domain/managedMemberComparison.js +211 -0
- package/dist/domain/managedMemberComparisonCoverage.js +72 -0
- package/dist/domain/managedMemberComparisonMatch.js +356 -0
- package/dist/domain/managedNativeVerification.js +46 -0
- package/dist/domain/managedNativeVerificationMatch.js +272 -0
- package/dist/domain/managedNativeVerificationSchemas.js +141 -0
- package/dist/domain/managedReconstruction.js +196 -0
- package/dist/domain/managedRuntimeCorrelation.js +184 -0
- package/dist/domain/nodeRuntimeCharacterization.js +66 -0
- package/dist/domain/permissionPolicy.js +2 -0
- package/dist/domain/reconstructionCoverage.js +387 -0
- package/dist/domain/reconstructionCoverageEvaluation.js +212 -0
- package/dist/domain/reconstructionVerificationSchemas.js +9 -3
- package/dist/domain/runtimeCharacterization.js +85 -0
- package/dist/domain/webBundleAnalyzer.js +3 -409
- package/dist/domain/webBundleAnalyzerAst.js +77 -0
- package/dist/domain/webBundleAnalyzerInspection.js +307 -0
- package/dist/domain/webBundleAnalyzerResult.js +120 -0
- package/dist/dotnet/ManagedArtifactInspector.js +339 -0
- package/dist/dotnet/ManagedMemberInspector.js +153 -0
- package/dist/dotnet/ManagedMemberInspectorCore.js +297 -0
- package/dist/dotnet/ManagedMemberInstructionDecoder.js +427 -0
- package/dist/dotnet/ManagedMemberRows.js +120 -0
- package/dist/dotnet/ManagedMetadataHeaps.js +131 -0
- package/dist/dotnet/ManagedMetadataInventory.js +104 -0
- package/dist/dotnet/ManagedMetadataInventoryRows.js +292 -0
- package/dist/dotnet/ManagedMetadataLayout.js +259 -0
- package/dist/dotnet/ManagedMetadataRowSizes.js +54 -0
- package/dist/dotnet/ManagedMethodBodyReader.js +143 -0
- package/dist/dotnet/ManagedNativeBoundaryHelpers.js +311 -0
- package/dist/dotnet/ManagedNativeBoundaryInspector.js +143 -0
- package/dist/dotnet/ManagedPeReader.js +188 -0
- package/dist/dotnet/ManagedReaderFailure.js +10 -0
- package/dist/dotnet/ManagedStaticProvider.js +203 -0
- package/dist/generatedPackageMetadata.js +2 -2
- package/dist/ghidra/GhidraClient.js +76 -91
- package/dist/ghidra/GhidraClientStartup.js +29 -0
- package/dist/ghidra/GhidraClientWire.js +34 -0
- package/dist/ghidra/GhidraDefaults.js +1 -1
- package/dist/ghidra/GhidraDiagnostics.js +6 -1
- package/dist/ghidra/GhidraInstallation.js +20 -15
- package/dist/ghidra/GhidraLauncher.js +93 -29
- package/dist/ghidra/GhidraProvider.js +67 -161
- package/dist/ghidra/GhidraProviderCapabilities.js +173 -0
- package/dist/ghidra/GhidraSessionValues.js +2 -0
- package/dist/ghidra/GhidraSocketConnection.js +44 -7
- package/dist/ghidra/GhidraTargetSnapshot.js +30 -0
- package/dist/ghidra/GhidraTransport.js +71 -0
- package/dist/identity.js +3 -2
- package/dist/main/messages.js +5 -0
- package/dist/main/reload.js +49 -0
- package/dist/main/shutdown.js +25 -0
- package/dist/main/startup.js +14 -0
- package/dist/main/state.js +26 -0
- package/dist/main/transport.js +61 -0
- package/dist/main/types.js +1 -0
- package/dist/main.js +35 -132
- package/dist/process/PrivateRuntimeRoot.js +3 -2
- package/dist/process/ProcessOwnership.js +35 -0
- package/dist/process/ProviderProcess.js +2 -0
- package/dist/replay/JavaScriptReplayWorker.js +300 -0
- package/dist/replay/LinuxJavaScriptReplayRunner.js +217 -0
- package/dist/replay/LinuxJavaScriptReplaySandbox.js +140 -0
- package/dist/replay/LinuxRuntimeClosure.js +46 -0
- package/dist/replay/LinuxSeccompPolicy.js +40 -0
- package/dist/replay/ReplayOutcome.js +95 -0
- package/dist/replay/ReplayProcessLifecycle.js +71 -0
- package/dist/replay/ReplayWorkerProtocol.js +55 -0
- package/dist/replay/SystemJavaScriptReplayHost.js +182 -0
- package/dist/server/ProcessCaptureElicitation.js +157 -0
- package/dist/server/createServer.js +158 -41
- package/dist/server/registerApplicationTools/characterization.js +57 -0
- package/dist/server/registerApplicationTools/compareVersions.js +34 -0
- package/dist/server/registerApplicationTools/controlledReplay.js +38 -0
- package/dist/server/registerApplicationTools/coverage.js +84 -0
- package/dist/server/registerApplicationTools/helpers.js +42 -0
- package/dist/server/registerApplicationTools/traceFeature.js +26 -0
- package/dist/server/registerApplicationTools/types.js +1 -0
- package/dist/server/registerApplicationTools.js +13 -0
- package/dist/server/registerArtifactComparisonTool.js +16 -9
- package/dist/server/registerBrowserTools.js +61 -60
- package/dist/server/registerBundleComparisonTool.js +17 -20
- package/dist/server/registerElectronTools.js +39 -12
- package/dist/server/registerEnhancedTools.js +52 -15
- package/dist/server/registerEvidenceResources.js +54 -0
- package/dist/server/registerEvidenceTools.js +5 -1
- package/dist/server/registerFunctionComparisonTool.js +14 -7
- package/dist/server/registerInvestigationTools/helpers.js +74 -0
- package/dist/server/registerInvestigationTools/types.js +1 -0
- package/dist/server/registerInvestigationTools.js +103 -132
- package/dist/server/registerManagedTools.js +6 -0
- package/dist/server/registerManagedWorkflowTools/compareManagedMembers.js +65 -0
- package/dist/server/registerManagedWorkflowTools/contract.js +8 -0
- package/dist/server/registerManagedWorkflowTools/evidence.js +43 -0
- package/dist/server/registerManagedWorkflowTools/importManagedReconstruction.js +39 -0
- package/dist/server/registerManagedWorkflowTools/planManagedRuntimeCorrelation.js +39 -0
- package/dist/server/registerManagedWorkflowTools/projectManagedApplicationGraph.js +52 -0
- package/dist/server/registerManagedWorkflowTools/types.js +1 -0
- package/dist/server/registerManagedWorkflowTools/verifyManagedNativeBoundaries.js +75 -0
- package/dist/server/registerManagedWorkflowTools.js +13 -0
- package/dist/server/registerOfficialTools.js +81 -67
- package/dist/server/registerProcessComparisonTool.js +25 -47
- package/dist/server/registerSessionRecordTools.js +137 -0
- package/dist/server/registerSessionStatusTool.js +9 -8
- package/dist/server/registerSessionTools.js +77 -216
- package/dist/server/sessionAvailabilityPolicy.js +10 -0
- package/dist/server/sessionEvidence.js +14 -19
- package/dist/server/toolInputValidation.js +77 -0
- package/dist/server/toolRegistrationOptions.js +177 -2
- package/dist/server/toolResult.js +30 -17
- package/package.json +21 -14
- package/scripts/rea.mjs +1 -1
- package/skills/{rea-analysis → reverse-engineer-anything}/SKILL.md +75 -6
- package/scripts/prepare-node-pty.mjs +0 -35
- package/scripts/rebuild-native.mjs +0 -22
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import { METADATA_TABLE_NAMES, } from "./ManagedMetadataLayout.js";
|
|
2
|
+
import { readAssembly, readAssemblyReference, readCustomAttribute, readModule, readResource, } from "./ManagedMetadataInventoryRows.js";
|
|
3
|
+
import { metadataToken } from "./ManagedMetadataHeaps.js";
|
|
4
|
+
import { ManagedReaderFailure } from "./ManagedReaderFailure.js";
|
|
5
|
+
const safeRead = (operation, issues) => {
|
|
6
|
+
try {
|
|
7
|
+
return operation();
|
|
8
|
+
}
|
|
9
|
+
catch (cause) {
|
|
10
|
+
if (!(cause instanceof ManagedReaderFailure))
|
|
11
|
+
throw cause;
|
|
12
|
+
issues.push(cause.issue);
|
|
13
|
+
return undefined;
|
|
14
|
+
}
|
|
15
|
+
};
|
|
16
|
+
const pageRows = (descriptor, offset, limit, read) => {
|
|
17
|
+
const total = descriptor?.rowCount ?? 0;
|
|
18
|
+
const items = [];
|
|
19
|
+
const end = Math.min(total, offset + limit);
|
|
20
|
+
for (let index = offset; index < end; index += 1) {
|
|
21
|
+
const item = read(index + 1);
|
|
22
|
+
if (item !== undefined)
|
|
23
|
+
items.push(item);
|
|
24
|
+
}
|
|
25
|
+
return {
|
|
26
|
+
items,
|
|
27
|
+
offset,
|
|
28
|
+
limit,
|
|
29
|
+
total,
|
|
30
|
+
returned: items.length,
|
|
31
|
+
dropped: total - items.length,
|
|
32
|
+
complete: offset === 0 && end === total && items.length === total,
|
|
33
|
+
};
|
|
34
|
+
};
|
|
35
|
+
const uniqueIssues = (issues) => [
|
|
36
|
+
...new Map(issues.map((issue) => [JSON.stringify(issue), issue])).values(),
|
|
37
|
+
];
|
|
38
|
+
const readPagedReferences = (bytes, layout, input, issues) => pageRows(layout.table(35), input.referenceOffset, input.referenceLimit, (row) => safeRead(() => readAssemblyReference(bytes, layout, row, input.maxHeapItemBytes), issues));
|
|
39
|
+
const readPagedResources = ({ bytes, layout, input, resourceDirectory, issues, }) => pageRows(layout.table(40), input.resourceOffset, input.resourceLimit, (row) => safeRead(() => readResource({
|
|
40
|
+
bytes,
|
|
41
|
+
layout,
|
|
42
|
+
row,
|
|
43
|
+
maxBytes: input.maxHeapItemBytes,
|
|
44
|
+
directory: resourceDirectory,
|
|
45
|
+
issues,
|
|
46
|
+
}), issues));
|
|
47
|
+
const readPagedAttributes = (bytes, layout, input, issues) => pageRows(layout.table(12), input.attributeOffset, input.attributeLimit, (row) => safeRead(() => readCustomAttribute(bytes, layout, row, input.maxHeapItemBytes), issues));
|
|
48
|
+
const collectReferenceNames = (bytes, layout, maxBytes, issues) => {
|
|
49
|
+
const names = [];
|
|
50
|
+
const referenceTable = layout.table(35);
|
|
51
|
+
for (let row = 1; row <= (referenceTable?.rowCount ?? 0); row += 1) {
|
|
52
|
+
const reference = safeRead(() => readAssemblyReference(bytes, layout, row, maxBytes), issues);
|
|
53
|
+
if (reference !== undefined)
|
|
54
|
+
names.push(reference.name);
|
|
55
|
+
}
|
|
56
|
+
return names;
|
|
57
|
+
};
|
|
58
|
+
const collectTargetFrameworks = (bytes, layout, maxBytes, issues) => {
|
|
59
|
+
const targetFrameworks = [];
|
|
60
|
+
const attributeTable = layout.table(12);
|
|
61
|
+
for (let row = 1; row <= (attributeTable?.rowCount ?? 0); row += 1) {
|
|
62
|
+
const attribute = safeRead(() => readCustomAttribute(bytes, layout, row, maxBytes), issues);
|
|
63
|
+
if (attribute?.parent_token === metadataToken(32, 1) &&
|
|
64
|
+
attribute.type_name ===
|
|
65
|
+
"System.Runtime.Versioning.TargetFrameworkAttribute" &&
|
|
66
|
+
attribute.decoded_fixed_string !== null &&
|
|
67
|
+
!targetFrameworks.includes(attribute.decoded_fixed_string))
|
|
68
|
+
targetFrameworks.push(attribute.decoded_fixed_string);
|
|
69
|
+
}
|
|
70
|
+
return targetFrameworks;
|
|
71
|
+
};
|
|
72
|
+
/** Inventory bounded identity tables without CLR reflection or execution. */
|
|
73
|
+
export const readManagedMetadataInventory = (bytes, layout, input, resourceDirectory) => {
|
|
74
|
+
const issues = [];
|
|
75
|
+
const module = safeRead(() => readModule(bytes, layout, input.maxHeapItemBytes), issues) ??
|
|
76
|
+
null;
|
|
77
|
+
const assembly = safeRead(() => readAssembly(bytes, layout, input.maxHeapItemBytes), issues) ?? null;
|
|
78
|
+
const references = readPagedReferences(bytes, layout, input, issues);
|
|
79
|
+
const resources = readPagedResources({
|
|
80
|
+
bytes,
|
|
81
|
+
layout,
|
|
82
|
+
input,
|
|
83
|
+
resourceDirectory,
|
|
84
|
+
issues,
|
|
85
|
+
});
|
|
86
|
+
const attributes = readPagedAttributes(bytes, layout, input, issues);
|
|
87
|
+
const referenceNames = collectReferenceNames(bytes, layout, input.maxHeapItemBytes, issues);
|
|
88
|
+
const targetFrameworks = collectTargetFrameworks(bytes, layout, input.maxHeapItemBytes, issues);
|
|
89
|
+
return {
|
|
90
|
+
module,
|
|
91
|
+
assembly,
|
|
92
|
+
targetFrameworks: [...targetFrameworks].sort(),
|
|
93
|
+
referenceNames: [...new Set(referenceNames)].sort(),
|
|
94
|
+
references,
|
|
95
|
+
resources,
|
|
96
|
+
attributes,
|
|
97
|
+
issues: uniqueIssues(issues),
|
|
98
|
+
};
|
|
99
|
+
};
|
|
100
|
+
/** Stable table-name/count projection for caller-visible coverage. */
|
|
101
|
+
export const managedTableRowCounts = (layout) => Object.fromEntries([...layout.tables.values()].map(({ index, rowCount }) => [
|
|
102
|
+
METADATA_TABLE_NAMES[index] ?? `Table${String(index)}`,
|
|
103
|
+
rowCount,
|
|
104
|
+
]));
|
|
@@ -0,0 +1,292 @@
|
|
|
1
|
+
import { metadataRowOffset, } from "./ManagedMetadataLayout.js";
|
|
2
|
+
import { MetadataRowCursor, metadataToken, readMetadataBlob, readMetadataGuid, readMetadataString, sha256Bytes, strongNameToken, } from "./ManagedMetadataHeaps.js";
|
|
3
|
+
import { managedFailure } from "./ManagedReaderFailure.js";
|
|
4
|
+
const rowCursor = ({ bytes, layout, table, row, }) => {
|
|
5
|
+
const descriptor = layout.table(table);
|
|
6
|
+
const start = metadataRowOffset(layout, table, row);
|
|
7
|
+
if (descriptor === undefined)
|
|
8
|
+
throw managedFailure("invalid-row", `metadata.${String(table)}`, "Metadata table is absent", start);
|
|
9
|
+
return new MetadataRowCursor(bytes, start, start + descriptor.rowSize, `metadata.${descriptor.name}`);
|
|
10
|
+
};
|
|
11
|
+
const publicKeyIdentity = (bytes, kind) => ({
|
|
12
|
+
kind: bytes.length === 0 ? "none" : kind,
|
|
13
|
+
byte_length: bytes.length,
|
|
14
|
+
sha256: bytes.length === 0 ? null : sha256Bytes(bytes),
|
|
15
|
+
token: bytes.length === 0
|
|
16
|
+
? null
|
|
17
|
+
: kind === "public-key"
|
|
18
|
+
? strongNameToken(bytes)
|
|
19
|
+
: bytes.length === 8
|
|
20
|
+
? bytes.toString("hex")
|
|
21
|
+
: null,
|
|
22
|
+
});
|
|
23
|
+
export const readModule = (bytes, layout, maxBytes) => {
|
|
24
|
+
const table = layout.table(0);
|
|
25
|
+
if (table === undefined || table.rowCount === 0)
|
|
26
|
+
return null;
|
|
27
|
+
const cursor = rowCursor({ bytes, layout, table: 0, row: 1 });
|
|
28
|
+
const generation = cursor.readUInt16();
|
|
29
|
+
const name = readMetadataString(bytes, layout, cursor.readIndex(layout.stringIndexSize), maxBytes);
|
|
30
|
+
const mvid = readMetadataGuid(bytes, layout, cursor.readIndex(layout.guidIndexSize));
|
|
31
|
+
const encId = readMetadataGuid(bytes, layout, cursor.readIndex(layout.guidIndexSize));
|
|
32
|
+
const encBaseId = readMetadataGuid(bytes, layout, cursor.readIndex(layout.guidIndexSize));
|
|
33
|
+
return {
|
|
34
|
+
name,
|
|
35
|
+
generation,
|
|
36
|
+
mvid,
|
|
37
|
+
enc_id: encId,
|
|
38
|
+
enc_base_id: encBaseId,
|
|
39
|
+
token: metadataToken(0, 1),
|
|
40
|
+
row_offset: table.offset,
|
|
41
|
+
};
|
|
42
|
+
};
|
|
43
|
+
export const readAssembly = (bytes, layout, maxBytes) => {
|
|
44
|
+
const table = layout.table(32);
|
|
45
|
+
if (table === undefined || table.rowCount === 0)
|
|
46
|
+
return null;
|
|
47
|
+
const cursor = rowCursor({ bytes, layout, table: 32, row: 1 });
|
|
48
|
+
const hashAlgorithm = cursor.readUInt32();
|
|
49
|
+
const version = [
|
|
50
|
+
cursor.readUInt16(),
|
|
51
|
+
cursor.readUInt16(),
|
|
52
|
+
cursor.readUInt16(),
|
|
53
|
+
cursor.readUInt16(),
|
|
54
|
+
].join(".");
|
|
55
|
+
const flags = cursor.readUInt32();
|
|
56
|
+
const key = readMetadataBlob(bytes, layout, cursor.readIndex(layout.blobIndexSize), maxBytes);
|
|
57
|
+
const name = readMetadataString(bytes, layout, cursor.readIndex(layout.stringIndexSize), maxBytes);
|
|
58
|
+
const culture = readMetadataString(bytes, layout, cursor.readIndex(layout.stringIndexSize), maxBytes);
|
|
59
|
+
return {
|
|
60
|
+
name,
|
|
61
|
+
version,
|
|
62
|
+
culture: culture.length === 0 ? null : culture,
|
|
63
|
+
flags,
|
|
64
|
+
hash_algorithm: hashAlgorithm,
|
|
65
|
+
public_key: publicKeyIdentity(key, (flags & 1) === 0 ? "public-key-token" : "public-key"),
|
|
66
|
+
token: metadataToken(32, 1),
|
|
67
|
+
row_offset: table.offset,
|
|
68
|
+
};
|
|
69
|
+
};
|
|
70
|
+
export const readAssemblyReference = (bytes, layout, row, maxBytes) => {
|
|
71
|
+
const cursor = rowCursor({ bytes, layout, table: 35, row });
|
|
72
|
+
const version = [
|
|
73
|
+
cursor.readUInt16(),
|
|
74
|
+
cursor.readUInt16(),
|
|
75
|
+
cursor.readUInt16(),
|
|
76
|
+
cursor.readUInt16(),
|
|
77
|
+
].join(".");
|
|
78
|
+
const flags = cursor.readUInt32();
|
|
79
|
+
const keyOrToken = readMetadataBlob(bytes, layout, cursor.readIndex(layout.blobIndexSize), maxBytes);
|
|
80
|
+
const name = readMetadataString(bytes, layout, cursor.readIndex(layout.stringIndexSize), maxBytes);
|
|
81
|
+
const culture = readMetadataString(bytes, layout, cursor.readIndex(layout.stringIndexSize), maxBytes);
|
|
82
|
+
const hashValue = readMetadataBlob(bytes, layout, cursor.readIndex(layout.blobIndexSize), maxBytes);
|
|
83
|
+
return {
|
|
84
|
+
name,
|
|
85
|
+
version,
|
|
86
|
+
culture: culture.length === 0 ? null : culture,
|
|
87
|
+
flags,
|
|
88
|
+
public_key_or_token: publicKeyIdentity(keyOrToken, (flags & 1) === 0 ? "public-key-token" : "public-key"),
|
|
89
|
+
hash_value_sha256: hashValue.length === 0 ? null : sha256Bytes(hashValue),
|
|
90
|
+
hash_value_length: hashValue.length,
|
|
91
|
+
token: metadataToken(35, row),
|
|
92
|
+
row_offset: metadataRowOffset(layout, 35, row),
|
|
93
|
+
};
|
|
94
|
+
};
|
|
95
|
+
const codedToken = (raw, bits, tables) => {
|
|
96
|
+
if (raw === 0)
|
|
97
|
+
return null;
|
|
98
|
+
const tag = raw & (2 ** bits - 1);
|
|
99
|
+
const row = Math.floor(raw / 2 ** bits);
|
|
100
|
+
const table = tables[tag];
|
|
101
|
+
return table === undefined || row === 0 ? null : metadataToken(table, row);
|
|
102
|
+
};
|
|
103
|
+
const readTypeName = ({ bytes, layout, table, row, maxBytes, }) => {
|
|
104
|
+
const descriptor = layout.table(table);
|
|
105
|
+
if (descriptor === undefined || row < 1 || row > descriptor.rowCount)
|
|
106
|
+
return null;
|
|
107
|
+
const cursor = rowCursor({ bytes, layout, table, row });
|
|
108
|
+
if (table === 1)
|
|
109
|
+
cursor.readIndex(layout.codedIndexSize("ResolutionScope"));
|
|
110
|
+
else
|
|
111
|
+
cursor.readUInt32();
|
|
112
|
+
const name = readMetadataString(bytes, layout, cursor.readIndex(layout.stringIndexSize), maxBytes);
|
|
113
|
+
const namespace = readMetadataString(bytes, layout, cursor.readIndex(layout.stringIndexSize), maxBytes);
|
|
114
|
+
return namespace.length === 0 ? name : `${namespace}.${name}`;
|
|
115
|
+
};
|
|
116
|
+
const declaringTypeForMethod = (bytes, layout, methodRow, maxBytes) => {
|
|
117
|
+
const types = layout.table(2);
|
|
118
|
+
const methods = layout.table(6);
|
|
119
|
+
if (types === undefined || methods === undefined)
|
|
120
|
+
return null;
|
|
121
|
+
for (let row = 1; row <= types.rowCount; row += 1) {
|
|
122
|
+
const cursor = rowCursor({ bytes, layout, table: 2, row });
|
|
123
|
+
cursor.readUInt32();
|
|
124
|
+
const nameIndex = cursor.readIndex(layout.stringIndexSize);
|
|
125
|
+
const namespaceIndex = cursor.readIndex(layout.stringIndexSize);
|
|
126
|
+
cursor.readIndex(layout.codedIndexSize("TypeDefOrRef"));
|
|
127
|
+
cursor.readIndex(layout.tableIndexSize(4));
|
|
128
|
+
const methodStart = cursor.readIndex(layout.tableIndexSize(6));
|
|
129
|
+
const methodEnd = row === types.rowCount
|
|
130
|
+
? methods.rowCount + 1
|
|
131
|
+
: methodListForType(bytes, layout, row + 1);
|
|
132
|
+
if (methodRow < methodStart || methodRow >= methodEnd)
|
|
133
|
+
continue;
|
|
134
|
+
const name = readMetadataString(bytes, layout, nameIndex, maxBytes);
|
|
135
|
+
const namespace = readMetadataString(bytes, layout, namespaceIndex, maxBytes);
|
|
136
|
+
return namespace.length === 0 ? name : `${namespace}.${name}`;
|
|
137
|
+
}
|
|
138
|
+
return null;
|
|
139
|
+
};
|
|
140
|
+
const methodListForType = (bytes, layout, row) => {
|
|
141
|
+
const cursor = rowCursor({ bytes, layout, table: 2, row });
|
|
142
|
+
cursor.readUInt32();
|
|
143
|
+
cursor.readIndex(layout.stringIndexSize);
|
|
144
|
+
cursor.readIndex(layout.stringIndexSize);
|
|
145
|
+
cursor.readIndex(layout.codedIndexSize("TypeDefOrRef"));
|
|
146
|
+
cursor.readIndex(layout.tableIndexSize(4));
|
|
147
|
+
return cursor.readIndex(layout.tableIndexSize(6));
|
|
148
|
+
};
|
|
149
|
+
const attributeTypeName = (bytes, layout, rawType, maxBytes) => {
|
|
150
|
+
const tag = rawType & 7;
|
|
151
|
+
const row = Math.floor(rawType / 8);
|
|
152
|
+
if (row === 0)
|
|
153
|
+
return null;
|
|
154
|
+
if (tag === 2)
|
|
155
|
+
return declaringTypeForMethod(bytes, layout, row, maxBytes);
|
|
156
|
+
if (tag !== 3)
|
|
157
|
+
return null;
|
|
158
|
+
const memberRefs = layout.table(10);
|
|
159
|
+
if (memberRefs === undefined || row > memberRefs.rowCount)
|
|
160
|
+
return null;
|
|
161
|
+
const cursor = rowCursor({ bytes, layout, table: 10, row });
|
|
162
|
+
const parent = cursor.readIndex(layout.codedIndexSize("MemberRefParent"));
|
|
163
|
+
const parentTag = parent & 7;
|
|
164
|
+
const parentRow = Math.floor(parent / 8);
|
|
165
|
+
if (parentTag === 0)
|
|
166
|
+
return readTypeName({ bytes, layout, table: 2, row: parentRow, maxBytes });
|
|
167
|
+
if (parentTag === 1)
|
|
168
|
+
return readTypeName({ bytes, layout, table: 1, row: parentRow, maxBytes });
|
|
169
|
+
if (parentTag === 3)
|
|
170
|
+
return declaringTypeForMethod(bytes, layout, parentRow, maxBytes);
|
|
171
|
+
return null;
|
|
172
|
+
};
|
|
173
|
+
const decodeCompressedLength = (blob, offset) => {
|
|
174
|
+
const first = blob[offset];
|
|
175
|
+
if (first === undefined)
|
|
176
|
+
return undefined;
|
|
177
|
+
if ((first & 0x80) === 0)
|
|
178
|
+
return { length: first, prefix: 1 };
|
|
179
|
+
if ((first & 0xc0) === 0x80 && blob[offset + 1] !== undefined)
|
|
180
|
+
return {
|
|
181
|
+
length: ((first & 0x3f) << 8) | (blob[offset + 1] ?? 0),
|
|
182
|
+
prefix: 2,
|
|
183
|
+
};
|
|
184
|
+
if ((first & 0xe0) === 0xc0 &&
|
|
185
|
+
blob[offset + 1] !== undefined &&
|
|
186
|
+
blob[offset + 2] !== undefined &&
|
|
187
|
+
blob[offset + 3] !== undefined)
|
|
188
|
+
return {
|
|
189
|
+
length: (first & 0x1f) * 0x01_00_00_00 +
|
|
190
|
+
(blob[offset + 1] ?? 0) * 0x01_00_00 +
|
|
191
|
+
(blob[offset + 2] ?? 0) * 0x0100 +
|
|
192
|
+
(blob[offset + 3] ?? 0),
|
|
193
|
+
prefix: 4,
|
|
194
|
+
};
|
|
195
|
+
return undefined;
|
|
196
|
+
};
|
|
197
|
+
const decodeFixedString = (blob) => {
|
|
198
|
+
if (blob.length < 3 || blob.readUInt16LE(0) !== 1 || blob[2] === 0xff)
|
|
199
|
+
return null;
|
|
200
|
+
const length = decodeCompressedLength(blob, 2);
|
|
201
|
+
if (length === undefined)
|
|
202
|
+
return null;
|
|
203
|
+
const start = 2 + length.prefix;
|
|
204
|
+
if (start > blob.length - length.length)
|
|
205
|
+
return null;
|
|
206
|
+
try {
|
|
207
|
+
return new TextDecoder("utf-8", { fatal: true }).decode(blob.subarray(start, start + length.length));
|
|
208
|
+
}
|
|
209
|
+
catch {
|
|
210
|
+
return null;
|
|
211
|
+
}
|
|
212
|
+
};
|
|
213
|
+
export const readCustomAttribute = (bytes, layout, row, maxBytes) => {
|
|
214
|
+
const cursor = rowCursor({ bytes, layout, table: 12, row });
|
|
215
|
+
const parentRaw = cursor.readIndex(layout.codedIndexSize("HasCustomAttribute"));
|
|
216
|
+
const typeRaw = cursor.readIndex(layout.codedIndexSize("CustomAttributeType"));
|
|
217
|
+
const value = readMetadataBlob(bytes, layout, cursor.readIndex(layout.blobIndexSize), maxBytes);
|
|
218
|
+
const parent = codedToken(parentRaw, 5, [
|
|
219
|
+
6, 4, 1, 2, 8, 9, 10, 0, 14, 23, 20, 17, 26, 27, 32, 35, 38, 39, 40, 42,
|
|
220
|
+
44, 43,
|
|
221
|
+
]);
|
|
222
|
+
if (parent === null)
|
|
223
|
+
throw managedFailure("invalid-row", "metadata.CustomAttribute", "CustomAttribute parent coded index is invalid", cursor.start);
|
|
224
|
+
const typeName = attributeTypeName(bytes, layout, typeRaw, maxBytes);
|
|
225
|
+
return {
|
|
226
|
+
parent_token: parent,
|
|
227
|
+
constructor_token: codedToken(typeRaw, 3, [undefined, undefined, 6, 10]),
|
|
228
|
+
type_name: typeName,
|
|
229
|
+
value_length: value.length,
|
|
230
|
+
value_sha256: sha256Bytes(value),
|
|
231
|
+
decoded_fixed_string: typeName === "System.Runtime.Versioning.TargetFrameworkAttribute"
|
|
232
|
+
? decodeFixedString(value)
|
|
233
|
+
: null,
|
|
234
|
+
token: metadataToken(12, row),
|
|
235
|
+
row_offset: cursor.start,
|
|
236
|
+
};
|
|
237
|
+
};
|
|
238
|
+
export const readResource = ({ bytes, layout, row, maxBytes, directory, issues, }) => {
|
|
239
|
+
const cursor = rowCursor({ bytes, layout, table: 40, row });
|
|
240
|
+
const declaredOffset = cursor.readUInt32();
|
|
241
|
+
const flags = cursor.readUInt32();
|
|
242
|
+
const name = readMetadataString(bytes, layout, cursor.readIndex(layout.stringIndexSize), maxBytes);
|
|
243
|
+
const implementationRaw = cursor.readIndex(layout.codedIndexSize("Implementation"));
|
|
244
|
+
const implementationToken = codedToken(implementationRaw, 2, [38, 35, 39]);
|
|
245
|
+
let dataLength = null;
|
|
246
|
+
let dataSha256 = null;
|
|
247
|
+
if (implementationRaw === 0) {
|
|
248
|
+
if (directory === null || declaredOffset > directory.size - 4) {
|
|
249
|
+
issues.push({
|
|
250
|
+
code: "invalid-resource",
|
|
251
|
+
scope: `metadata.ManifestResource:${metadataToken(40, row)}`,
|
|
252
|
+
offset: directory?.offset ?? null,
|
|
253
|
+
detail: "Embedded resource offset leaves the CLI resources directory",
|
|
254
|
+
});
|
|
255
|
+
}
|
|
256
|
+
else {
|
|
257
|
+
const start = directory.offset + declaredOffset;
|
|
258
|
+
dataLength = bytes.readUInt32LE(start);
|
|
259
|
+
if (dataLength > directory.size - declaredOffset - 4) {
|
|
260
|
+
issues.push({
|
|
261
|
+
code: "invalid-resource",
|
|
262
|
+
scope: `metadata.ManifestResource:${metadataToken(40, row)}`,
|
|
263
|
+
offset: start,
|
|
264
|
+
detail: "Embedded resource length leaves the CLI resources directory",
|
|
265
|
+
});
|
|
266
|
+
}
|
|
267
|
+
else if (dataLength > maxBytes) {
|
|
268
|
+
issues.push({
|
|
269
|
+
code: "limit-exceeded",
|
|
270
|
+
scope: `metadata.ManifestResource:${metadataToken(40, row)}`,
|
|
271
|
+
offset: start + 4,
|
|
272
|
+
detail: `Embedded resource exceeds max_heap_item_bytes ${String(maxBytes)}; digest omitted`,
|
|
273
|
+
});
|
|
274
|
+
}
|
|
275
|
+
else {
|
|
276
|
+
dataSha256 = sha256Bytes(bytes.subarray(start + 4, start + 4 + dataLength));
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
return {
|
|
281
|
+
name,
|
|
282
|
+
flags,
|
|
283
|
+
visibility: (flags & 7) === 1 ? "public" : (flags & 7) === 2 ? "private" : "unknown",
|
|
284
|
+
implementation_token: implementationToken,
|
|
285
|
+
embedded: implementationRaw === 0,
|
|
286
|
+
declared_offset: declaredOffset,
|
|
287
|
+
data_length: dataLength,
|
|
288
|
+
data_sha256: dataSha256,
|
|
289
|
+
token: metadataToken(40, row),
|
|
290
|
+
row_offset: cursor.start,
|
|
291
|
+
};
|
|
292
|
+
};
|
|
@@ -0,0 +1,259 @@
|
|
|
1
|
+
import { managedFailure } from "./ManagedReaderFailure.js";
|
|
2
|
+
import { computeRowSize, } from "./ManagedMetadataRowSizes.js";
|
|
3
|
+
export const METADATA_TABLE_NAMES = [
|
|
4
|
+
"Module",
|
|
5
|
+
"TypeRef",
|
|
6
|
+
"TypeDef",
|
|
7
|
+
"FieldPtr",
|
|
8
|
+
"Field",
|
|
9
|
+
"MethodPtr",
|
|
10
|
+
"MethodDef",
|
|
11
|
+
"ParamPtr",
|
|
12
|
+
"Param",
|
|
13
|
+
"InterfaceImpl",
|
|
14
|
+
"MemberRef",
|
|
15
|
+
"Constant",
|
|
16
|
+
"CustomAttribute",
|
|
17
|
+
"FieldMarshal",
|
|
18
|
+
"DeclSecurity",
|
|
19
|
+
"ClassLayout",
|
|
20
|
+
"FieldLayout",
|
|
21
|
+
"StandAloneSig",
|
|
22
|
+
"EventMap",
|
|
23
|
+
"EventPtr",
|
|
24
|
+
"Event",
|
|
25
|
+
"PropertyMap",
|
|
26
|
+
"PropertyPtr",
|
|
27
|
+
"Property",
|
|
28
|
+
"MethodSemantics",
|
|
29
|
+
"MethodImpl",
|
|
30
|
+
"ModuleRef",
|
|
31
|
+
"TypeSpec",
|
|
32
|
+
"ImplMap",
|
|
33
|
+
"FieldRVA",
|
|
34
|
+
"ENCLog",
|
|
35
|
+
"ENCMap",
|
|
36
|
+
"Assembly",
|
|
37
|
+
"AssemblyProcessor",
|
|
38
|
+
"AssemblyOS",
|
|
39
|
+
"AssemblyRef",
|
|
40
|
+
"AssemblyRefProcessor",
|
|
41
|
+
"AssemblyRefOS",
|
|
42
|
+
"File",
|
|
43
|
+
"ExportedType",
|
|
44
|
+
"ManifestResource",
|
|
45
|
+
"NestedClass",
|
|
46
|
+
"GenericParam",
|
|
47
|
+
"MethodSpec",
|
|
48
|
+
"GenericParamConstraint",
|
|
49
|
+
];
|
|
50
|
+
const CODED_INDEXES = {
|
|
51
|
+
TypeDefOrRef: { bits: 2, tables: [2, 1, 27] },
|
|
52
|
+
HasConstant: { bits: 2, tables: [4, 8, 23] },
|
|
53
|
+
HasCustomAttribute: {
|
|
54
|
+
bits: 5,
|
|
55
|
+
tables: [
|
|
56
|
+
6, 4, 1, 2, 8, 9, 10, 0, 14, 23, 20, 17, 26, 27, 32, 35, 38, 39, 40, 42,
|
|
57
|
+
44, 43,
|
|
58
|
+
],
|
|
59
|
+
},
|
|
60
|
+
HasFieldMarshal: { bits: 1, tables: [4, 8] },
|
|
61
|
+
HasDeclSecurity: { bits: 2, tables: [2, 6, 32] },
|
|
62
|
+
MemberRefParent: { bits: 3, tables: [2, 1, 26, 6, 27] },
|
|
63
|
+
HasSemantics: { bits: 1, tables: [20, 23] },
|
|
64
|
+
MethodDefOrRef: { bits: 1, tables: [6, 10] },
|
|
65
|
+
MemberForwarded: { bits: 1, tables: [4, 6] },
|
|
66
|
+
Implementation: { bits: 2, tables: [38, 35, 39] },
|
|
67
|
+
CustomAttributeType: { bits: 3, tables: [6, 10] },
|
|
68
|
+
ResolutionScope: { bits: 2, tables: [0, 26, 35, 1] },
|
|
69
|
+
TypeOrMethodDef: { bits: 1, tables: [2, 6] },
|
|
70
|
+
};
|
|
71
|
+
const SUPPORTED_TABLE_MASK = (1n << BigInt(METADATA_TABLE_NAMES.length)) - 1n;
|
|
72
|
+
const requireRange = (bytes, offset, length, scope) => {
|
|
73
|
+
if (!Number.isSafeInteger(offset) ||
|
|
74
|
+
!Number.isSafeInteger(length) ||
|
|
75
|
+
offset < 0 ||
|
|
76
|
+
length < 0 ||
|
|
77
|
+
offset > bytes.length - length)
|
|
78
|
+
throw managedFailure("invalid-metadata-root", scope, `${scope} leaves the metadata byte range`, Number.isSafeInteger(offset) && offset >= 0 ? offset : null);
|
|
79
|
+
};
|
|
80
|
+
const align4 = (value) => (value + 3) & ~3;
|
|
81
|
+
const readStreamName = (bytes, offset) => {
|
|
82
|
+
const endLimit = Math.min(bytes.length, offset + 32);
|
|
83
|
+
let end = offset;
|
|
84
|
+
while (end < endLimit && bytes[end] !== 0)
|
|
85
|
+
end += 1;
|
|
86
|
+
if (end === endLimit)
|
|
87
|
+
throw managedFailure("invalid-stream", "metadata.stream-name", "Metadata stream name is missing a bounded terminator", offset);
|
|
88
|
+
const nameBytes = bytes.subarray(offset, end);
|
|
89
|
+
if (nameBytes.some((byte) => byte < 0x20 || byte > 0x7e))
|
|
90
|
+
throw managedFailure("invalid-stream", "metadata.stream-name", "Metadata stream name is not printable ASCII", offset);
|
|
91
|
+
return {
|
|
92
|
+
name: nameBytes.toString("ascii"),
|
|
93
|
+
next: align4(end + 1),
|
|
94
|
+
};
|
|
95
|
+
};
|
|
96
|
+
const streamByName = (streams, names) => {
|
|
97
|
+
for (const name of names) {
|
|
98
|
+
const stream = streams.get(name);
|
|
99
|
+
if (stream !== undefined)
|
|
100
|
+
return stream;
|
|
101
|
+
}
|
|
102
|
+
return undefined;
|
|
103
|
+
};
|
|
104
|
+
const requireStream = (streams, name) => {
|
|
105
|
+
const stream = streams.get(name);
|
|
106
|
+
if (stream === undefined)
|
|
107
|
+
throw managedFailure("missing-stream", `metadata.${name}`, `Required metadata stream ${name} is absent`);
|
|
108
|
+
return stream;
|
|
109
|
+
};
|
|
110
|
+
const codedSize = (name, rowCounts) => {
|
|
111
|
+
const descriptor = CODED_INDEXES[name];
|
|
112
|
+
let maximum = 0;
|
|
113
|
+
for (const table of descriptor.tables)
|
|
114
|
+
maximum = Math.max(maximum, rowCounts[table] ?? 0);
|
|
115
|
+
return maximum < 2 ** (16 - descriptor.bits) ? 2 : 4;
|
|
116
|
+
};
|
|
117
|
+
const tableSize = (index, rows) => (rows[index] ?? 0) < 0x1_0000 ? 2 : 4;
|
|
118
|
+
const readMetadataStreams = (bytes, rootOffset) => {
|
|
119
|
+
if (bytes.readUInt32LE(0) !== 0x424a_5342)
|
|
120
|
+
throw managedFailure("invalid-metadata-root", "metadata.signature", "CLI metadata signature is not BSJB", rootOffset);
|
|
121
|
+
const versionLength = bytes.readUInt32LE(12);
|
|
122
|
+
requireRange(bytes, 16, versionLength, "metadata.version");
|
|
123
|
+
const versionBytes = bytes.subarray(16, 16 + versionLength);
|
|
124
|
+
const zero = versionBytes.indexOf(0);
|
|
125
|
+
const version = versionBytes
|
|
126
|
+
.subarray(0, zero < 0 ? versionBytes.length : zero)
|
|
127
|
+
.toString("utf8");
|
|
128
|
+
let cursor = align4(16 + versionLength);
|
|
129
|
+
requireRange(bytes, cursor, 4, "metadata.stream-count");
|
|
130
|
+
const streamCount = bytes.readUInt16LE(cursor + 2);
|
|
131
|
+
if (streamCount === 0 || streamCount > 64)
|
|
132
|
+
throw managedFailure("invalid-stream", "metadata.stream-count", "Metadata stream count is outside the admitted range", rootOffset + cursor + 2);
|
|
133
|
+
cursor += 4;
|
|
134
|
+
const streams = new Map();
|
|
135
|
+
for (let index = 0; index < streamCount; index += 1) {
|
|
136
|
+
requireRange(bytes, cursor, 8, "metadata.stream-header");
|
|
137
|
+
const relativeOffset = bytes.readUInt32LE(cursor);
|
|
138
|
+
const streamSize = bytes.readUInt32LE(cursor + 4);
|
|
139
|
+
const named = readStreamName(bytes, cursor + 8);
|
|
140
|
+
if (streams.has(named.name))
|
|
141
|
+
throw managedFailure("invalid-stream", "metadata.stream-header", `Duplicate metadata stream ${named.name}`, rootOffset + cursor);
|
|
142
|
+
requireRange(bytes, relativeOffset, streamSize, `metadata.${named.name}`);
|
|
143
|
+
streams.set(named.name, {
|
|
144
|
+
name: named.name,
|
|
145
|
+
offset: rootOffset + relativeOffset,
|
|
146
|
+
size: streamSize,
|
|
147
|
+
});
|
|
148
|
+
cursor = named.next;
|
|
149
|
+
}
|
|
150
|
+
return { streams, version };
|
|
151
|
+
};
|
|
152
|
+
const readTablesHeader = (bytes, streams) => {
|
|
153
|
+
const tablesStream = streamByName(streams, ["#~", "#-"]);
|
|
154
|
+
if (tablesStream === undefined)
|
|
155
|
+
throw managedFailure("missing-stream", "metadata.tables", "Required metadata table stream #~ or #- is absent");
|
|
156
|
+
const tableBytes = bytes.subarray(tablesStream.offset, tablesStream.offset + tablesStream.size);
|
|
157
|
+
requireRange(tableBytes, 0, 24, "metadata.tables-header");
|
|
158
|
+
const heapSizes = tableBytes[6] ?? 0;
|
|
159
|
+
const heaps = {
|
|
160
|
+
string: (heapSizes & 0x01) === 0 ? 2 : 4,
|
|
161
|
+
guid: (heapSizes & 0x02) === 0 ? 2 : 4,
|
|
162
|
+
blob: (heapSizes & 0x04) === 0 ? 2 : 4,
|
|
163
|
+
};
|
|
164
|
+
const valid = tableBytes.readBigUInt64LE(8);
|
|
165
|
+
if ((valid & ~SUPPORTED_TABLE_MASK) !== 0n)
|
|
166
|
+
throw managedFailure("invalid-tables", "metadata.valid-mask", "Metadata valid mask names unsupported tables outside the admitted ECMA-335 range", tablesStream.offset + 8);
|
|
167
|
+
return { tableBytes, tablesOffset: tablesStream.offset, heaps, valid };
|
|
168
|
+
};
|
|
169
|
+
const readRowCounts = (tableBytes, valid, maxTableRows, tablesOffset) => {
|
|
170
|
+
const rowCounts = Array(METADATA_TABLE_NAMES.length).fill(0);
|
|
171
|
+
let tableCursor = 24;
|
|
172
|
+
for (let index = 0; index < METADATA_TABLE_NAMES.length; index += 1) {
|
|
173
|
+
if ((valid & (1n << BigInt(index))) === 0n)
|
|
174
|
+
continue;
|
|
175
|
+
requireRange(tableBytes, tableCursor, 4, "metadata.table-row-count");
|
|
176
|
+
const count = tableBytes.readUInt32LE(tableCursor);
|
|
177
|
+
if (count > maxTableRows)
|
|
178
|
+
throw managedFailure("limit-exceeded", `metadata.${METADATA_TABLE_NAMES[index] ?? String(index)}`, `Metadata table row count ${String(count)} exceeds max_table_rows ${String(maxTableRows)}`, tablesOffset + tableCursor);
|
|
179
|
+
rowCounts[index] = count;
|
|
180
|
+
tableCursor += 4;
|
|
181
|
+
}
|
|
182
|
+
return rowCounts;
|
|
183
|
+
};
|
|
184
|
+
const buildRowSizeContext = (rowCounts, heaps) => ({
|
|
185
|
+
tableSize: (index) => tableSize(index, rowCounts),
|
|
186
|
+
codedSize: (name) => codedSize(name, rowCounts),
|
|
187
|
+
stringSize: heaps.string,
|
|
188
|
+
guidSize: heaps.guid,
|
|
189
|
+
blobSize: heaps.blob,
|
|
190
|
+
});
|
|
191
|
+
const buildMetadataTables = ({ tableBytes, rowCounts, heaps, valid, tablesOffset, }) => {
|
|
192
|
+
const tables = new Map();
|
|
193
|
+
const context = buildRowSizeContext(rowCounts, heaps);
|
|
194
|
+
let tableCursor = 24;
|
|
195
|
+
for (let index = 0; index < METADATA_TABLE_NAMES.length; index += 1) {
|
|
196
|
+
if ((valid & (1n << BigInt(index))) === 0n)
|
|
197
|
+
continue;
|
|
198
|
+
tableCursor += 4;
|
|
199
|
+
}
|
|
200
|
+
for (let index = 0; index < METADATA_TABLE_NAMES.length; index += 1) {
|
|
201
|
+
const count = rowCounts[index] ?? 0;
|
|
202
|
+
if ((valid & (1n << BigInt(index))) === 0n)
|
|
203
|
+
continue;
|
|
204
|
+
const sizeOfRow = computeRowSize(index, context);
|
|
205
|
+
const byteLength = count * sizeOfRow;
|
|
206
|
+
if (!Number.isSafeInteger(byteLength))
|
|
207
|
+
throw managedFailure("invalid-tables", "metadata.tables", "Metadata table byte size overflowed", tablesOffset + tableCursor);
|
|
208
|
+
requireRange(tableBytes, tableCursor, byteLength, "metadata.table-data");
|
|
209
|
+
tables.set(index, {
|
|
210
|
+
index,
|
|
211
|
+
name: METADATA_TABLE_NAMES[index] ?? `Table${String(index)}`,
|
|
212
|
+
rowCount: count,
|
|
213
|
+
rowSize: sizeOfRow,
|
|
214
|
+
offset: tablesOffset + tableCursor,
|
|
215
|
+
});
|
|
216
|
+
tableCursor += byteLength;
|
|
217
|
+
}
|
|
218
|
+
return tables;
|
|
219
|
+
};
|
|
220
|
+
/** Parse ECMA-335 stream and table layout with checked byte extents. */
|
|
221
|
+
export const readManagedMetadataLayout = (artifact, rootOffset, size, maxTableRows) => {
|
|
222
|
+
if (size < 20 || rootOffset < 0 || rootOffset > artifact.length - size)
|
|
223
|
+
throw managedFailure("invalid-metadata-root", "metadata.root", "CLI metadata directory leaves the artifact byte range", rootOffset);
|
|
224
|
+
const bytes = artifact.subarray(rootOffset, rootOffset + size);
|
|
225
|
+
const { streams, version } = readMetadataStreams(bytes, rootOffset);
|
|
226
|
+
const { tableBytes, tablesOffset, heaps, valid } = readTablesHeader(artifact, streams);
|
|
227
|
+
const rowCounts = readRowCounts(tableBytes, valid, maxTableRows, tablesOffset);
|
|
228
|
+
const tables = buildMetadataTables({
|
|
229
|
+
tableBytes,
|
|
230
|
+
rowCounts,
|
|
231
|
+
heaps,
|
|
232
|
+
valid,
|
|
233
|
+
tablesOffset,
|
|
234
|
+
});
|
|
235
|
+
return {
|
|
236
|
+
rootOffset,
|
|
237
|
+
size,
|
|
238
|
+
version,
|
|
239
|
+
streamNames: [...streams.keys()].sort(),
|
|
240
|
+
strings: requireStream(streams, "#Strings"),
|
|
241
|
+
guid: requireStream(streams, "#GUID"),
|
|
242
|
+
blob: requireStream(streams, "#Blob"),
|
|
243
|
+
tables,
|
|
244
|
+
rowCounts,
|
|
245
|
+
stringIndexSize: heaps.string,
|
|
246
|
+
guidIndexSize: heaps.guid,
|
|
247
|
+
blobIndexSize: heaps.blob,
|
|
248
|
+
table: (index) => tables.get(index),
|
|
249
|
+
tableIndexSize: (index) => tableSize(index, rowCounts),
|
|
250
|
+
codedIndexSize: (name) => codedSize(name, rowCounts),
|
|
251
|
+
};
|
|
252
|
+
};
|
|
253
|
+
/** Resolve a one-based metadata row to its exact file offset. */
|
|
254
|
+
export const metadataRowOffset = (layout, table, row) => {
|
|
255
|
+
const descriptor = layout.table(table);
|
|
256
|
+
if (descriptor === undefined || row < 1 || row > descriptor.rowCount)
|
|
257
|
+
throw managedFailure("invalid-row", `metadata.${METADATA_TABLE_NAMES[table] ?? String(table)}`, `Metadata row ${String(row)} is outside its table`);
|
|
258
|
+
return descriptor.offset + (row - 1) * descriptor.rowSize;
|
|
259
|
+
};
|