fix(analysis-view): preserve native relationship coverage

Carry the parent native coverage facts through inline and retained views independently of row-page exhaustion. Preserve uncertainty, optional diagnostics and source Evidence.

Refs #3891, #4263

Co-authored-by: ogutdgn <79801848+ogutdgn@users.noreply.github.com>
This commit is contained in:
morlutoandogutdgn committed 2026-10-16 07:25:22 +08:00
1 parent a05092726f
commit 89dd71f0f0
4 files changed
+130

No files matched your search

@@ -224,6 +224,9 @@ const createAnalysisViewEvidence = (
: "derived",
authority: parent.authority,
limitations: result.limitations,
...(result.kind === "native" && parent.relationship_coverage !== null
? { relationshipCoverage: parent.relationship_coverage }
: {}),
locations: [
...(result.artifact.path.length > 0
? [{ kind: "artifact-path" as const, path: result.artifact.path }]
@@ -17,12 +17,50 @@ import {
analysisViewObjectDenseCapture,
analysisViewJavaScriptEvidence,
analysisViewJavaScriptAnalysisWithSource,
analysisViewNativeEvidence,
} from "../../fixtures/analysisView.js";
import { createEvidence, parseEvidence } from "../../../src/domain/evidence.js";
import { ghidraFunctionDossier } from "../../../src/domain/ghidraValues.fixture.js";
import { createEvidenceBundle } from "../../../src/domain/evidenceBundle.js";
import { inspectEvidenceBundle } from "../../../src/application/investigation/InspectEvidenceBundle.js";
cliTest(
"preserves native relationship coverage independently of exhausted CLI pages",
async ({ cli }) => {
const root = await createTestTempDirectory("rea-native-coverage-cli-");
const parent = analysisViewNativeEvidence({
status: "partial",
limited_directions: ["callees"],
limitations: ["Callees: fixture read limit"],
diagnostics: ["Fixture relationship diagnostic"],
});
const parentBefore = JSON.stringify(parent);
const request = join(root, "request.json");
await writeFile(
request,
JSON.stringify({
source: { kind: "inline", evidence: parent },
view: { kind: "native", facet: "callees", offset: 0, limit: 1 },
}),
);
const response = await cli.run({
arguments: ["inspect-analysis-view", request, "--json"],
});
expect(response.exitCode).toBe(0);
const evidence = parseEvidence(response.json);
expect(evidence.relationship_coverage).toEqual(
parent.relationship_coverage,
);
expect(evidence.normalized_result).toMatchObject({
item: [],
coverage: { total: 0, exhausted: true },
limitations: expect.arrayContaining(["Fixture dossier caveat"]),
});
expect(evidence.evidence_links).toEqual([parent.evidence_id]);
expect(JSON.stringify(parent)).toBe(parentBefore);
},
);
cliTest(
"reads typed module references through the public CLI with canonical Evidence",
async ({ cli }) => {
@@ -37,11 +37,13 @@ import {
analysisViewCaptureFixture,
analysisViewCaptureEvidence,
analysisViewObjectDenseCapture,
analysisViewNativeEvidence,
} from "../../fixtures/analysisView.js";
import { createTestBinarySession } from "../../fixtures/binarySession.js";
import { BINARY_LAYOUT_TEST_PROVIDER } from "../../fixtures/binaryDiagnostics/layout.js";
import { wasmArtifactFixture } from "../../fixtures/wasm/artifact.js";
import { evmInterfaceFixture } from "../../fixtures/evm/interface.js";
import type { RelationshipCoverage } from "../../../src/domain/relationshipCoverage.js";
const connect = async (
binaryLayout?: BinaryLayoutService,
@@ -70,6 +72,72 @@ const connect = async (
return { client, session };
};
it("preserves native relationship coverage through inline and retained MCP views", async () => {
const { client, session } = await connect();
const { ajv, outputSchema } = await advertisedMcpToolSchemas(
client,
"inspect_analysis_view",
);
const coverages: (RelationshipCoverage | undefined)[] = [
{
status: "partial",
limited_directions: ["callees"],
limitations: ["Callees: fixture read limit"],
diagnostics: ["Fixture relationship diagnostic"],
},
{ status: "partial", limitations: ["Historical read limit"] },
{
status: "unknown",
limited_directions: ["callees"],
reason: "Callees not measured",
diagnostics: ["Unknown coverage diagnostic"],
},
{
status: "complete",
diagnostics: ["Only direct references are observed"],
},
{ status: "complete" },
undefined,
];
for (const coverage of coverages) {
const parent = analysisViewNativeEvidence(coverage);
const parentBefore = JSON.stringify(parent);
const recorded = session.recordEvidence(parent);
if (!recorded.ok) throw recorded.error;
const outputs = [];
for (const source of [
{ kind: "inline", evidence: parent },
{ kind: "retained-evidence", evidence_id: parent.evidence_id },
]) {
const response = await client.callTool({
name: "inspect_analysis_view",
arguments: {
source,
view: { kind: "native", facet: "callees", offset: 0, limit: 1 },
},
});
expect(response.isError).not.toBe(true);
expect(
ajv.validate(outputSchema, response.structuredContent),
JSON.stringify(ajv.errors),
).toBe(true);
const evidence = parseEvidence(response.structuredContent);
expect(evidence.relationship_coverage).toEqual(
parent.relationship_coverage,
);
expect(evidence.normalized_result).toMatchObject({
item: [],
coverage: { total: 0, exhausted: true },
limitations: expect.arrayContaining(["Fixture dossier caveat"]),
});
expect(session.evidenceById(evidence.evidence_id)).toEqual(evidence);
outputs.push(evidence);
}
expect(outputs[0]).toEqual(outputs[1]);
expect(JSON.stringify(parent)).toBe(parentBefore);
}
});
it("advertises typed module references and preserves portable/retained Evidence parity", async () => {
const { client, session } = await connect();
const parent = analysisViewJavaScriptEvidence(
+21
View File
@@ -13,6 +13,27 @@ import {
import { createJavaScriptSemanticGraph } from "../../src/domain/javascript/javascriptSemanticGraph.js";
import type { BinaryLayout } from "../../src/domain/native/binaryLayout.js";
import { binaryLayoutSchema } from "../../src/domain/native/binaryLayout.js";
import { ghidraFunctionDossier } from "../../src/domain/ghidraValues.fixture.js";
import { functionDossierSchema } from "../../src/domain/hopperValues.js";
import type { RelationshipCoverage } from "../../src/domain/relationshipCoverage.js";
/** Canonical portable Evidence; no native provider is started by this fixture. */
export const analysisViewNativeEvidence = (
relationshipCoverage?: RelationshipCoverage,
): Evidence =>
createEvidence(
{ path: "/fixtures/native.exe", format: "pe", sha256: "a".repeat(64) },
{ id: "fixture", name: "Fixture", version: "1" },
{
operation: "analyze_function",
parameters: { address: "0x401000" },
result: {
...functionDossierSchema.parse(ghidraFunctionDossier()),
limitations: ["Fixture dossier caveat"],
},
...(relationshipCoverage === undefined ? {} : { relationshipCoverage }),
},
);
const hex = (value: number): string =>
value === 0 ? "0x0" : `0x${value.toString(16)}`;