mirror of
https://github.com/morluto/rea.git
synced 2026-10-31 03:45:39 +00:00
114 lines
4.1 KiB
TypeScript
114 lines
4.1 KiB
TypeScript
import { mkdir, rename, symlink, writeFile } from "node:fs/promises";
|
|
import { join } from "node:path";
|
|
import { expect, it } from "vitest";
|
|
|
|
import { DirectoryArtifactReader } from "../../../src/artifacts/DirectoryArtifactReader.js";
|
|
import { DesktopApplicationReader } from "../../../src/artifacts/DesktopApplicationReader.js";
|
|
import { DesktopApplicationService } from "../../../src/application/artifacts/DesktopApplicationService.js";
|
|
import { parseEvidence } from "../../../src/domain/evidence.js";
|
|
import { desktopApplicationSchema } from "../../../src/domain/desktopApplication.js";
|
|
import { createTestTempDirectory } from "../../fixtures/temporaryDirectory.js";
|
|
|
|
it.each(["before-open", "during-inspection", "during-stream"] as const)(
|
|
"rejects replacement of the admitted physical root %s even when file identity survives",
|
|
async (stage) => {
|
|
const root = await createTestTempDirectory("rea-directory-root-identity-");
|
|
const original = join(root, "original");
|
|
await mkdir(original);
|
|
await writeFile(join(original, "sample.txt"), "original content");
|
|
const reader = new DirectoryArtifactReader(original);
|
|
const entries = [];
|
|
for await (const entry of reader.entries()) entries.push(entry);
|
|
const entry = entries[0];
|
|
if (entry === undefined) throw new Error("Expected sample file");
|
|
const replaceRoot = async () => {
|
|
const moved = join(root, "moved");
|
|
await rename(original, moved);
|
|
await symlink(
|
|
moved,
|
|
original,
|
|
process.platform === "win32" ? "junction" : "dir",
|
|
);
|
|
};
|
|
try {
|
|
if (stage === "before-open") {
|
|
await replaceRoot();
|
|
await expect(reader.open(entry)).rejects.toMatchObject({
|
|
reason: "integrity",
|
|
});
|
|
} else if (stage === "during-inspection") {
|
|
await expect(
|
|
reader.inspectFile(entry, async (file) => {
|
|
const bytes = await file.readFile();
|
|
await replaceRoot();
|
|
return bytes;
|
|
}),
|
|
).rejects.toMatchObject({ reason: "integrity" });
|
|
} else {
|
|
const stream = await reader.open(entry);
|
|
await replaceRoot();
|
|
await expect(
|
|
(async () => {
|
|
for await (const _chunk of stream) {
|
|
/* consume the admitted file */
|
|
}
|
|
})(),
|
|
).rejects.toMatchObject({ reason: "integrity" });
|
|
}
|
|
} finally {
|
|
await reader.close();
|
|
}
|
|
},
|
|
);
|
|
|
|
it("discards unverified desktop byte facts when the admitted root changes during inspection", async () => {
|
|
const root = await createTestTempDirectory("rea-desktop-root-identity-");
|
|
const original = join(root, "original");
|
|
const moved = join(root, "moved");
|
|
await mkdir(original);
|
|
await writeFile(join(original, "sample.exe"), Buffer.from("MZ"));
|
|
const service = new DesktopApplicationService(
|
|
new DesktopApplicationReader((path) => {
|
|
const reader = new DirectoryArtifactReader(path);
|
|
const inspect = reader.inspectFile.bind(reader);
|
|
reader.inspectFile = (entry, read, signal) =>
|
|
inspect(
|
|
entry,
|
|
async (file) => {
|
|
const value = await read(file);
|
|
await rename(original, moved);
|
|
await symlink(
|
|
moved,
|
|
original,
|
|
process.platform === "win32" ? "junction" : "dir",
|
|
);
|
|
return value;
|
|
},
|
|
signal,
|
|
);
|
|
return reader;
|
|
}),
|
|
);
|
|
try {
|
|
const result = await service.inspect({ path: original });
|
|
expect(result.ok).toBe(false);
|
|
if (result.ok) throw new Error("Expected root identity failure");
|
|
const report = desktopApplicationSchema.parse(
|
|
parseEvidence(result.error.partialObservation).normalized_result,
|
|
);
|
|
expect(report.root.path).toBe(original);
|
|
expect(report.files).toHaveLength(1);
|
|
expect(report.files[0]).toMatchObject({
|
|
path: "sample.exe",
|
|
read_status: "unavailable",
|
|
ranges: [],
|
|
});
|
|
expect(report.files[0]?.facts.every((fact) => fact.offset === null)).toBe(
|
|
true,
|
|
);
|
|
expect(report.coverage.status).toBe("partial");
|
|
} finally {
|
|
await service.close();
|
|
}
|
|
});
|