rea-agents 1.7.0 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +38 -31
- package/dist/application/CapabilityInventory.js +18 -0
- package/dist/application/CrossVersionInvestigation.js +10 -6
- package/dist/application/Doctor.js +56 -6
- package/dist/application/ElectronBoundaryAnalysis.js +96 -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 +199 -0
- package/dist/application/EnhancedTools.js +38 -13
- package/dist/application/InvestigationProviders.js +30 -0
- 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 +7 -1
- package/dist/application/JavaScriptArtifactGraphBuilder.js +59 -16
- package/dist/application/JavaScriptArtifactGraphEvidence.js +24 -14
- package/dist/application/JavaScriptArtifactReconstruction.js +3 -0
- package/dist/application/JavaScriptArtifactReconstructionInput.js +2 -83
- package/dist/application/JavaScriptReplayPlanning.js +201 -0
- package/dist/application/JavaScriptReplayService.js +266 -0
- package/dist/application/JavaScriptRuntimeReconciliationEvidence.js +25 -0
- package/dist/application/JavaScriptRuntimeReconciliationService.js +27 -0
- 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/PermissionAuthority.js +1 -1
- package/dist/application/ProcessSampling.js +15 -11
- package/dist/application/ProjectPermissionStore.js +1 -0
- package/dist/application/runtime.js +2 -0
- package/dist/browser/CdpCaptureEvents.js +49 -0
- 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/CdpPageCapture.js +8 -4
- package/dist/catalogIdentity.js +4 -1
- package/dist/cli.js +388 -0
- package/dist/cliApplicationCommands.js +62 -0
- package/dist/cliCommandNames.js +13 -0
- package/dist/cliElectronCommands.js +103 -1
- package/dist/cliJsonInput.js +54 -0
- package/dist/config.js +83 -0
- package/dist/contracts/applicationToolContracts.js +91 -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/toolContractExamples.js +10 -25
- package/dist/contracts/toolContracts.js +47 -45
- package/dist/contracts/toolEffects.js +160 -0
- package/dist/contracts/toolOutputSchemas.js +46 -31
- package/dist/doctorRuntime.js +42 -1
- package/dist/domain/artifactComparison.js +3 -4
- package/dist/domain/browserObservation.js +3 -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 +194 -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 +67 -1
- 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 +221 -0
- package/dist/domain/javascriptApplicationVersionKeys.js +170 -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 +88 -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 +191 -0
- package/dist/domain/javascriptStaticAnalysis.js +31 -43
- package/dist/domain/javascriptStaticAnalysisFindings.js +39 -0
- package/dist/domain/javascriptStaticAnalysisHelpers.js +10 -0
- package/dist/domain/javascriptStaticAnalysisState.js +6 -0
- package/dist/domain/managedApplicationGraph.js +556 -0
- package/dist/domain/managedArtifact.js +444 -0
- package/dist/domain/managedMemberComparison.js +565 -0
- package/dist/domain/managedNativeVerification.js +408 -0
- package/dist/domain/managedReconstruction.js +183 -0
- package/dist/domain/managedRuntimeCorrelation.js +175 -0
- package/dist/domain/reconstructionVerificationSchemas.js +1 -3
- package/dist/dotnet/ManagedArtifactInspector.js +318 -0
- package/dist/dotnet/ManagedMemberInspector.js +887 -0
- package/dist/dotnet/ManagedMetadataHeaps.js +131 -0
- package/dist/dotnet/ManagedMetadataInventory.js +368 -0
- package/dist/dotnet/ManagedMetadataLayout.js +322 -0
- package/dist/dotnet/ManagedNativeBoundaryInspector.js +391 -0
- package/dist/dotnet/ManagedPeReader.js +157 -0
- package/dist/dotnet/ManagedReaderFailure.js +10 -0
- package/dist/dotnet/ManagedStaticProvider.js +203 -0
- package/dist/generatedPackageMetadata.js +2 -2
- package/dist/ghidra/GhidraProvider.js +6 -0
- package/dist/main.js +14 -0
- package/dist/replay/JavaScriptReplayWorker.js +286 -0
- package/dist/replay/LinuxJavaScriptReplayRunner.js +434 -0
- package/dist/replay/LinuxRuntimeClosure.js +46 -0
- package/dist/replay/LinuxSeccompPolicy.js +40 -0
- package/dist/replay/ReplayWorkerProtocol.js +55 -0
- package/dist/replay/SystemJavaScriptReplayHost.js +182 -0
- package/dist/server/createServer.js +52 -0
- package/dist/server/registerApplicationTools.js +115 -0
- package/dist/server/registerArtifactComparisonTool.js +16 -9
- package/dist/server/registerBrowserTools.js +33 -8
- package/dist/server/registerBundleComparisonTool.js +17 -20
- package/dist/server/registerElectronTools.js +45 -3
- package/dist/server/registerEnhancedTools.js +58 -13
- package/dist/server/registerEvidenceTools.js +5 -1
- package/dist/server/registerFunctionComparisonTool.js +14 -7
- package/dist/server/registerInvestigationTools.js +21 -17
- package/dist/server/registerManagedTools.js +6 -0
- package/dist/server/registerManagedWorkflowTools.js +277 -0
- package/dist/server/registerOfficialTools.js +8 -8
- package/dist/server/registerProcessComparisonTool.js +25 -47
- package/dist/server/registerSessionStatusTool.js +7 -7
- package/dist/server/registerSessionTools.js +51 -86
- package/dist/server/sessionEvidence.js +14 -19
- package/dist/server/toolInputValidation.js +77 -0
- package/dist/server/toolRegistrationOptions.js +79 -2
- package/dist/server/toolResult.js +25 -17
- package/package.json +12 -4
- package/scripts/rea.mjs +1 -1
- package/skills/rea-analysis/SKILL.md +59 -3
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { managedFailure } from "./ManagedReaderFailure.js";
|
|
3
|
+
const requireRange = (bytes, offset, length, scope) => {
|
|
4
|
+
if (offset < 0 || length < 0 || offset > bytes.length - length)
|
|
5
|
+
throw managedFailure("invalid-row", scope, `${scope} leaves the artifact byte range`, offset >= 0 ? offset : null);
|
|
6
|
+
};
|
|
7
|
+
/** Cursor for checked fixed-width metadata row decoding. */
|
|
8
|
+
export class MetadataRowCursor {
|
|
9
|
+
bytes;
|
|
10
|
+
start;
|
|
11
|
+
end;
|
|
12
|
+
scope;
|
|
13
|
+
#offset;
|
|
14
|
+
constructor(bytes, start, end, scope) {
|
|
15
|
+
this.bytes = bytes;
|
|
16
|
+
this.start = start;
|
|
17
|
+
this.end = end;
|
|
18
|
+
this.scope = scope;
|
|
19
|
+
this.#offset = start;
|
|
20
|
+
}
|
|
21
|
+
get offset() {
|
|
22
|
+
return this.#offset;
|
|
23
|
+
}
|
|
24
|
+
readUInt16() {
|
|
25
|
+
this.require(2);
|
|
26
|
+
const value = this.bytes.readUInt16LE(this.#offset);
|
|
27
|
+
this.#offset += 2;
|
|
28
|
+
return value;
|
|
29
|
+
}
|
|
30
|
+
readUInt32() {
|
|
31
|
+
this.require(4);
|
|
32
|
+
const value = this.bytes.readUInt32LE(this.#offset);
|
|
33
|
+
this.#offset += 4;
|
|
34
|
+
return value;
|
|
35
|
+
}
|
|
36
|
+
readIndex(size) {
|
|
37
|
+
return size === 2 ? this.readUInt16() : this.readUInt32();
|
|
38
|
+
}
|
|
39
|
+
require(length) {
|
|
40
|
+
if (this.#offset < this.start || this.#offset > this.end - length)
|
|
41
|
+
throw managedFailure("invalid-row", this.scope, `${this.scope} row ended before all declared columns`, this.#offset);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
const compressedLength = (bytes, offset, scope) => {
|
|
45
|
+
requireRange(bytes, offset, 1, scope);
|
|
46
|
+
const first = bytes[offset] ?? 0;
|
|
47
|
+
if ((first & 0x80) === 0)
|
|
48
|
+
return { length: first, prefix: 1 };
|
|
49
|
+
if ((first & 0xc0) === 0x80) {
|
|
50
|
+
requireRange(bytes, offset, 2, scope);
|
|
51
|
+
return {
|
|
52
|
+
length: ((first & 0x3f) << 8) | (bytes[offset + 1] ?? 0),
|
|
53
|
+
prefix: 2,
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
if ((first & 0xe0) === 0xc0) {
|
|
57
|
+
requireRange(bytes, offset, 4, scope);
|
|
58
|
+
return {
|
|
59
|
+
length: (first & 0x1f) * 0x01_00_00_00 +
|
|
60
|
+
(bytes[offset + 1] ?? 0) * 0x01_00_00 +
|
|
61
|
+
(bytes[offset + 2] ?? 0) * 0x0100 +
|
|
62
|
+
(bytes[offset + 3] ?? 0),
|
|
63
|
+
prefix: 4,
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
throw managedFailure("invalid-blob", scope, "Metadata blob uses a reserved compressed-length prefix", offset);
|
|
67
|
+
};
|
|
68
|
+
/** Read one bounded UTF-8 value from #Strings. */
|
|
69
|
+
export const readMetadataString = (bytes, layout, index, maxBytes) => {
|
|
70
|
+
if (index === 0)
|
|
71
|
+
return "";
|
|
72
|
+
if (index < 0 || index >= layout.strings.size)
|
|
73
|
+
throw managedFailure("invalid-heap-index", "metadata.#Strings", "String heap index is outside #Strings", layout.strings.offset + Math.max(index, 0));
|
|
74
|
+
const start = layout.strings.offset + index;
|
|
75
|
+
const maximum = Math.min(layout.strings.offset + layout.strings.size, start + maxBytes + 1);
|
|
76
|
+
let end = start;
|
|
77
|
+
while (end < maximum && bytes[end] !== 0)
|
|
78
|
+
end += 1;
|
|
79
|
+
if (end === maximum)
|
|
80
|
+
throw managedFailure("limit-exceeded", "metadata.#Strings", `String heap item exceeds max_heap_item_bytes ${String(maxBytes)}`, start);
|
|
81
|
+
try {
|
|
82
|
+
return new TextDecoder("utf-8", { fatal: true }).decode(bytes.subarray(start, end));
|
|
83
|
+
}
|
|
84
|
+
catch {
|
|
85
|
+
throw managedFailure("invalid-string", "metadata.#Strings", "String heap item is not valid UTF-8", start);
|
|
86
|
+
}
|
|
87
|
+
};
|
|
88
|
+
/** Read one bounded item from #Blob without interpreting its contents. */
|
|
89
|
+
export const readMetadataBlob = (bytes, layout, index, maxBytes) => {
|
|
90
|
+
if (index === 0)
|
|
91
|
+
return Buffer.alloc(0);
|
|
92
|
+
if (index < 0 || index >= layout.blob.size)
|
|
93
|
+
throw managedFailure("invalid-heap-index", "metadata.#Blob", "Blob heap index is outside #Blob", layout.blob.offset + Math.max(index, 0));
|
|
94
|
+
const start = layout.blob.offset + index;
|
|
95
|
+
const decoded = compressedLength(bytes, start, "metadata.#Blob");
|
|
96
|
+
if (decoded.length > maxBytes)
|
|
97
|
+
throw managedFailure("limit-exceeded", "metadata.#Blob", `Blob heap item exceeds max_heap_item_bytes ${String(maxBytes)}`, start);
|
|
98
|
+
const content = start + decoded.prefix;
|
|
99
|
+
if (content > layout.blob.offset + layout.blob.size - decoded.length)
|
|
100
|
+
throw managedFailure("invalid-blob", "metadata.#Blob", "Blob heap item leaves #Blob", start);
|
|
101
|
+
return bytes.subarray(content, content + decoded.length);
|
|
102
|
+
};
|
|
103
|
+
/** Read one conventional GUID value from the one-based #GUID heap. */
|
|
104
|
+
export const readMetadataGuid = (bytes, layout, index) => {
|
|
105
|
+
if (index === 0)
|
|
106
|
+
return null;
|
|
107
|
+
const relative = (index - 1) * 16;
|
|
108
|
+
if (relative < 0 || relative > layout.guid.size - 16)
|
|
109
|
+
throw managedFailure("invalid-guid", "metadata.#GUID", "GUID heap index is outside #GUID", layout.guid.offset + Math.max(relative, 0));
|
|
110
|
+
const offset = layout.guid.offset + relative;
|
|
111
|
+
const part1 = bytes.readUInt32LE(offset).toString(16).padStart(8, "0");
|
|
112
|
+
const part2 = bytes
|
|
113
|
+
.readUInt16LE(offset + 4)
|
|
114
|
+
.toString(16)
|
|
115
|
+
.padStart(4, "0");
|
|
116
|
+
const part3 = bytes
|
|
117
|
+
.readUInt16LE(offset + 6)
|
|
118
|
+
.toString(16)
|
|
119
|
+
.padStart(4, "0");
|
|
120
|
+
const part4 = bytes.subarray(offset + 8, offset + 10).toString("hex");
|
|
121
|
+
const part5 = bytes.subarray(offset + 10, offset + 16).toString("hex");
|
|
122
|
+
return `${part1}-${part2}-${part3}-${part4}-${part5}`;
|
|
123
|
+
};
|
|
124
|
+
/** Format one metadata table/row pair as an exact build-local token. */
|
|
125
|
+
export const metadataToken = (table, row) => `0x${(table * 0x01_00_00_00 + row).toString(16).padStart(8, "0")}`;
|
|
126
|
+
/** Hash bytes without exposing their contents. */
|
|
127
|
+
export const sha256Bytes = (bytes) => createHash("sha256").update(bytes).digest("hex");
|
|
128
|
+
/** Derive the conventional strong-name token from a full public key. */
|
|
129
|
+
export const strongNameToken = (publicKey) => Buffer.from(createHash("sha1").update(publicKey).digest().subarray(-8))
|
|
130
|
+
.reverse()
|
|
131
|
+
.toString("hex");
|
|
@@ -0,0 +1,368 @@
|
|
|
1
|
+
import { METADATA_TABLE_NAMES, metadataRowOffset, } from "./ManagedMetadataLayout.js";
|
|
2
|
+
import { MetadataRowCursor, metadataToken, readMetadataBlob, readMetadataGuid, readMetadataString, sha256Bytes, strongNameToken, } from "./ManagedMetadataHeaps.js";
|
|
3
|
+
import { ManagedReaderFailure, 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
|
+
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, 0, 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
|
+
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, 32, 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
|
+
const readAssemblyReference = (bytes, layout, row, maxBytes) => {
|
|
71
|
+
const cursor = rowCursor(bytes, layout, 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, 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, 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, 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, 2, parentRow, maxBytes);
|
|
167
|
+
if (parentTag === 1)
|
|
168
|
+
return readTypeName(bytes, layout, 1, 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
|
+
const readCustomAttribute = (bytes, layout, row, maxBytes) => {
|
|
214
|
+
const cursor = rowCursor(bytes, layout, 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
|
+
const readResource = (bytes, layout, row, maxBytes, directory, issues) => {
|
|
239
|
+
const cursor = rowCursor(bytes, layout, 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
|
+
};
|
|
293
|
+
const safeRead = (operation, issues) => {
|
|
294
|
+
try {
|
|
295
|
+
return operation();
|
|
296
|
+
}
|
|
297
|
+
catch (cause) {
|
|
298
|
+
if (!(cause instanceof ManagedReaderFailure))
|
|
299
|
+
throw cause;
|
|
300
|
+
issues.push(cause.issue);
|
|
301
|
+
return undefined;
|
|
302
|
+
}
|
|
303
|
+
};
|
|
304
|
+
const pageRows = (descriptor, offset, limit, read) => {
|
|
305
|
+
const total = descriptor?.rowCount ?? 0;
|
|
306
|
+
const items = [];
|
|
307
|
+
const end = Math.min(total, offset + limit);
|
|
308
|
+
for (let index = offset; index < end; index += 1) {
|
|
309
|
+
const item = read(index + 1);
|
|
310
|
+
if (item !== undefined)
|
|
311
|
+
items.push(item);
|
|
312
|
+
}
|
|
313
|
+
return {
|
|
314
|
+
items,
|
|
315
|
+
offset,
|
|
316
|
+
limit,
|
|
317
|
+
total,
|
|
318
|
+
returned: items.length,
|
|
319
|
+
dropped: total - items.length,
|
|
320
|
+
complete: offset === 0 && end === total && items.length === total,
|
|
321
|
+
};
|
|
322
|
+
};
|
|
323
|
+
const uniqueIssues = (issues) => [
|
|
324
|
+
...new Map(issues.map((issue) => [JSON.stringify(issue), issue])).values(),
|
|
325
|
+
];
|
|
326
|
+
/** Inventory bounded identity tables without CLR reflection or execution. */
|
|
327
|
+
export const readManagedMetadataInventory = (bytes, layout, input, resourceDirectory) => {
|
|
328
|
+
const issues = [];
|
|
329
|
+
const module = safeRead(() => readModule(bytes, layout, input.maxHeapItemBytes), issues) ??
|
|
330
|
+
null;
|
|
331
|
+
const assembly = safeRead(() => readAssembly(bytes, layout, input.maxHeapItemBytes), issues) ?? null;
|
|
332
|
+
const references = pageRows(layout.table(35), input.referenceOffset, input.referenceLimit, (row) => safeRead(() => readAssemblyReference(bytes, layout, row, input.maxHeapItemBytes), issues));
|
|
333
|
+
const resources = pageRows(layout.table(40), input.resourceOffset, input.resourceLimit, (row) => safeRead(() => readResource(bytes, layout, row, input.maxHeapItemBytes, resourceDirectory, issues), issues));
|
|
334
|
+
const attributes = pageRows(layout.table(12), input.attributeOffset, input.attributeLimit, (row) => safeRead(() => readCustomAttribute(bytes, layout, row, input.maxHeapItemBytes), issues));
|
|
335
|
+
const referenceNames = [];
|
|
336
|
+
const referenceTable = layout.table(35);
|
|
337
|
+
for (let row = 1; row <= (referenceTable?.rowCount ?? 0); row += 1) {
|
|
338
|
+
const reference = safeRead(() => readAssemblyReference(bytes, layout, row, input.maxHeapItemBytes), issues);
|
|
339
|
+
if (reference !== undefined)
|
|
340
|
+
referenceNames.push(reference.name);
|
|
341
|
+
}
|
|
342
|
+
const targetFrameworks = [];
|
|
343
|
+
const attributeTable = layout.table(12);
|
|
344
|
+
for (let row = 1; row <= (attributeTable?.rowCount ?? 0); row += 1) {
|
|
345
|
+
const attribute = safeRead(() => readCustomAttribute(bytes, layout, row, input.maxHeapItemBytes), issues);
|
|
346
|
+
if (attribute?.parent_token === metadataToken(32, 1) &&
|
|
347
|
+
attribute.type_name ===
|
|
348
|
+
"System.Runtime.Versioning.TargetFrameworkAttribute" &&
|
|
349
|
+
attribute.decoded_fixed_string !== null &&
|
|
350
|
+
!targetFrameworks.includes(attribute.decoded_fixed_string))
|
|
351
|
+
targetFrameworks.push(attribute.decoded_fixed_string);
|
|
352
|
+
}
|
|
353
|
+
return {
|
|
354
|
+
module,
|
|
355
|
+
assembly,
|
|
356
|
+
targetFrameworks: targetFrameworks.sort(),
|
|
357
|
+
referenceNames: [...new Set(referenceNames)].sort(),
|
|
358
|
+
references,
|
|
359
|
+
resources,
|
|
360
|
+
attributes,
|
|
361
|
+
issues: uniqueIssues(issues),
|
|
362
|
+
};
|
|
363
|
+
};
|
|
364
|
+
/** Stable table-name/count projection for caller-visible coverage. */
|
|
365
|
+
export const managedTableRowCounts = (layout) => Object.fromEntries([...layout.tables.values()].map(({ index, rowCount }) => [
|
|
366
|
+
METADATA_TABLE_NAMES[index] ?? `Table${String(index)}`,
|
|
367
|
+
rowCount,
|
|
368
|
+
]));
|