mirror of
https://github.com/morluto/rea.git
synced 2026-10-26 17:35:11 +00:00
fix(providers): share selected-file error classification
This commit is contained in:
1 parent
8d8d85ba22
commit
a32d731ed7
10 files changed
+96
-38
No files matched your search
@@ -1,3 +1,4 @@
|
||||
import { isSelectedRegularFileError } from "../filesystem/RegularFile.js";
|
||||
import { readStableArtifact } from "../artifacts/readStableArtifact.js";
|
||||
import {
|
||||
artifactFilePrimaryFailure,
|
||||
@@ -110,11 +111,7 @@ const failureForPrimary = (cause: unknown, path: string): AnalysisError => {
|
||||
return new AnalysisAccessDeniedError(OPERATION, path, cause.code, {
|
||||
cause,
|
||||
});
|
||||
if (
|
||||
["ENOENT", "ENOTDIR", "ELOOP", "ENAMETOOLONG"].includes(
|
||||
String(cause.code),
|
||||
)
|
||||
)
|
||||
if (isSelectedRegularFileError(cause))
|
||||
return new AnalysisInputError(OPERATION, { cause }, [
|
||||
{
|
||||
path: ["path"],
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { isSelectedRegularFileError } from "../filesystem/RegularFile.js";
|
||||
import { Il2cppBinaryFormatFailure } from "./Il2cppBinaryFormat.js";
|
||||
import { Il2cppMetadataFormatFailure } from "./Il2cppMetadataFormat.js";
|
||||
import type { AnalysisPartialObservation } from "../domain/analysisErrorBase.js";
|
||||
@@ -138,11 +139,7 @@ const failureForPrimary = (
|
||||
return new AnalysisAccessDeniedError(OPERATION, path, cause.code, {
|
||||
cause,
|
||||
});
|
||||
if (
|
||||
["ENOENT", "ENOTDIR", "ELOOP", "ENAMETOOLONG"].includes(
|
||||
String(cause.code),
|
||||
)
|
||||
)
|
||||
if (isSelectedRegularFileError(cause))
|
||||
return new AnalysisInputError(OPERATION, { cause }, [
|
||||
{
|
||||
path: [field],
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { isSelectedRegularFileError } from "../../filesystem/RegularFile.js";
|
||||
import { ArtifactReaderFailure } from "../../artifacts/ArtifactReader.js";
|
||||
import { readStableArtifact } from "../../artifacts/readStableArtifact.js";
|
||||
import {
|
||||
@@ -201,11 +202,7 @@ const elfNotesFailurePrimary = (
|
||||
return new AnalysisAccessDeniedError(OPERATION, input.path, cause.code, {
|
||||
cause,
|
||||
});
|
||||
if (
|
||||
["ENOENT", "ENOTDIR", "ELOOP", "ENAMETOOLONG"].includes(
|
||||
String(cause.code),
|
||||
)
|
||||
)
|
||||
if (isSelectedRegularFileError(cause))
|
||||
return new AnalysisInputError(OPERATION, { cause }, [
|
||||
{
|
||||
path: ["path"],
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { isSelectedRegularFileError } from "../../filesystem/RegularFile.js";
|
||||
import { ArtifactReaderFailure } from "../../artifacts/ArtifactReader.js";
|
||||
import { readStableArtifact } from "../../artifacts/readStableArtifact.js";
|
||||
import {
|
||||
@@ -161,11 +162,7 @@ const nativeExportsFailurePrimary = (
|
||||
return new AnalysisAccessDeniedError(OPERATION, input.path, cause.code, {
|
||||
cause,
|
||||
});
|
||||
if (
|
||||
["ENOENT", "ENOTDIR", "ELOOP", "ENAMETOOLONG"].includes(
|
||||
String(cause.code),
|
||||
)
|
||||
)
|
||||
if (isSelectedRegularFileError(cause))
|
||||
return new AnalysisInputError(OPERATION, { cause }, [
|
||||
{
|
||||
path: ["path"],
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { isSelectedRegularFileError } from "../../filesystem/RegularFile.js";
|
||||
import { ArtifactReaderFailure } from "../../artifacts/ArtifactReader.js";
|
||||
import { readStableArtifact } from "../../artifacts/readStableArtifact.js";
|
||||
import {
|
||||
@@ -148,11 +149,7 @@ const peCertificatesFailurePrimary = (
|
||||
return new AnalysisAccessDeniedError(OPERATION, input.path, cause.code, {
|
||||
cause,
|
||||
});
|
||||
if (
|
||||
["ENOENT", "ENOTDIR", "ELOOP", "ENAMETOOLONG"].includes(
|
||||
String(cause.code),
|
||||
)
|
||||
)
|
||||
if (isSelectedRegularFileError(cause))
|
||||
return new AnalysisInputError(OPERATION, { cause }, [
|
||||
{
|
||||
path: ["path"],
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { isSelectedRegularFileError } from "../../filesystem/RegularFile.js";
|
||||
import { fixedNativeAllowance } from "../../domain/native/nativeDeadlines.js";
|
||||
import { ArtifactReaderFailure } from "../../artifacts/ArtifactReader.js";
|
||||
import { readStableArtifact } from "../../artifacts/readStableArtifact.js";
|
||||
@@ -172,7 +173,7 @@ const peResourcesFailurePrimary = (
|
||||
return new AnalysisAccessDeniedError(operation, input.path, cause.code, {
|
||||
cause,
|
||||
});
|
||||
if (cause.code === "ENOENT" || cause.code === "ENOTDIR")
|
||||
if (isSelectedRegularFileError(cause))
|
||||
return new AnalysisInputError(operation, { cause }, [
|
||||
{
|
||||
path: ["path"],
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { isSelectedRegularFileError } from "../filesystem/RegularFile.js";
|
||||
import {
|
||||
artifactFileCancellationCause,
|
||||
artifactFilePrimaryFailure,
|
||||
@@ -96,11 +97,7 @@ const failureForPrimary = (cause: unknown, input: InspectPythonPycInput) => {
|
||||
return new AnalysisAccessDeniedError(OPERATION, input.path, cause.code, {
|
||||
cause,
|
||||
});
|
||||
if (
|
||||
["ENOENT", "ENOTDIR", "ELOOP", "ENAMETOOLONG"].includes(
|
||||
String(cause.code),
|
||||
)
|
||||
)
|
||||
if (isSelectedRegularFileError(cause))
|
||||
return new AnalysisInputError(OPERATION, { cause }, [
|
||||
{
|
||||
path: ["path"],
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { isSelectedRegularFileError } from "../filesystem/RegularFile.js";
|
||||
import { readStableArtifact } from "../artifacts/readStableArtifact.js";
|
||||
import {
|
||||
artifactFilePrimaryFailure,
|
||||
@@ -299,11 +300,7 @@ const artifactFailure = (cause: unknown, path: string): AnalysisError => {
|
||||
return new AnalysisAccessDeniedError(OPERATION, path, cause.code, {
|
||||
cause,
|
||||
});
|
||||
if (
|
||||
["ENOENT", "ENOTDIR", "ELOOP", "ENAMETOOLONG"].includes(
|
||||
String(cause.code),
|
||||
)
|
||||
)
|
||||
if (isSelectedRegularFileError(cause))
|
||||
return new AnalysisInputError(OPERATION, { cause }, [
|
||||
{
|
||||
path: ["path"],
|
||||
|
||||
@@ -38,6 +38,30 @@ describe("PE resource stable filesystem boundary", () => {
|
||||
}
|
||||
});
|
||||
|
||||
it.skipIf(process.platform === "win32")(
|
||||
"reports a native symlink-loop failure as invalid selected-path input",
|
||||
async () => {
|
||||
const root = await createTestTempDirectory("rea-pe-resource-path-loop-");
|
||||
const loop = join(root, "loop");
|
||||
await symlink(loop, loop, "dir");
|
||||
const path = join(loop, "image.exe");
|
||||
const result = await createPeResourcesService().inspect({ path });
|
||||
if (result.ok) throw new Error("Expected symlink-loop failure");
|
||||
expect(result.error).toMatchObject({
|
||||
_tag: "AnalysisInputError",
|
||||
operation: "inspect_pe_resources",
|
||||
issues: [
|
||||
{
|
||||
path: ["path"],
|
||||
reason: "invalid_value",
|
||||
message: expect.stringContaining("ELOOP"),
|
||||
},
|
||||
],
|
||||
cause: { code: "ELOOP" },
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
it("returns typed resource and cancellation failures", async () => {
|
||||
const root = await createTestTempDirectory("rea-pe-resource-budget-");
|
||||
const path = join(root, "image.exe");
|
||||
|
||||
@@ -67,6 +67,7 @@ import { parseEvidence } from "../../src/domain/evidence.js";
|
||||
import { projectAnalysisError } from "../../src/domain/analysisErrorProjection.js";
|
||||
import {
|
||||
AnalysisCancelledError,
|
||||
AnalysisInputError,
|
||||
AnalysisArtifactChangedError,
|
||||
AnalysisAccessDeniedError,
|
||||
AnalysisResourceConstraintError,
|
||||
@@ -696,15 +697,19 @@ it("preserves upstream resource-bound facts through an unconfirmed descriptor cl
|
||||
}
|
||||
});
|
||||
|
||||
const fileFailureProviders = [
|
||||
const stableArtifactProviders = [
|
||||
...staticProviders,
|
||||
["ELF notes", () => new ElfNotesProvider()],
|
||||
["PE certificates", () => new PeCertificatesProvider()],
|
||||
["native exports", () => new NativeExportsProvider()],
|
||||
["BEAM", () => new BeamProvider()],
|
||||
["Python pyc", () => new PythonPycProvider()],
|
||||
] as const;
|
||||
|
||||
const fileFailureProviders = [
|
||||
...stableArtifactProviders,
|
||||
["BEAM", () => new BeamProvider()],
|
||||
] as const;
|
||||
|
||||
const inspectFileFailure = (
|
||||
provider: ReturnType<(typeof fileFailureProviders)[number][1]>,
|
||||
path: string,
|
||||
@@ -744,6 +749,55 @@ const inspectFileFailure = (
|
||||
return provider.inspect({ path }, options);
|
||||
};
|
||||
|
||||
it.each(
|
||||
stableArtifactProviders.flatMap(([name, makeProvider]) =>
|
||||
[
|
||||
["ESTALE", "AnalysisInputError"],
|
||||
["ENOTFILE", "AnalysisInputError"],
|
||||
["EISDIR", "AnalysisInputError"],
|
||||
["ELOOP", "AnalysisInputError"],
|
||||
["EACCES", "AnalysisAccessDeniedError"],
|
||||
["EIO", undefined],
|
||||
].map(([code, expectedTag]) => ({ name, makeProvider, code, expectedTag })),
|
||||
),
|
||||
)(
|
||||
"preserves $name descriptor failure classification for $code",
|
||||
async ({ makeProvider, code, expectedTag }) => {
|
||||
const path = await selectedImage();
|
||||
const provider = makeProvider();
|
||||
const physical = Object.assign(new Error("descriptor metadata failed"), {
|
||||
code,
|
||||
});
|
||||
fault.blockNew = false;
|
||||
fault.readFailure = { cause: physical };
|
||||
try {
|
||||
const result = await inspectFileFailure(provider, path);
|
||||
if (result.ok) throw new Error("Expected physical read failure");
|
||||
expect(result.error.cause).toBe(physical);
|
||||
expect(result.error._tag).toBe(
|
||||
expectedTag ??
|
||||
(provider instanceof ElfNotesProvider ||
|
||||
provider instanceof PeCertificatesProvider ||
|
||||
provider instanceof PeResourcesProvider
|
||||
? "ArtifactOperationError"
|
||||
: "ProviderAdapterError"),
|
||||
);
|
||||
if (result.error instanceof AnalysisInputError)
|
||||
expect(result.error.issues).toEqual([
|
||||
{
|
||||
path: ["path"],
|
||||
reason: "invalid_value",
|
||||
message: expect.stringContaining(path),
|
||||
},
|
||||
]);
|
||||
expect(fault.handles).toHaveLength(1);
|
||||
expect(fault.handles[0]!.fd).toBe(-1);
|
||||
} finally {
|
||||
await provider.close();
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
it.each(fileFailureProviders)(
|
||||
"uses the original %s physical failure across nested descriptor owners",
|
||||
async (_name, makeProvider) => {
|
||||
|
||||
Reference in new issue
Block a user