fix(ntfs): preserve acquisition access and I/O failures

Co-authored-by: gensokyo-dev <342580529+gensokyo-dev@users.noreply.github.com>
This commit is contained in:
N0zoM1z0andgensokyo-dev authored and gensokyo-dev committed 2026-11-11 13:03:32 +08:00
1 parent 55f344a77b
commit 3c54f3ce4e
3 files changed
+65 -4

No files matched your search

@@ -0,0 +1,38 @@
import { expect, it, vi } from "vitest";
import { ArtifactResourceScope } from "../../artifacts/ArtifactResourceScope.js";
import * as stableArtifact from "../../artifacts/readStableArtifact.js";
import { projectAnalysisError } from "../../domain/analysisErrorProjection.js";
import { inspectNtfsJournalEvidence } from "./NtfsJournalService.js";
it.each([
["EACCES", "access_denied"],
["EIO", "artifact_operation_failed"],
])(
"preserves the acquisition failure category for %s",
async (systemCode, expectedCode) => {
const resources = new ArtifactResourceScope();
const spy = vi
.spyOn(stableArtifact, "readStableArtifact")
.mockRejectedValueOnce(
Object.assign(new Error("acquisition read failed"), {
code: systemCode,
}),
);
try {
const result = await inspectNtfsJournalEvidence(
{
mft_path: "/fixture/mft",
journal_path: "/fixture/journal",
},
resources,
);
expect(result.ok).toBe(false);
if (result.ok) throw new Error("expected acquisition failure");
expect(projectAnalysisError(result.error).code).toBe(expectedCode);
} finally {
spy.mockRestore();
await resources.close();
}
},
);
+26 -4
View File
@@ -11,11 +11,14 @@ import {
} from "../../artifacts/StableArtifactFailure.js";
import type { AnalysisError } from "../../domain/analysisErrorBase.js";
import {
AnalysisAccessDeniedError,
AnalysisArtifactChangedError,
AnalysisCancelledError,
AnalysisInputError,
AnalysisResourceConstraintError,
} from "../../domain/analysisErrorCore.js";
import { ArtifactOperationError } from "../../domain/artifactOperationError.js";
import { isSelectedRegularFileError } from "../../filesystem/RegularFile.js";
import { createEvidence, type Evidence } from "../../domain/evidence.js";
import { err, ok, type Result } from "../../domain/result.js";
import {
@@ -73,10 +76,29 @@ const readFailure = (
{ cause, retryable: false },
);
}
return inputFailure(
parameter,
`Could not read the selected acquisition at ${path}`,
cause,
if (
cause instanceof ArtifactReaderFailure &&
(cause.reason === "path" || cause.reason === "format")
)
return inputFailure(parameter, cause.message, cause);
if (cause instanceof Error && "code" in cause) {
if (cause.code === "EACCES" || cause.code === "EPERM")
return new AnalysisAccessDeniedError(operation, path, cause.code, {
cause,
});
if (isSelectedRegularFileError(cause))
return inputFailure(
parameter,
`Could not read the selected acquisition at ${path}`,
cause,
);
}
return new ArtifactOperationError(
operation,
"io",
undefined,
"NTFS acquisition read failed.",
{ cause },
);
};
+1
View File
@@ -38,6 +38,7 @@ export class ArtifactOperationError extends AnalysisError {
| "inspect_elf_notes"
| "inspect_elf_unwind_ranges"
| "inspect_devicetree"
| "inspect_ntfs_journal"
| "inspect_api_contract"
| "inspect_playwright_trace"
| "inspect_kernel_module"