[GH-ISSUE #6075] [Bug] compare-managed-members reports malformed PE input as unreadable output #3713

Closed
opened 2026-10-31 00:43:18 +00:00 by bsakaguchi · 0 comments
Owner

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

Before submitting

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

Area

Native analysis or decompilation

Observed behavior

compare-managed-members reports a malformed caller-selected PE as unreadable_output / execution_failure, with advice to run rea doctor. The sibling inspect-managed-members command correctly reports the same bytes as invalid_request / invalid_input and explains that the PE section count must be positive.

The packaged CLI reproduced this when the malformed file was either comparison input. Comparing the unchanged managed fixture to itself succeeded. When only the right input was malformed, the failure retained the successfully inspected left Evidence.

Reproduction

With locked dependencies and built runtime artifacts, save the following test in src/dotnet/managedInputs.repro.test.ts and run it from the repository root:

REA_REPRO_DIR="$(mktemp -d)" node node_modules/vitest/vitest.mjs run src/dotnet/managedInputs.repro.test.ts --maxWorkers=1

This is the relevant case and its controls from an executed focused test. It asserts the observed defect, so the observation test passes on the affected revision. CLI child processes, filesystem reads and parsing are real; the assembly fixture is synthetic data and is never executed.

import { spawnSync } from "node:child_process";
import { writeFileSync } from "node:fs";
import { join } from "node:path";
import { expect, it } from "vitest";
import { buildManagedPeFixture } from "./ManagedPe.fixture.js";

const directory = process.env.REA_REPRO_DIR;
if (!directory) throw new Error("Set REA_REPRO_DIR to a writable directory");
const good = join(directory, "assembly.dll");
writeFileSync(good, buildManagedPeFixture());
const run = (...args: string[]) => {
  const result = spawnSync(process.execPath,
    ["scripts/rea.mjs", ...args, "--json"], { encoding: "utf8" });
  expect(result.error).toBeUndefined();
  expect(result.stderr).toBe("");
  const json = JSON.parse(result.stdout);
  console.log(JSON.stringify({ command: args[0], exit: result.status,
    code: json.code ?? "success", category: json.category ?? null,
    partial: json.details?.partial_observation !== undefined }));
  return { exit: result.status, json };
};

it("observes comparison misclassification with both path positions", () => {
  const bad = join(directory!, "malformed-assembly.dll");
  const bytes = buildManagedPeFixture();
  bytes.writeUInt16LE(0, bytes.readUInt32LE(0x3c) + 6);
  writeFileSync(bad, bytes);
  expect(run("compare-managed-members", good, good).exit).toBe(0);
  for (const [side, left, right] of [["left", bad, good], ["right", good, bad]]) {
    const result = run("compare-managed-members", left!, right!);
    expect(result).toMatchObject({ exit: 1, json: {
      code: "unreadable_output", category: "execution_failure", retryable: false,
    } });
    expect(result.json.message).toContain("rea doctor");
    expect(result.json.details?.partial_observation !== undefined)
      .toBe(side === "right");
  }
  expect(run("inspect-managed-members", bad)).toMatchObject({ exit: 1,
    json: { code: "invalid_request", category: "invalid_input", details: {
      issues: [{ path: ["path"], reason: "invalid_format",
        message: expect.stringContaining("PE section count must be positive") }],
    } },
  });
}, 60_000);

Expected behavior

Classify the malformed selected file as caller input, with invalid_format detail identifying the selected path and comparison side. Preserve any already-retained partial Evidence. The managed provider's existing translation already applies this contract to the same reader failure, and the sibling CLI control demonstrates it on identical bytes.

Impact and scope

P2: callers receive an internal-output error and environment-repair advice for correctable malformed input. The typed code/category and missing failing-file attribution can misdirect recovery decisions. The sibling inspection command provides a demonstrated workaround to identify this input defect. No corrupted successful result or downstream automation incident was observed.

Both comparison positions reproduced, with an unchanged-fixture success control. The executed mutation is a zero PE section count; other layout mutations and other platforms were not tested. This concerns path-based CLI comparison, not the MCP operation's Evidence-to-Evidence input.

REA version

rea-agents 6.7.0; source revision 52415975942a4b9c53a817915b313ccc404f280e.

Runtime and platform

macOS 27.0.1 arm64; Node.js v24.18.0 (repository pin); locked Vitest 5.0.3. Initial manual CLI probes also reproduced on Node.js v26.12.0. No external analysis engine.

Target and artifact context

Repository-generated PE/CLI assembly; the malformed copy differs only in NumberOfSections, set to zero. Real regular-file input, no provider simulation or target execution.

Interface

CLI

Earliest failure stage

Native analysis or reconstruction

Relevant output or evidence

Observed CLI results:

compare-managed-members assembly.dll assembly.dll: exit 0
compare-managed-members malformed-assembly.dll assembly.dll:
  exit 1; unreadable_output / execution_failure; no partial observation
compare-managed-members assembly.dll malformed-assembly.dll:
  exit 1; unreadable_output / execution_failure; left partial observation retained
inspect-managed-members malformed-assembly.dll:
  exit 1; invalid_request / invalid_input
  issues[0]: path=["path"], reason="invalid_format"
  reader detail: "PE section count must be positive"

Comparison message: "Analysis returned an unreadable result. Run rea doctor and review the reported details; repeating the unchanged request is not expected to succeed."

Typed outcome or recovery expected

AnalysisInputError / invalid_request, category invalid_input, carrying the malformed-file detail and failed input attribution, with input correction guidance. Keep the existing partial observation when the other side has already succeeded.

Analysis or suspected cause

Source-derived: path comparison calls the member inspector directly. The inspector reads the PE layout before its metadata failure handler. Zero sections throw ManagedReaderFailure, but the comparison catch only recognizes AnalysisError, ZodError and TypeError before constructing AnalysisProtocolError. The sibling provider already translates the reader failure to malformed input; comparison bypasses that boundary.

Additional context

Related: closed #2191, closed PR #2192, and merged PR #2194 concern malformed-layout classification in ManagedStaticProvider; PR #2194 supplied the merged repair. Their repair is present at this revision, but does not change the separate path-comparison catch. #6068 concerns source changes and digest guards rather than a stable malformed file. #5988 and #5990 concern supplied Evidence identities or workflow roles, not raw PE parsing. The other managed cancellation/cleanup and field-path reports were checked and have different producers and repairs.

Fetched upstream main f86206535f8ec71014781f9d432a84b01e183e5d retains the relevant comparison, reader, inspector and sibling provider lines. Newer main was inspected, not executed. No regression range was established.

Originally created by @coygeek on GitHub (Oct 21, 2026). Original GitHub issue: https://github.com/morluto/rea/issues/6075 ### 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 Native analysis or decompilation ### Observed behavior `compare-managed-members` reports a malformed caller-selected PE as `unreadable_output` / `execution_failure`, with advice to run `rea doctor`. The sibling `inspect-managed-members` command correctly reports the same bytes as `invalid_request` / `invalid_input` and explains that the PE section count must be positive. The packaged CLI reproduced this when the malformed file was either comparison input. Comparing the unchanged managed fixture to itself succeeded. When only the right input was malformed, the failure retained the successfully inspected left Evidence. ### Reproduction With locked dependencies and built runtime artifacts, save the following test in `src/dotnet/managedInputs.repro.test.ts` and run it from the repository root: ```sh REA_REPRO_DIR="$(mktemp -d)" node node_modules/vitest/vitest.mjs run src/dotnet/managedInputs.repro.test.ts --maxWorkers=1 ``` This is the relevant case and its controls from an executed focused test. It asserts the observed defect, so the observation test passes on the affected revision. CLI child processes, filesystem reads and parsing are real; the assembly fixture is synthetic data and is never executed. ```ts import { spawnSync } from "node:child_process"; import { writeFileSync } from "node:fs"; import { join } from "node:path"; import { expect, it } from "vitest"; import { buildManagedPeFixture } from "./ManagedPe.fixture.js"; const directory = process.env.REA_REPRO_DIR; if (!directory) throw new Error("Set REA_REPRO_DIR to a writable directory"); const good = join(directory, "assembly.dll"); writeFileSync(good, buildManagedPeFixture()); const run = (...args: string[]) => { const result = spawnSync(process.execPath, ["scripts/rea.mjs", ...args, "--json"], { encoding: "utf8" }); expect(result.error).toBeUndefined(); expect(result.stderr).toBe(""); const json = JSON.parse(result.stdout); console.log(JSON.stringify({ command: args[0], exit: result.status, code: json.code ?? "success", category: json.category ?? null, partial: json.details?.partial_observation !== undefined })); return { exit: result.status, json }; }; it("observes comparison misclassification with both path positions", () => { const bad = join(directory!, "malformed-assembly.dll"); const bytes = buildManagedPeFixture(); bytes.writeUInt16LE(0, bytes.readUInt32LE(0x3c) + 6); writeFileSync(bad, bytes); expect(run("compare-managed-members", good, good).exit).toBe(0); for (const [side, left, right] of [["left", bad, good], ["right", good, bad]]) { const result = run("compare-managed-members", left!, right!); expect(result).toMatchObject({ exit: 1, json: { code: "unreadable_output", category: "execution_failure", retryable: false, } }); expect(result.json.message).toContain("rea doctor"); expect(result.json.details?.partial_observation !== undefined) .toBe(side === "right"); } expect(run("inspect-managed-members", bad)).toMatchObject({ exit: 1, json: { code: "invalid_request", category: "invalid_input", details: { issues: [{ path: ["path"], reason: "invalid_format", message: expect.stringContaining("PE section count must be positive") }], } }, }); }, 60_000); ``` ### Expected behavior Classify the malformed selected file as caller input, with `invalid_format` detail identifying the selected path and comparison side. Preserve any already-retained partial Evidence. The [managed provider's existing translation](https://github.com/morluto/rea/blob/52415975942a4b9c53a817915b313ccc404f280e/src/dotnet/ManagedStaticProvider.ts#L140-L153) already applies this contract to the same reader failure, and the sibling CLI control demonstrates it on identical bytes. ### Impact and scope P2: callers receive an internal-output error and environment-repair advice for correctable malformed input. The typed code/category and missing failing-file attribution can misdirect recovery decisions. The sibling inspection command provides a demonstrated workaround to identify this input defect. No corrupted successful result or downstream automation incident was observed. Both comparison positions reproduced, with an unchanged-fixture success control. The executed mutation is a zero PE section count; other layout mutations and other platforms were not tested. This concerns path-based CLI comparison, not the MCP operation's Evidence-to-Evidence input. ### REA version rea-agents 6.7.0; source revision `52415975942a4b9c53a817915b313ccc404f280e`. ### Runtime and platform macOS 27.0.1 arm64; Node.js v24.18.0 (repository pin); locked Vitest 5.0.3. Initial manual CLI probes also reproduced on Node.js v26.12.0. No external analysis engine. ### Target and artifact context Repository-generated PE/CLI assembly; the malformed copy differs only in NumberOfSections, set to zero. Real regular-file input, no provider simulation or target execution. ### Interface CLI ### Earliest failure stage Native analysis or reconstruction ### Relevant output or evidence Observed CLI results: ```text compare-managed-members assembly.dll assembly.dll: exit 0 compare-managed-members malformed-assembly.dll assembly.dll: exit 1; unreadable_output / execution_failure; no partial observation compare-managed-members assembly.dll malformed-assembly.dll: exit 1; unreadable_output / execution_failure; left partial observation retained inspect-managed-members malformed-assembly.dll: exit 1; invalid_request / invalid_input issues[0]: path=["path"], reason="invalid_format" reader detail: "PE section count must be positive" ``` Comparison message: "Analysis returned an unreadable result. Run `rea doctor` and review the reported details; repeating the unchanged request is not expected to succeed." ### Typed outcome or recovery expected `AnalysisInputError` / `invalid_request`, category `invalid_input`, carrying the malformed-file detail and failed input attribution, with input correction guidance. Keep the existing partial observation when the other side has already succeeded. ### Analysis or suspected cause Source-derived: [path comparison](https://github.com/morluto/rea/blob/52415975942a4b9c53a817915b313ccc404f280e/src/application/managed/ManagedMemberComparisonService.ts#L69-L123) calls the member inspector directly. The [inspector reads the PE layout before its metadata failure handler](https://github.com/morluto/rea/blob/52415975942a4b9c53a817915b313ccc404f280e/src/dotnet/ManagedMemberInspector.ts#L190-L198). [Zero sections throw ManagedReaderFailure](https://github.com/morluto/rea/blob/52415975942a4b9c53a817915b313ccc404f280e/src/dotnet/ManagedPeReader.ts#L291-L314), but the [comparison catch](https://github.com/morluto/rea/blob/52415975942a4b9c53a817915b313ccc404f280e/src/application/managed/ManagedMemberComparisonService.ts#L172-L190) only recognizes AnalysisError, ZodError and TypeError before constructing AnalysisProtocolError. The sibling provider already translates the reader failure to malformed input; comparison bypasses that boundary. ### Additional context Related: closed [#2191](https://github.com/morluto/rea/issues/2191), closed [PR #2192](https://github.com/morluto/rea/pull/2192), and merged [PR #2194](https://github.com/morluto/rea/pull/2194) concern malformed-layout classification in ManagedStaticProvider; PR #2194 supplied the merged repair. Their repair is present at this revision, but does not change the separate path-comparison catch. #6068 concerns source changes and digest guards rather than a stable malformed file. #5988 and #5990 concern supplied Evidence identities or workflow roles, not raw PE parsing. The other managed cancellation/cleanup and field-path reports were checked and have different producers and repairs. Fetched upstream main `f86206535f8ec71014781f9d432a84b01e183e5d` retains the relevant comparison, reader, inspector and sibling provider lines. Newer main was inspected, not executed. No regression range was established.
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#3713
No description provided.