diff --git a/src/domain/analysisView/pcapCaptureView.test.ts b/src/domain/analysisView/pcapCaptureView.test.ts index 6e56ea3e60..48603df3b4 100644 --- a/src/domain/analysisView/pcapCaptureView.test.ts +++ b/src/domain/analysisView/pcapCaptureView.test.ts @@ -74,6 +74,23 @@ const diagnostics = (offset: number, limit: number) => limit, }) as const; +it("reads historical capture Evidence without inventing executable identity", () => { + const result = projectAnalysisView(capture(""), { kind: "summary" }); + if (!result.ok) throw result.error; + if (result.value.kind !== "summary") + throw new Error("Expected a packet summary."); + if (!("client" in result.value.summary)) + throw new Error("Expected packet client facts."); + expect(result.value.summary.client).toMatchObject({ + tshark_resolved_path: null, + tshark_sha256: null, + tshark_identity_status: "unresolved", + capinfos_resolved_path: null, + capinfos_sha256: null, + capinfos_identity_status: "unresolved", + }); +}); + it("pages diagnostics without splitting a surrogate pair", () => { const parent = capture("ab😀cd"); expect(projectAnalysisView(parent, diagnostics(3, 2))).toMatchObject({ diff --git a/src/domain/analysisView/pcapCaptureView.ts b/src/domain/analysisView/pcapCaptureView.ts index c8659dd432..16b045a601 100644 --- a/src/domain/analysisView/pcapCaptureView.ts +++ b/src/domain/analysisView/pcapCaptureView.ts @@ -1,6 +1,6 @@ import { analysisViewInputError } from "./analysisViewErrors.js"; import { z } from "zod"; -import { pcapResultSchemas } from "../pcap/pcapAnalysis.js"; +import { pcapEvidenceSchemas } from "../pcap/pcapAnalysis.js"; import { analysisInputErrorFromIssues } from "../inputIssueProjection.js"; import type { AnalysisError } from "../analysisErrorBase.js"; import { err, ok, type Result } from "../result.js"; @@ -15,7 +15,7 @@ import { } from "./analysisViewCoverage.js"; import { analysisTextPage } from "./analysisViewText.js"; -const captureSchema = pcapResultSchemas.inspect_pcap_capture; +const captureSchema = pcapEvidenceSchemas.inspect_pcap_capture; const fields = captureSchema.shape; const pageShape = { kind: z.literal("pcap"), diff --git a/src/domain/pcap/pcapAnalysis.ts b/src/domain/pcap/pcapAnalysis.ts index c05122712f..5bde8afac3 100644 --- a/src/domain/pcap/pcapAnalysis.ts +++ b/src/domain/pcap/pcapAnalysis.ts @@ -52,6 +52,20 @@ const client = z.strictObject({ capinfos_identity_status: z.enum(["verified", "unresolved"]), }); +/** Defaults for historical report Evidence written before executable identity was recorded. */ +const legacyClientIdentity = client.extend({ + tshark_resolved_path: localPathStringSchema.nullable().default(null), + tshark_sha256: digestSchema.nullable().default(null), + tshark_identity_status: z + .enum(["verified", "unresolved"]) + .default("unresolved"), + capinfos_resolved_path: localPathStringSchema.nullable().default(null), + capinfos_sha256: digestSchema.nullable().default(null), + capinfos_identity_status: z + .enum(["verified", "unresolved"]) + .default("unresolved"), +}); + const endpoint = z.strictObject({ address: z.string().min(1), port: z.number().int().positive().nullable(), @@ -114,3 +128,10 @@ export const pcapResultSchemas = { .optional(), }), } as const; + +/** Read compatibility for retained capture Evidence from earlier REA versions. */ +export const pcapEvidenceSchemas = { + inspect_pcap_capture: pcapResultSchemas.inspect_pcap_capture.extend({ + client: legacyClientIdentity, + }), +} as const; diff --git a/tests/boundary/mcp/pcapMcp.test.ts b/tests/boundary/mcp/pcapMcp.test.ts index dff164ee55..95154eb14e 100644 --- a/tests/boundary/mcp/pcapMcp.test.ts +++ b/tests/boundary/mcp/pcapMcp.test.ts @@ -15,9 +15,15 @@ const clientIdentity = { tshark_command: "/usr/bin/tshark", tshark_source: "path", tshark_version: "TShark (Wireshark) 4.6.6", + tshark_resolved_path: null, + tshark_sha256: null, + tshark_identity_status: "unresolved", capinfos_command: "/usr/bin/capinfos", capinfos_source: "path", capinfos_version: "Capinfos (Wireshark) 4.6.6", + capinfos_resolved_path: null, + capinfos_sha256: null, + capinfos_identity_status: "unresolved", }; it("advertises exact valid pcap schemas and records inline evidence through MCP", async () => {