mirror of
https://github.com/morluto/rea.git
synced 2026-10-11 21:39:55 +00:00
fix(native): report missing and non-file PE resource targets as input errors
`inspect_pe_resources` turned every acquisition failure other than limits and access denial into `artifact_operation_failed` with "Artifact could not be read or written. Check file access and try again." A missing path, a directory, a symlink or a path under a regular file got that advice, although the caller selected a target that cannot be a PE image. Map the stable reader's non-regular-file refusal and ENOENT/ENOTDIR to `AnalysisInputError` issues on `path`, and a file that changed during the read to `AnalysisArtifactChangedError`, as the sibling EVM carrier read does. Other I/O failures stay artifact faults. Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
83e52559b8
commit
e1def2bf1a
@@ -5,7 +5,9 @@ import type { ExecutionOptions } from "../../application/AnalysisProvider.js";
|
||||
import { ArtifactOperationError } from "../../domain/artifactOperationError.js";
|
||||
import {
|
||||
AnalysisAccessDeniedError,
|
||||
AnalysisArtifactChangedError,
|
||||
AnalysisCancelledError,
|
||||
AnalysisInputError,
|
||||
AnalysisResourceConstraintError,
|
||||
} from "../../domain/analysisErrorCore.js";
|
||||
import type { InspectPeResourcesInput } from "../../domain/native/peResources.js";
|
||||
@@ -37,47 +39,62 @@ export class PeResourcesProvider implements PeResourcesPort {
|
||||
),
|
||||
);
|
||||
} catch (cause) {
|
||||
if (options?.signal?.aborted)
|
||||
return err(
|
||||
new AnalysisCancelledError("inspect_pe_resources", { cause }),
|
||||
);
|
||||
if (cause instanceof ArtifactReaderFailure && cause.reason === "limit")
|
||||
return err(
|
||||
new AnalysisResourceConstraintError(
|
||||
"inspect_pe_resources",
|
||||
"memory",
|
||||
cause.message,
|
||||
{
|
||||
max_file_bytes: input.max_file_bytes,
|
||||
max_entries: input.max_entries,
|
||||
},
|
||||
{ cause },
|
||||
),
|
||||
);
|
||||
if (
|
||||
cause instanceof Error &&
|
||||
"code" in cause &&
|
||||
(cause.code === "EACCES" || cause.code === "EPERM")
|
||||
)
|
||||
return err(
|
||||
new AnalysisAccessDeniedError(
|
||||
"inspect_pe_resources",
|
||||
input.path,
|
||||
cause.code,
|
||||
{ cause },
|
||||
),
|
||||
);
|
||||
return err(
|
||||
new ArtifactOperationError(
|
||||
"inspect_pe_resources",
|
||||
cause instanceof ArtifactReaderFailure ? cause.reason : "io",
|
||||
undefined,
|
||||
cause instanceof Error
|
||||
? cause.message
|
||||
: "PE artifact inspection failed.",
|
||||
{ cause },
|
||||
),
|
||||
);
|
||||
return err(peResourcesFailure(cause, input, options?.signal));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const errorCode = (cause: unknown): unknown =>
|
||||
cause instanceof Error && "code" in cause ? cause.code : undefined;
|
||||
|
||||
// A missing or non-regular caller selection is an input error, as for the
|
||||
// EVM bytecode carrier read; only other I/O remains an artifact fault.
|
||||
const peResourcesFailure = (
|
||||
cause: unknown,
|
||||
input: InspectPeResourcesInput,
|
||||
signal: AbortSignal | undefined,
|
||||
) => {
|
||||
const operation = "inspect_pe_resources";
|
||||
if (signal?.aborted) return new AnalysisCancelledError(operation, { cause });
|
||||
const reason = cause instanceof ArtifactReaderFailure ? cause.reason : "io";
|
||||
const message = cause instanceof Error ? cause.message : String(cause);
|
||||
if (reason === "limit")
|
||||
return new AnalysisResourceConstraintError(
|
||||
operation,
|
||||
"memory",
|
||||
message,
|
||||
{
|
||||
max_file_bytes: input.max_file_bytes,
|
||||
max_entries: input.max_entries,
|
||||
},
|
||||
{ cause },
|
||||
);
|
||||
if (reason === "path")
|
||||
return new AnalysisInputError(operation, { cause }, [
|
||||
{ path: ["path"], reason: "invalid_format", message },
|
||||
]);
|
||||
if (reason === "integrity")
|
||||
return new AnalysisArtifactChangedError(operation, input.path, message, {
|
||||
cause,
|
||||
});
|
||||
const code = errorCode(cause);
|
||||
if (code === "ENOENT" || code === "ENOTDIR")
|
||||
return new AnalysisInputError(operation, { cause }, [
|
||||
{
|
||||
path: ["path"],
|
||||
reason: "invalid_value",
|
||||
message: `Selected PE image could not be read (${code}): ${input.path}.`,
|
||||
},
|
||||
]);
|
||||
if (code === "EACCES" || code === "EPERM")
|
||||
return new AnalysisAccessDeniedError(operation, input.path, code, {
|
||||
cause,
|
||||
});
|
||||
return new ArtifactOperationError(
|
||||
operation,
|
||||
reason,
|
||||
undefined,
|
||||
cause instanceof Error ? cause.message : "PE artifact inspection failed.",
|
||||
{ cause },
|
||||
);
|
||||
};
|
||||
|
||||
@@ -6,17 +6,32 @@ import { peResourceFixture } from "../../../src/native/pe/PeResources.fixture.js
|
||||
import { createTestTempDirectory } from "../../fixtures/temporaryDirectory.js";
|
||||
|
||||
describe("PE resource stable filesystem boundary", () => {
|
||||
it("rejects symlinks, missing files and directories without inventing empty inventory", async () => {
|
||||
it("rejects symlinks, missing files and directories as caller input without inventing empty inventory", async () => {
|
||||
const root = await createTestTempDirectory("rea-pe-resource-files-");
|
||||
const path = join(root, "image.exe");
|
||||
const alias = join(root, "alias.exe");
|
||||
await writeFile(path, peResourceFixture().bytes);
|
||||
await symlink(path, alias);
|
||||
const service = createPeResourcesService();
|
||||
for (const candidate of [root, alias, join(root, "missing.exe")]) {
|
||||
for (const [candidate, reason] of [
|
||||
[root, "invalid_format"],
|
||||
[alias, "invalid_format"],
|
||||
[join(root, "missing.exe"), "invalid_value"],
|
||||
[join(path, "child.exe"), "invalid_value"],
|
||||
] as const) {
|
||||
const result = await service.inspect({ path: candidate });
|
||||
if (result.ok) throw new Error("Expected file acquisition failure");
|
||||
expect(result.error._tag).toBe("ArtifactOperationError");
|
||||
expect(result.error).toMatchObject({
|
||||
_tag: "AnalysisInputError",
|
||||
operation: "inspect_pe_resources",
|
||||
issues: [
|
||||
{
|
||||
path: ["path"],
|
||||
reason,
|
||||
message: expect.stringContaining(candidate),
|
||||
},
|
||||
],
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user