From 90e37f4152990be721784006a820a3794920213c Mon Sep 17 00:00:00 2001 From: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com> Date: Mon, 12 Oct 2026 06:53:26 -0700 Subject: [PATCH] fix(managed): report malformed PE layouts as invalid input (#2194) * fix(managed): report malformed PE layouts as invalid input readManagedPeLayout raises ManagedReaderFailure for DOS, COFF and section header failures such as section data past EOF. Those escaped the managed inspectors, so ManagedStaticProvider wrapped them as ProviderAdapterError and the CLI and MCP reported execution_failure with advice to run rea doctor. Map format failures to an invalid_format input issue carrying the reader's detail, as inspect-pe-resources and inspect-go-binary do. Fixes #2191 Co-Authored-By: Claude Opus 5.5 * fix(managed): route unmappable PE layouts through one input error inspect-managed-native-boundaries caught a layout-level ManagedReaderFailure itself and returned an empty malformed inspection with exit 0, while the other managed operations now report the same bytes as invalid input. Let the layout failure reach the provider's invalid_format mapping; metadata failures after a readable layout still return malformed coverage. Co-Authored-By: Claude Opus 5.5 --------- Co-authored-by: Claude Opus 5.5 --- src/dotnet/ManagedNativeBoundaryInspector.ts | 11 ++----- src/dotnet/ManagedStaticProvider.ts | 16 ++++++++++ tests/acceptance/managedCliOutput.test.ts | 33 ++++++++++++++++++++ 3 files changed, 51 insertions(+), 9 deletions(-) diff --git a/src/dotnet/ManagedNativeBoundaryInspector.ts b/src/dotnet/ManagedNativeBoundaryInspector.ts index 773a95c077..d2b84be049 100644 --- a/src/dotnet/ManagedNativeBoundaryInspector.ts +++ b/src/dotnet/ManagedNativeBoundaryInspector.ts @@ -121,15 +121,8 @@ export const inspectManagedNativeBoundariesBytes = ( bytes: Buffer, target: BinaryTarget, ): ManagedNativeBoundaryInspection => { - let pe: ManagedPeLayout; - try { - pe = readManagedPeLayout(bytes); - } catch (cause: unknown) { - if (!(cause instanceof ManagedReaderFailure)) throw cause; - return emptyInspection(target, bytes, "malformed", { - issues: [cause.issue], - }); - } + // An unmappable PE layout is invalid input for every managed operation. + const pe = readManagedPeLayout(bytes); if (pe.cli === null) return emptyInspection( target, diff --git a/src/dotnet/ManagedStaticProvider.ts b/src/dotnet/ManagedStaticProvider.ts index ba09a1939f..d456455a20 100644 --- a/src/dotnet/ManagedStaticProvider.ts +++ b/src/dotnet/ManagedStaticProvider.ts @@ -22,6 +22,7 @@ import type { BinaryTarget } from "../domain/binaryTargetTypes.js"; import { AnalysisCancelledError, AnalysisCapabilityUnavailableError, + AnalysisInputError, } from "../domain/analysisErrorCore.js"; import { EvidenceIntegrityError } from "../domain/evidenceErrors.js"; import { ProviderAdapterError } from "../domain/providerAdapterError.js"; @@ -38,6 +39,7 @@ import { inspectManagedArtifactBytes } from "./ManagedArtifactInspector.js"; import { inspectManagedMembersBytes } from "./ManagedMemberInspector.js"; import { inspectManagedNativeBoundariesBytes } from "./ManagedNativeBoundaryInspector.js"; import { RegularFileCleanupFailure } from "../application/RegularFileRead.js"; +import { ManagedReaderFailure } from "./ManagedReaderFailure.js"; import { hashManagedSource, managedCleanupError, @@ -135,6 +137,20 @@ class ManagedStaticClient implements AnalysisClient { if (cause instanceof AnalysisError) return err(cause); if (options?.signal?.aborted === true) return err(new AnalysisCancelledError(operation)); + // A PE header or section table the reader cannot map is malformed input. + if ( + cause instanceof ManagedReaderFailure && + cause.issue.code !== "resource-limit" + ) + return err( + new AnalysisInputError(operation, { cause }, [ + { + path: ["path"], + reason: "invalid_format", + message: `Selected artifact ${this.target.path}: ${cause.issue.detail}`, + }, + ]), + ); return err( new ProviderAdapterError(MANAGED_STATIC_PROVIDER.id, operation, { cause, diff --git a/tests/acceptance/managedCliOutput.test.ts b/tests/acceptance/managedCliOutput.test.ts index a24020d601..26ef7b85d4 100644 --- a/tests/acceptance/managedCliOutput.test.ts +++ b/tests/acceptance/managedCliOutput.test.ts @@ -307,3 +307,36 @@ cliTest( } }, ); + +cliTest( + "managed CLI reports a section table past EOF as invalid input", + async ({ cli }) => { + const root = await createTestTempDirectory("rea-managed-cli-sections-"); + const path = join(root, "fixture.dll"); + const bytes = buildManagedPeFixture(); + const pe = bytes.readUInt32LE(0x3c); + const sections = pe + 24 + bytes.readUInt16LE(pe + 20); + bytes.writeUInt32LE(bytes.length + 0x1000, sections + 20); + await writeFile(path, bytes); + for (const command of [ + "inspect-managed-artifact", + "inspect-managed-members", + "inspect-managed-native-boundaries", + ]) { + const result = await cli.run({ arguments: [command, path, "--json"] }); + expect(result.exitCode).toBe(1); + expect(result.json).toMatchObject({ + code: "invalid_request", + details: { + issues: [ + { + path: ["path"], + reason: "invalid_format", + message: expect.stringContaining("pe.section-data"), + }, + ], + }, + }); + } + }, +);