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"), + }, + ], + }, + }); + } + }, +);