fix(managed): bind graph facts to their source Evidence

This commit is contained in:
morluto 2026-10-10 00:13:24 +08:00
parent b112d44a39
commit cc7b5af077
4 changed files with 309 additions and 124 deletions

View File

@ -151,8 +151,8 @@ export const projectManagedApplicationGraph = (
};
const artifactNode = addArtifactNode(state);
addArtifactIdentityNodes(state, artifactNode, parsed);
const memberCounts = addMemberNodes(state, artifactNode, parsed);
const boundaryCounts = addBoundaryNodes(state, artifactNode, parsed);
addMemberNodes(state, artifactNode, parsed);
addBoundaryNodes(state, artifactNode, parsed);
const omissions = omittedCounts(parsed);
const limitations = projectionLimitations(parsed, omissions);
const graph = createJavaScriptApplicationGraph({
@ -175,10 +175,16 @@ export const projectManagedApplicationGraph = (
summary: {
graph_nodes: graph.nodes.length,
graph_edges: graph.edges.length,
assemblies: parsed.artifact?.result.assembly === null ? 0 : 1,
modules: parsed.artifact?.result.module === null ? 0 : 1,
...memberCounts,
...boundaryCounts,
assemblies: countNodes(graph.nodes, "managed-assembly"),
modules: countNodes(graph.nodes, "managed-module"),
types: countNodes(graph.nodes, "managed-type"),
methods: countNodes(graph.nodes, "managed-method"),
fields: countNodes(graph.nodes, "managed-field"),
pinvoke_imports: countNodes(graph.nodes, "managed-pinvoke-import"),
native_implementations: countNodes(
graph.nodes,
"managed-native-implementation",
),
},
graph,
coverage: managedGraphResultCoverage(omissions),
@ -191,6 +197,11 @@ export const projectManagedApplicationGraph = (
});
};
const countNodes = (
nodes: readonly ApplicationNode[],
kind: ApplicationNode["kind"],
): number => nodes.filter((node) => node.kind === kind).length;
const parseManagedInputs = (
input: ProjectManagedApplicationGraphInput,
): ParsedManagedGraphInput => ({

View File

@ -1,11 +0,0 @@
import type { ParsedManagedGraphInput } from "./managedApplicationGraph.js";
/** Derive module coverage from the best available managed evidence source. */
export const moduleCoverageState = (
parsed: ParsedManagedGraphInput,
): "complete" | "partial" | "unavailable" =>
parsed.artifact !== null && parsed.artifact.result.module !== null
? parsed.artifact.result.coverage.state
: parsed.members !== null && parsed.members.result.module !== null
? parsed.members.result.coverage.state
: (parsed.boundaries?.result.coverage.state ?? "complete");

View File

@ -6,7 +6,9 @@ import {
import type { ApplicationGraphEvidence } from "../javascript/javascriptApplicationEvidenceSchemas.js";
import type { ApplicationNode } from "../javascript/javascriptApplicationGraphSchemas.js";
import { managedSourceCoverage } from "./managedApplicationGraphCoverage.js";
import type { Evidence } from "../evidence.js";
import {
type ManagedArtifactInspection,
type ManagedMemberInspection,
type ManagedNativeBoundaryInspection,
} from "./managedArtifact.js";
@ -14,7 +16,6 @@ import type {
GraphBuildState,
ParsedManagedGraphInput,
} from "./managedApplicationGraph.js";
import { moduleCoverageState } from "./managedApplicationGraphNodeCoverage.js";
type ManagedMethod = ManagedMemberInspection["methods"][number];
type ManagedField = ManagedMemberInspection["fields"][number];
type ManagedType = ManagedMemberInspection["types"][number];
@ -44,7 +45,7 @@ export const addArtifactNode = (state: GraphBuildState): ApplicationNode => {
format: "pe",
source: "managed-application-graph",
},
evidence: managedEvidence(state, "project-managed-artifact"),
evidence: projectedManagedEvidence(state, "project-managed-artifact"),
},
],
});
@ -58,47 +59,48 @@ export const addArtifactIdentityNodes = (
artifactNode: ApplicationNode,
parsed: ParsedManagedGraphInput,
): void => {
const artifact = parsed.artifact?.result;
const artifactCoverage = managedSourceCoverage(
parsed.artifact?.result.coverage.state ?? "complete",
);
if (artifact?.assembly !== undefined && artifact.assembly !== null) {
const artifactInput = parsed.artifact;
if (artifactInput !== null && artifactInput.result.assembly !== null) {
const assemblyFacts = artifactInput.result.assembly;
const artifactEvidence = sourceManagedEvidence(
state,
artifactInput.evidence,
artifactInput.result.coverage.state,
);
const assembly = createJavaScriptApplicationNode({
kind: "managed-assembly",
identity: artifactLocalIdentity(
state.artifactSha256,
"managed-assembly",
artifact.assembly.token,
assemblyFacts.token,
),
observations: [
{
label: artifact.assembly.name,
label: assemblyFacts.name,
properties: {
name: artifact.assembly.name,
version: artifact.assembly.version,
culture: artifact.assembly.culture,
public_key_kind: artifact.assembly.public_key.kind,
name: assemblyFacts.name,
version: assemblyFacts.version,
culture: assemblyFacts.culture,
public_key_kind: assemblyFacts.public_key.kind,
},
evidence: managedEvidence(
state,
"inspect_managed_artifact",
artifactCoverage,
),
evidence: artifactEvidence,
},
],
});
state.nodes.push(assembly);
addContainsEdge(state, artifactNode, assembly, {
kind: "declares-managed-assembly",
coverage: artifactCoverage,
coverage: artifactEvidence.coverage,
});
}
const module =
artifact?.module ??
parsed.members?.result.module ??
parsed.boundaries?.result.module;
if (module !== undefined && module !== null) {
const moduleCoverage = managedSourceCoverage(moduleCoverageState(parsed));
const moduleSource = selectModuleSource(parsed);
if (moduleSource !== null) {
const { module } = moduleSource;
const moduleEvidence = sourceManagedEvidence(
state,
moduleSource.evidence,
moduleSource.coverageState,
);
const moduleNode = createJavaScriptApplicationNode({
kind: "managed-module",
identity: artifactLocalIdentity(
@ -115,36 +117,68 @@ export const addArtifactIdentityNodes = (
generation: module.generation,
token: module.token,
},
evidence: managedEvidence(
state,
"inspect_managed_artifact",
moduleCoverage,
),
evidence: moduleEvidence,
},
],
});
state.nodes.push(moduleNode);
addContainsEdge(state, artifactNode, moduleNode, {
kind: "declares-managed-module",
coverage: moduleCoverage,
coverage: moduleEvidence.coverage,
});
}
};
interface ManagedModuleSource {
readonly module: NonNullable<ManagedArtifactInspection["module"]>;
readonly evidence: Evidence;
readonly coverageState: ManagedArtifactInspection["coverage"]["state"];
}
const selectModuleSource = (
parsed: ParsedManagedGraphInput,
): ManagedModuleSource | null => {
if (parsed.artifact?.result.module != null)
return {
module: parsed.artifact.result.module,
evidence: parsed.artifact.evidence,
coverageState: parsed.artifact.result.coverage.state,
};
if (parsed.members?.result.module != null)
return {
module: parsed.members.result.module,
evidence: parsed.members.evidence,
coverageState: parsed.members.result.coverage.state,
};
if (parsed.boundaries?.result.module != null)
return {
module: parsed.boundaries.result.module,
evidence: parsed.boundaries.evidence,
coverageState: parsed.boundaries.result.coverage.state,
};
return null;
};
/** Add managed type, method, and field nodes from member evidence. */
export const addMemberNodes = (
state: GraphBuildState,
artifactNode: ApplicationNode,
parsed: ParsedManagedGraphInput,
) => {
const members = parsed.members?.result;
if (members === undefined) return { types: 0, methods: 0, fields: 0 };
): void => {
const memberInput = parsed.members;
if (memberInput === null) return;
const members = memberInput.result;
const { types, methods, fields } = members;
const typeCoverage = managedSourceCoverage(members.coverage.state);
const methodCoverage = managedSourceCoverage(members.coverage.state);
const fieldCoverage = managedSourceCoverage(members.coverage.state);
const sourceEvidence = sourceManagedEvidence(
state,
memberInput.evidence,
members.coverage.state,
);
for (const type of types) {
const node = typeNode(state, type, typeCoverage);
const node = typeNode(state, type, sourceEvidence);
state.nodes.push(node);
state.typeNodes.set(type.token, node);
addContainsEdge(state, artifactNode, node, {
@ -153,7 +187,7 @@ export const addMemberNodes = (
});
}
for (const method of methods) {
const node = methodNode(state, method, methodCoverage);
const node = methodNode(state, method, sourceEvidence);
state.nodes.push(node);
state.methodNodes.set(method.token, node);
const owner =
@ -166,7 +200,7 @@ export const addMemberNodes = (
});
}
for (const field of fields) {
const node = fieldNode(state, field, fieldCoverage);
const node = fieldNode(state, field, sourceEvidence);
state.nodes.push(node);
state.fieldNodes.set(field.token, node);
const owner =
@ -178,18 +212,13 @@ export const addMemberNodes = (
coverage: fieldCoverage,
});
}
return {
types: types.length,
methods: methods.length,
fields: fields.length,
};
};
/** Build a managed type application node. */
const typeNode = (
state: GraphBuildState,
type: ManagedType,
coverage: ApplicationGraphEvidence["coverage"],
evidence: ApplicationGraphEvidence,
): ApplicationNode =>
createJavaScriptApplicationNode({
kind: "managed-type",
@ -209,7 +238,7 @@ const typeNode = (
flags: type.flags,
extends_token: type.extends_token,
},
evidence: managedEvidence(state, "inspect_managed_members", coverage),
evidence,
},
],
});
@ -218,7 +247,7 @@ const typeNode = (
const methodNode = (
state: GraphBuildState,
method: ManagedMethod,
coverage: ApplicationGraphEvidence["coverage"],
evidence: ApplicationGraphEvidence,
): ApplicationNode =>
createJavaScriptApplicationNode({
kind: "managed-method",
@ -245,7 +274,7 @@ const methodNode = (
normalized_il_sha256: method.body.normalized_il_sha256,
il_size: method.body.il_size,
},
evidence: managedEvidence(state, "inspect_managed_members", coverage),
evidence,
},
],
});
@ -254,7 +283,7 @@ const methodNode = (
const fieldNode = (
state: GraphBuildState,
field: ManagedField,
coverage: ApplicationGraphEvidence["coverage"],
evidence: ApplicationGraphEvidence,
): ApplicationNode =>
createJavaScriptApplicationNode({
kind: "managed-field",
@ -278,7 +307,7 @@ const fieldNode = (
signature_sha256: field.signature.raw_sha256,
signature_status: field.signature.parse_status,
},
evidence: managedEvidence(state, "inspect_managed_members", coverage),
evidence,
},
],
});
@ -288,23 +317,33 @@ export const addBoundaryNodes = (
state: GraphBuildState,
artifactNode: ApplicationNode,
parsed: ParsedManagedGraphInput,
) => {
const boundaries = parsed.boundaries?.result;
if (boundaries === undefined)
return { pinvoke_imports: 0, native_implementations: 0 };
): void => {
const boundaryInput = parsed.boundaries;
if (boundaryInput === null) return;
const boundaries = boundaryInput.result;
const { pinvoke_imports: pinvokes, native_implementations: implementations } =
boundaries;
const pinvokeCoverage = managedSourceCoverage(boundaries.coverage.state);
const implementationCoverage = managedSourceCoverage(
boundaries.coverage.state,
);
const sourceEvidence = sourceManagedEvidence(
state,
boundaryInput.evidence,
boundaries.coverage.state,
);
for (const pinvoke of pinvokes) {
const node = pinvokeNode(state, pinvoke, pinvokeCoverage);
const node = pinvokeNode(state, pinvoke, sourceEvidence);
state.nodes.push(node);
const owner =
const method =
pinvoke.member_token === null
? artifactNode
: (state.methodNodes.get(pinvoke.member_token) ?? artifactNode);
? undefined
: state.methodNodes.get(pinvoke.member_token);
const memberEvidence =
method === undefined || parsed.members === null
? undefined
: parsed.members.evidence;
const owner = method ?? artifactNode;
state.edges.push(
createJavaScriptApplicationEdge({
source_node_id: owner.node_id,
@ -315,10 +354,11 @@ export const addBoundaryNodes = (
import_name: pinvoke.import_name,
import_scope_name: pinvoke.import_scope_name,
},
evidence: managedEvidence(
evidence: pinvokeAssociationEvidence(
state,
"inspect_managed_native_boundaries",
boundaryInput.evidence,
pinvokeCoverage,
memberEvidence,
),
}),
);
@ -327,7 +367,7 @@ export const addBoundaryNodes = (
const node = nativeImplementationNode(
state,
implementation,
implementationCoverage,
sourceEvidence,
);
state.nodes.push(node);
const owner = state.methodNodes.get(implementation.token) ?? artifactNode;
@ -336,17 +376,13 @@ export const addBoundaryNodes = (
coverage: implementationCoverage,
});
}
return {
pinvoke_imports: pinvokes.length,
native_implementations: implementations.length,
};
};
/** Build a managed P/Invoke import application node. */
const pinvokeNode = (
state: GraphBuildState,
pinvoke: PinvokeImport,
coverage: ApplicationGraphEvidence["coverage"],
evidence: ApplicationGraphEvidence,
): ApplicationNode =>
createJavaScriptApplicationNode({
kind: "managed-pinvoke-import",
@ -368,11 +404,7 @@ const pinvokeNode = (
call_convention: pinvoke.call_convention,
verification: pinvoke.verification,
},
evidence: managedEvidence(
state,
"inspect_managed_native_boundaries",
coverage,
),
evidence,
},
],
});
@ -381,7 +413,7 @@ const pinvokeNode = (
const nativeImplementationNode = (
state: GraphBuildState,
implementation: NativeImplementation,
coverage: ApplicationGraphEvidence["coverage"],
evidence: ApplicationGraphEvidence,
): ApplicationNode =>
createJavaScriptApplicationNode({
kind: "managed-native-implementation",
@ -403,11 +435,7 @@ const nativeImplementationNode = (
boundary_kind: implementation.boundary_kind,
body_interpretation: implementation.body_interpretation,
},
evidence: managedEvidence(
state,
"inspect_managed_native_boundaries",
coverage,
),
evidence,
},
],
});
@ -425,7 +453,7 @@ const addContainsEdge = (
target_node_id: target.node_id,
relation: "contains",
properties: { kind: options.kind },
evidence: managedEvidence(
evidence: projectedManagedEvidence(
state,
"project-managed-contains",
options.coverage,
@ -448,12 +476,43 @@ const artifactLocalIdentity = (
});
/** Build managed static-analysis evidence for a graph observation. */
const managedEvidence = (
const sourceManagedEvidence = (
state: GraphBuildState,
source: Evidence,
coverageState: ManagedArtifactInspection["coverage"]["state"],
): ApplicationGraphEvidence =>
createManagedEvidence(
state,
source.operation,
managedSourceCoverage(coverageState),
[source.evidence_id],
);
const projectedManagedEvidence = (
state: GraphBuildState,
operation: string,
coverage: ApplicationGraphEvidence["coverage"] = managedSourceCoverage(
"complete",
),
): ApplicationGraphEvidence =>
createManagedEvidence(state, operation, coverage, state.evidenceLinks);
const pinvokeAssociationEvidence = (
state: GraphBuildState,
boundaryEvidence: Evidence,
coverage: ApplicationGraphEvidence["coverage"],
memberEvidence: Evidence | undefined,
): ApplicationGraphEvidence =>
createManagedEvidence(state, boundaryEvidence.operation, coverage, [
boundaryEvidence.evidence_id,
...(memberEvidence === undefined ? [] : [memberEvidence.evidence_id]),
]);
const createManagedEvidence = (
state: GraphBuildState,
operation: string,
coverage: ApplicationGraphEvidence["coverage"],
evidenceIds: readonly string[],
): ApplicationGraphEvidence => ({
authority: "managed-static-analysis",
state: "observed",
@ -483,7 +542,7 @@ const managedEvidence = (
: [
"The source managed Evidence slice is incomplete; unreturned or unavailable items remain unobserved.",
],
evidence_ids: state.evidenceLinks,
evidence_ids: [...evidenceIds],
});
/** Sanitize an artifact path into a managed graph location. */

View File

@ -16,43 +16,51 @@ import {
managedPeFixtureTarget,
} from "../../../src/dotnet/ManagedPe.fixture.js";
describe("managed application graph projection", () => {
it("projects managed metadata and native declarations into authenticated graph Evidence", () => {
const bytes = buildManagedPeFixture({
pinvoke: {
moduleName: "user32.dll",
importName: "MessageBoxW",
mappingFlags: 0x0345,
},
});
const binary = managedPeFixtureTarget(bytes, "/fixture/ManagedInterop.exe");
const managedArtifact = inspectManagedArtifactBytes(bytes, binary);
const members = inspectManagedMembersBytes(bytes, binary);
const boundaries = inspectManagedNativeBoundariesBytes(bytes, binary);
const artifactEvidence = createEvidence(binary, MANAGED_STATIC_PROVIDER, {
const createManagedInteropEvidence = () => {
const bytes = buildManagedPeFixture({
pinvoke: {
moduleName: "user32.dll",
importName: "MessageBoxW",
mappingFlags: 0x0345,
},
});
const binary = managedPeFixtureTarget(bytes, "/fixture/ManagedInterop.exe");
const managedArtifact = inspectManagedArtifactBytes(bytes, binary);
const members = inspectManagedMembersBytes(bytes, binary);
const boundaries = inspectManagedNativeBoundariesBytes(bytes, binary);
return {
artifactEvidence: createEvidence(binary, MANAGED_STATIC_PROVIDER, {
operation: "inspect_managed_artifact",
parameters: {},
result: managedArtifact,
rawResult: null,
limitations: managedArtifact.limitations,
locations: [{ kind: "artifact-path", path: binary.path }],
});
const memberEvidence = createEvidence(binary, MANAGED_STATIC_PROVIDER, {
}),
memberEvidence: createEvidence(binary, MANAGED_STATIC_PROVIDER, {
operation: "inspect_managed_members",
parameters: {},
result: members,
rawResult: null,
limitations: members.limitations,
locations: [{ kind: "artifact-path", path: binary.path }],
});
const boundaryEvidence = createEvidence(binary, MANAGED_STATIC_PROVIDER, {
}),
boundaryEvidence: createEvidence(binary, MANAGED_STATIC_PROVIDER, {
operation: "inspect_managed_native_boundaries",
parameters: {},
result: boundaries,
rawResult: null,
limitations: boundaries.limitations,
locations: [{ kind: "artifact-path", path: binary.path }],
});
}),
};
};
describe("managed application graph projection", () => {
it("projects managed metadata and native declarations into authenticated graph Evidence", () => {
const { artifactEvidence, memberEvidence, boundaryEvidence } =
createManagedInteropEvidence();
const evidence = projectManagedApplicationGraphEvidence({
managed_artifact: artifactEvidence,
@ -103,21 +111,51 @@ describe("managed application graph projection", () => {
"managed-pinvoke-import",
]),
);
const allEvidenceIds = [
artifactEvidence.evidence_id,
memberEvidence.evidence_id,
boundaryEvidence.evidence_id,
].sort();
const observedEvidenceIds = (kind: string): readonly string[] => {
const observation = graph.nodes.find((node) => node.kind === kind)
?.observations[0];
if (observation === undefined)
throw new Error(`Expected ${kind} observation`);
return observation.evidence.evidence_ids;
};
expect(observedEvidenceIds("artifact")).toEqual(allEvidenceIds);
expect(observedEvidenceIds("managed-assembly")).toEqual([
artifactEvidence.evidence_id,
]);
expect(observedEvidenceIds("managed-module")).toEqual([
artifactEvidence.evidence_id,
]);
for (const kind of ["managed-type", "managed-method", "managed-field"])
expect(observedEvidenceIds(kind)).toEqual([memberEvidence.evidence_id]);
expect(observedEvidenceIds("managed-pinvoke-import")).toEqual([
boundaryEvidence.evidence_id,
]);
for (const edge of graph.edges.filter(
({ relation }) => relation === "contains",
))
expect(edge.evidence.evidence_ids).toEqual(allEvidenceIds);
const method = graph.nodes.find(
({ kind, observations }) =>
kind === "managed-method" &&
observations[0]?.label === "Fixture.Program.Main",
);
expect(method).toBeDefined();
expect(
graph.edges.some(
({ source_node_id, relation, target_node_id }) =>
source_node_id === method?.node_id &&
relation === "imports" &&
graph.nodes.find(({ node_id: id }) => id === target_node_id)?.kind ===
"managed-pinvoke-import",
),
).toBe(true);
const importEdge = graph.edges.find(
({ source_node_id, relation, target_node_id }) =>
source_node_id === method?.node_id &&
relation === "imports" &&
graph.nodes.find(({ node_id: id }) => id === target_node_id)?.kind ===
"managed-pinvoke-import",
);
expect(importEdge).toBeDefined();
expect(importEdge?.evidence.evidence_ids).toEqual(
[memberEvidence.evidence_id, boundaryEvidence.evidence_id].sort(),
);
expect(
graph.nodes.flatMap(({ observations }) =>
observations.map(({ evidence }) => evidence.authority),
@ -167,7 +205,7 @@ describe("managed application graph request", () => {
});
describe("managed application graph coverage", () => {
it("rejects managed Evidence digest mismatch and discloses missing artifact observations", () => {
it("rejects managed Evidence whose artifact digest differs from its subject", () => {
const bytes = buildManagedPeFixture();
const binary = managedPeFixtureTarget(bytes, "/fixture/Mismatch.dll");
const members = inspectManagedMembersBytes(bytes, binary);
@ -191,7 +229,14 @@ describe("managed application graph coverage", () => {
expect(JSON.stringify(rejected.error)).toContain(
`Managed Evidence inspect_managed_members (${mismatched.evidence_id}) subject SHA-256 ${binary.sha256} does not match normalized artifact SHA-256 ${"b".repeat(64)}`,
);
});
});
describe("managed application graph identity facts", () => {
it("uses member Evidence for its module and reports only emitted identity nodes", () => {
const bytes = buildManagedPeFixture();
const binary = managedPeFixtureTarget(bytes, "/fixture/MembersOnly.dll");
const members = inspectManagedMembersBytes(bytes, binary);
const aligned = createEvidence(binary, MANAGED_STATIC_PROVIDER, {
operation: "inspect_managed_members",
parameters: {},
@ -204,11 +249,92 @@ describe("managed application graph coverage", () => {
const result = managedApplicationGraphResultSchema.parse(
parseEvidence(projected.value).normalized_result,
);
const graph = parseJavaScriptApplicationGraph(result.graph);
expect(result.summary).toMatchObject({ assemblies: 0, modules: 1 });
expect(
graph.nodes.find(({ kind }) => kind === "managed-module")
?.observations[0],
).toMatchObject({
evidence: {
extractor: { operation: "inspect_managed_members" },
evidence_ids: [aligned.evidence_id],
},
});
expect(result.evidence_links).toEqual([aligned.evidence_id]);
expect(result.limitations).toContain(
"Managed artifact Evidence was not supplied; assembly identity observations are absent.",
);
});
it("binds a boundaries-only module node to the boundary Evidence that supplied it", () => {
const bytes = buildManagedPeFixture();
const binary = managedPeFixtureTarget(bytes, "/fixture/BoundariesOnly.dll");
const boundaries = inspectManagedNativeBoundariesBytes(bytes, binary);
const boundaryEvidence = createEvidence(binary, MANAGED_STATIC_PROVIDER, {
operation: "inspect_managed_native_boundaries",
parameters: {},
result: boundaries,
rawResult: null,
limitations: boundaries.limitations,
locations: [{ kind: "artifact-path", path: binary.path }],
});
const projected = projectManagedApplicationGraphEvidence({
managed_native_boundaries: boundaryEvidence,
});
if (!projected.ok) throw projected.error;
const result = managedApplicationGraphResultSchema.parse(
parseEvidence(projected.value).normalized_result,
);
const graph = parseJavaScriptApplicationGraph(result.graph);
expect(result.summary).toMatchObject({ assemblies: 0, modules: 1 });
expect(
graph.nodes.find(({ kind }) => kind === "managed-module")
?.observations[0],
).toMatchObject({
evidence: {
extractor: { operation: "inspect_managed_native_boundaries" },
evidence_ids: [boundaryEvidence.evidence_id],
},
});
expect(result.evidence_links).toEqual([boundaryEvidence.evidence_id]);
});
it("reports no assembly or module when the inspector observes no identity rows", () => {
const bytes = buildManagedPeFixture({
moduleRowCount: 0,
assemblyRowCount: 0,
});
const binary = managedPeFixtureTarget(bytes, "/fixture/NoIdentityRows.exe");
const inspection = inspectManagedArtifactBytes(bytes, binary);
expect(inspection).toMatchObject({ module: null, assembly: null });
const artifactEvidence = createEvidence(binary, MANAGED_STATIC_PROVIDER, {
operation: "inspect_managed_artifact",
parameters: {},
result: inspection,
rawResult: null,
limitations: inspection.limitations,
locations: [{ kind: "artifact-path", path: binary.path }],
});
const projected = projectManagedApplicationGraphEvidence({
managed_artifact: artifactEvidence,
});
if (!projected.ok) throw projected.error;
const result = managedApplicationGraphResultSchema.parse(
parseEvidence(projected.value).normalized_result,
);
const graph = parseJavaScriptApplicationGraph(result.graph);
expect(result.summary).toMatchObject({ assemblies: 0, modules: 0 });
expect(
graph.nodes.filter(
({ kind }) => kind === "managed-assembly" || kind === "managed-module",
),
).toEqual([]);
});
});
describe("managed application graph coverage", () => {
it("preserves partial parser coverage in graph and per-fact coverage", () => {
const bytes = buildManagedPeFixture();
const binary = managedPeFixtureTarget(bytes, "/fixture/ManagedInterop.exe");