[GH-ISSUE #6130] [Bug] Pcap parent validation omits the analysis-view source prefix #3758

Closed
opened 2026-10-31 00:43:44 +00:00 by bsakaguchi · 1 comment
Owner

Originally created by @coygeek on GitHub (Oct 21, 2026).
Original GitHub issue: https://github.com/morluto/rea/issues/6130

Before submitting

  • I searched the open and closed issues for a related report.
  • I removed credentials, tokens, private paths, and sensitive target data.

Area

MCP or agent integration

Observed behavior

MCP inspect_analysis_view rejects digest-valid inspect_pcap_capture Evidence whose result has target.path: 123, but returns details.issues[0].path: ["target","path"]. Both inline and retained-evidence sources reproduce this. The established analysis-view parent convention would locate the issue at ["source","normalized_result","target","path"].

The same report with a string path succeeds. Classification remains invalid_request / invalid_input, with retryable: false.

Reproduction

After installing locked dependencies and running npm run build:cached, save the following as tests/boundary/mcp/pcapParentIssuePath.test.ts and run:

node node_modules/vitest/vitest.mjs run tests/boundary/mcp/pcapParentIssuePath.test.ts --maxWorkers=1

This executed the registered MCP operation through the SDK in-memory transport. The repository's simulated session supplies Evidence storage; its provider factory throws if invoked. The normalized pcap report is synthetic, and createEvidence seals each variant independently. No packet file or external decoder is accessed. The assertions record the current incorrect behavior.

import { Client, InMemoryTransport } from "@modelcontextprotocol/client";
import { expect, it } from "vitest";
import { createEvidence } from "../../../src/domain/evidence.js";
import { createServer } from "../../../src/server/createServer.js";
import { createTestBinarySession } from "../../fixtures/binarySession.js";
import { parseMcpToolError } from "../../fixtures/mcpToolError.js";

it("observes pcap parent diagnostic paths at the MCP boundary", async () => {
  const target = { path: "example.pcap", sha256: "a".repeat(64), bytes: 24 };
  const report = {
    client: {
      tshark_command: "tshark", tshark_source: "path", tshark_version: "4.6.6",
      capinfos_command: "capinfos", capinfos_source: "path", capinfos_version: "4.6.6",
    },
    target, file_type: "pcap", encapsulation: "Ethernet", packet_count: 0,
    capture_start: null, capture_end: null, capture_duration_seconds: null, snapshot_length: null,
    endpoints: [], endpoint_count: 0, dns_queries: [], dns_query_count: 0,
    tls_sni_values: [], tls_sni_count: 0, protocol_distribution: [], protocol_count: 0,
    http_request_uris: [], http_request_uri_count: 0, http_uri_redaction_count: 0,
    unattributed_dns_addresses: [], unattributed_dns_address_count: 0,
    unattributed_dns_packet_count: 0, coverage: "complete",
    diagnostics: { tshark_stderr: "", capinfos_stderr: "" },
  };
  const parent = (result: typeof report | Record<string, unknown>) => createEvidence(
    { ...target, format: "file" },
    { id: "historical-pcap", name: "Historical packet capture", version: "1" },
    { operation: "inspect_pcap_capture", parameters: {}, result, confidence: "derived", authority: "historical-reference" },
  );
  const session = createTestBinarySession(() => { throw new Error("provider must not run"); });
  const server = createServer({ kind: "session", session });
  const client = new Client({ name: "pcap-parent-path-repro", version: "1" });
  try {
    const [clientTransport, serverTransport] = InMemoryTransport.createLinkedPair();
    await server.connect(serverTransport);
    await client.connect(clientTransport);
    const valid = await client.callTool({ name: "inspect_analysis_view", arguments: {
      source: { kind: "inline", evidence: parent(report) }, view: { kind: "summary" },
    } });
    expect(valid.isError).not.toBe(true);
    console.log("VALID_PARENT", JSON.stringify({ isError: valid.isError ?? false }));
    const malformed = parent({ ...report, target: { ...target, path: 123 } });
    const recorded = session.recordEvidence(malformed);
    if (!recorded.ok) throw recorded.error;
    for (const source of [
      { kind: "inline", evidence: malformed },
      { kind: "retained-evidence", evidence_id: malformed.evidence_id },
    ]) {
      const result = await client.callTool({ name: "inspect_analysis_view", arguments: { source, view: { kind: "summary" } } });
      const diagnostic = parseMcpToolError(result);
      console.log(source.kind, JSON.stringify(diagnostic));
      expect(result.isError).toBe(true);
      expect(diagnostic).toMatchObject({ error: {
        code: "invalid_request", category: "invalid_input", retryable: false,
        details: { issues: [{ path: ["target", "path"], reason: "invalid_type", expected: "string" }] },
      } });
    }
    await client.ping();
  } finally {
    await client.close();
    await server.close();
    await session.close();
  }
});

Executed once: one successful valid-parent control, two malformed-parent rejections, and a successful subsequent ping. The focused test passed.

Expected behavior

Preserve the owning-schema issue beneath source.normalized_result, including its field path, reason and expected type. The shared parent parser explicitly defines this convention, and existing MCP coverage expects it for another parent operation.

This is the existing logical location of a resolved parent, rather than the literal inline JSON path: inline input has an additional evidence wrapper, while retained input contains only an Evidence ID. No new path convention is requested.

Impact and scope

Incorrect structured correction metadata for malformed pcap parent data. Both tested source kinds are affected; the valid control succeeds and malformed data remains rejected. The diagnostic still identifies the relative member and expected type, so callers can inspect the parent directly. No invalid success, data loss, valid-input failure or actual downstream recovery failure was observed. Frequency in normal use is unknown.

Executed scope is summary projection over synthetic pcap Evidence through MCP. The CLI, Evidence-file source and real capture production were not exercised. This report does not claim that the capture producer emits malformed results.

REA version

01788bac863ac786bfbb648cad69b11e23b40a89 (package version 6.7.0).

Runtime and platform

Node.js v24.18.0; npm 11.16.0; macOS 27.0.1 arm64; locked Vitest 5.0.3.

Target and artifact context

Synthetic, digest-valid historical inspect_pcap_capture Evidence describing an empty capture. The malformed variant differs only at normalized-result target.path.

Interface

MCP

Earliest failure stage

Request parsing or validation

Relevant output or evidence

Both malformed requests returned these actual fields:

{
  "error": {
    "code": "invalid_request",
    "category": "invalid_input",
    "retryable": false,
    "recovery": {
      "kind": "inspect_input_contract",
      "operation": "inspect_analysis_view"
    },
    "details": {
      "operation": "inspect_analysis_view",
      "issues": [
        {"path":["target","path"],"reason":"invalid_type","expected":"string"}
      ]
    }
  }
}

The valid-parent control logged {"isError":false}; one test in one file passed. These are public MCP results, not only a direct parser call.

Typed outcome or recovery expected

Keep the refusal, invalid_request / invalid_input, retryable: false, and inspect_input_contract recovery for inspect_analysis_view. For this malformed field, return path: ["source","normalized_result","target","path"], reason: "invalid_type", and expected: "string".

Analysis or suspected cause

Source-derived: the pcap parent parser sends raw schema issues and the unwrapped result into the error projector. It does not rebase them through the shared parent parser. The web-network capture sibling already supplies the established prefix; its summary path is not the failing producer reported here.

Additional context

Closed, unmerged PR #3122 proposed this exact pcap prefix fix and a shared-parser refactor; the defect still reproduces on the revision above.

Related reports concern different producers: #6001 covers incompatible artifact view selection, #6003 covers binary-layout parent parsing, #6034 covers artifact inventory parent parsing, and #6044 covers HAR grouping admission. None is this pcap raw-issue path.

Originally created by @coygeek on GitHub (Oct 21, 2026). Original GitHub issue: https://github.com/morluto/rea/issues/6130 ### Before submitting - [x] I searched the open and closed issues for a related report. - [x] I removed credentials, tokens, private paths, and sensitive target data. ### Area MCP or agent integration ### Observed behavior MCP `inspect_analysis_view` rejects digest-valid `inspect_pcap_capture` Evidence whose result has `target.path: 123`, but returns `details.issues[0].path: ["target","path"]`. Both inline and retained-evidence sources reproduce this. The established analysis-view parent convention would locate the issue at `["source","normalized_result","target","path"]`. The same report with a string path succeeds. Classification remains `invalid_request` / `invalid_input`, with `retryable: false`. ### Reproduction After installing locked dependencies and running `npm run build:cached`, save the following as `tests/boundary/mcp/pcapParentIssuePath.test.ts` and run: ```sh node node_modules/vitest/vitest.mjs run tests/boundary/mcp/pcapParentIssuePath.test.ts --maxWorkers=1 ``` This executed the registered MCP operation through the SDK in-memory transport. The repository's simulated session supplies Evidence storage; its provider factory throws if invoked. The normalized pcap report is synthetic, and `createEvidence` seals each variant independently. No packet file or external decoder is accessed. The assertions record the current incorrect behavior. ```ts import { Client, InMemoryTransport } from "@modelcontextprotocol/client"; import { expect, it } from "vitest"; import { createEvidence } from "../../../src/domain/evidence.js"; import { createServer } from "../../../src/server/createServer.js"; import { createTestBinarySession } from "../../fixtures/binarySession.js"; import { parseMcpToolError } from "../../fixtures/mcpToolError.js"; it("observes pcap parent diagnostic paths at the MCP boundary", async () => { const target = { path: "example.pcap", sha256: "a".repeat(64), bytes: 24 }; const report = { client: { tshark_command: "tshark", tshark_source: "path", tshark_version: "4.6.6", capinfos_command: "capinfos", capinfos_source: "path", capinfos_version: "4.6.6", }, target, file_type: "pcap", encapsulation: "Ethernet", packet_count: 0, capture_start: null, capture_end: null, capture_duration_seconds: null, snapshot_length: null, endpoints: [], endpoint_count: 0, dns_queries: [], dns_query_count: 0, tls_sni_values: [], tls_sni_count: 0, protocol_distribution: [], protocol_count: 0, http_request_uris: [], http_request_uri_count: 0, http_uri_redaction_count: 0, unattributed_dns_addresses: [], unattributed_dns_address_count: 0, unattributed_dns_packet_count: 0, coverage: "complete", diagnostics: { tshark_stderr: "", capinfos_stderr: "" }, }; const parent = (result: typeof report | Record<string, unknown>) => createEvidence( { ...target, format: "file" }, { id: "historical-pcap", name: "Historical packet capture", version: "1" }, { operation: "inspect_pcap_capture", parameters: {}, result, confidence: "derived", authority: "historical-reference" }, ); const session = createTestBinarySession(() => { throw new Error("provider must not run"); }); const server = createServer({ kind: "session", session }); const client = new Client({ name: "pcap-parent-path-repro", version: "1" }); try { const [clientTransport, serverTransport] = InMemoryTransport.createLinkedPair(); await server.connect(serverTransport); await client.connect(clientTransport); const valid = await client.callTool({ name: "inspect_analysis_view", arguments: { source: { kind: "inline", evidence: parent(report) }, view: { kind: "summary" }, } }); expect(valid.isError).not.toBe(true); console.log("VALID_PARENT", JSON.stringify({ isError: valid.isError ?? false })); const malformed = parent({ ...report, target: { ...target, path: 123 } }); const recorded = session.recordEvidence(malformed); if (!recorded.ok) throw recorded.error; for (const source of [ { kind: "inline", evidence: malformed }, { kind: "retained-evidence", evidence_id: malformed.evidence_id }, ]) { const result = await client.callTool({ name: "inspect_analysis_view", arguments: { source, view: { kind: "summary" } } }); const diagnostic = parseMcpToolError(result); console.log(source.kind, JSON.stringify(diagnostic)); expect(result.isError).toBe(true); expect(diagnostic).toMatchObject({ error: { code: "invalid_request", category: "invalid_input", retryable: false, details: { issues: [{ path: ["target", "path"], reason: "invalid_type", expected: "string" }] }, } }); } await client.ping(); } finally { await client.close(); await server.close(); await session.close(); } }); ``` Executed once: one successful valid-parent control, two malformed-parent rejections, and a successful subsequent ping. The focused test passed. ### Expected behavior Preserve the owning-schema issue beneath `source.normalized_result`, including its field path, reason and expected type. The [shared parent parser](https://github.com/morluto/rea/blob/01788bac863ac786bfbb648cad69b11e23b40a89/src/domain/analysisView/analysisViewParent.ts#L7-L25) explicitly defines this convention, and [existing MCP coverage](https://github.com/morluto/rea/blob/01788bac863ac786bfbb648cad69b11e23b40a89/tests/boundary/mcp/wasmArtifactMcp.test.ts#L568-L580) expects it for another parent operation. This is the existing logical location of a resolved parent, rather than the literal inline JSON path: inline input has an additional `evidence` wrapper, while retained input contains only an Evidence ID. No new path convention is requested. ### Impact and scope Incorrect structured correction metadata for malformed pcap parent data. Both tested source kinds are affected; the valid control succeeds and malformed data remains rejected. The diagnostic still identifies the relative member and expected type, so callers can inspect the parent directly. No invalid success, data loss, valid-input failure or actual downstream recovery failure was observed. Frequency in normal use is unknown. Executed scope is summary projection over synthetic pcap Evidence through MCP. The CLI, Evidence-file source and real capture production were not exercised. This report does not claim that the capture producer emits malformed results. ### REA version `01788bac863ac786bfbb648cad69b11e23b40a89` (package version 6.7.0). ### Runtime and platform Node.js v24.18.0; npm 11.16.0; macOS 27.0.1 arm64; locked Vitest 5.0.3. ### Target and artifact context Synthetic, digest-valid historical `inspect_pcap_capture` Evidence describing an empty capture. The malformed variant differs only at normalized-result `target.path`. ### Interface MCP ### Earliest failure stage Request parsing or validation ### Relevant output or evidence Both malformed requests returned these actual fields: ```json { "error": { "code": "invalid_request", "category": "invalid_input", "retryable": false, "recovery": { "kind": "inspect_input_contract", "operation": "inspect_analysis_view" }, "details": { "operation": "inspect_analysis_view", "issues": [ {"path":["target","path"],"reason":"invalid_type","expected":"string"} ] } } } ``` The valid-parent control logged `{"isError":false}`; one test in one file passed. These are public MCP results, not only a direct parser call. ### Typed outcome or recovery expected Keep the refusal, `invalid_request` / `invalid_input`, `retryable: false`, and `inspect_input_contract` recovery for `inspect_analysis_view`. For this malformed field, return `path: ["source","normalized_result","target","path"]`, `reason: "invalid_type"`, and `expected: "string"`. ### Analysis or suspected cause Source-derived: the [pcap parent parser](https://github.com/morluto/rea/blob/01788bac863ac786bfbb648cad69b11e23b40a89/src/domain/analysisView/pcapCaptureView.ts#L111-L123) sends raw schema issues and the unwrapped result into the error projector. It does not rebase them through the shared parent parser. The [web-network capture sibling](https://github.com/morluto/rea/blob/01788bac863ac786bfbb648cad69b11e23b40a89/src/domain/analysisView/webNetworkCaptureView.ts#L417-L433) already supplies the established prefix; its summary path is not the failing producer reported here. ### Additional context Closed, unmerged PR #3122 proposed this exact pcap prefix fix and a shared-parser refactor; the defect still reproduces on the revision above. Related reports concern different producers: #6001 covers incompatible artifact view selection, #6003 covers binary-layout parent parsing, #6034 covers artifact inventory parent parsing, and #6044 covers HAR grouping admission. None is this pcap raw-issue path.
Author
Owner

@XinwangTao commented on GitHub (Oct 22, 2026):

确认,问题本质是 parent 路径校验按字面匹配、没套用 analysis-view 的 source 前缀约定:同一条报告里字符串 path 能过、数字 path 就挂,说明校验逻辑在 path 类型分支上不一致,而"成功回传 details.issues[0].path 但拒绝它自己指向的位置"会让 MCP 客户端完全没法修正输入。两个修法(#6195/#6196)方向一致,建议合的时候顺手统一:pcap 和 web-network capture 的 sibling 源(#6001/#6003/#6034 那批)最好共用同一个"定位 parent 的规范化器",把 source 前缀规则收敛到一处,避免每个 capture 源各维护一套、下个新源再漏一遍。

<!-- gh-comment-id:6330437541 --> @XinwangTao commented on GitHub (Oct 22, 2026): 确认,问题本质是 parent 路径校验按字面匹配、没套用 analysis-view 的 source 前缀约定:同一条报告里字符串 path 能过、数字 path 就挂,说明校验逻辑在 path 类型分支上不一致,而"成功回传 details.issues[0].path 但拒绝它自己指向的位置"会让 MCP 客户端完全没法修正输入。两个修法(#6195/#6196)方向一致,建议合的时候顺手统一:pcap 和 web-network capture 的 sibling 源(#6001/#6003/#6034 那批)最好共用同一个"定位 parent 的规范化器",把 source 前缀规则收敛到一处,避免每个 capture 源各维护一套、下个新源再漏一遍。
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
morluto/rea#3758
No description provided.