mirror of
https://github.com/morluto/rea.git
synced 2026-11-07 05:56:19 -10:00
fix(artifacts): preserve mounted DMG traversal coverage
This commit is contained in:
1 parent
2146caeb3d
commit
650873e772
2 files changed
+84
-1
No files matched your search
@@ -202,6 +202,7 @@ export class NativeDmgArtifactReader implements ArtifactReader {
|
||||
const prefix = basename(this.path);
|
||||
const entries = directory
|
||||
.entries(signal, {
|
||||
onCoverage: (coverage) => options?.onCoverage?.(coverage),
|
||||
onOmission: (omission) =>
|
||||
options?.onOmission?.({
|
||||
...omission,
|
||||
|
||||
@@ -1,13 +1,95 @@
|
||||
import { ExecFileOutputCancelledError } from "../process/ExecFileOutput.js";
|
||||
import { ArtifactReaderFailure } from "./ArtifactReader.js";
|
||||
import { build } from "plist";
|
||||
import { expect, it } from "vitest";
|
||||
import { expect, it, vi } from "vitest";
|
||||
import {
|
||||
NativeDmgArtifactReader,
|
||||
type NativeDmgHost,
|
||||
} from "./NativeDmgArtifactReader.js";
|
||||
import { translateArtifactFailure } from "./ArtifactOperationFailure.js";
|
||||
import { projectAnalysisError } from "../domain/analysisErrorProjection.js";
|
||||
import { mkdir, writeFile } from "node:fs/promises";
|
||||
import { join } from "node:path";
|
||||
import type { ArtifactReaderTraversal } from "./ArtifactReader.js";
|
||||
import { DirectoryArtifactReader } from "./DirectoryArtifactReader.js";
|
||||
|
||||
it.skipIf(process.platform === "win32").each([false, true])(
|
||||
"preserves mounted-directory coverage alongside readable DMG entries (unknown coverage: %s)",
|
||||
async (unknownCoverage) => {
|
||||
let mountPoint: string | undefined;
|
||||
const coverage: ArtifactReaderTraversal[] = [];
|
||||
const producerCoverage: ArtifactReaderTraversal = {
|
||||
status: "unknown",
|
||||
examinedEntries: 3,
|
||||
limitations: [
|
||||
"Directory contains an entry name that is not valid UTF-8.",
|
||||
],
|
||||
};
|
||||
// APFS rejects invalid UTF-8 basenames at creation. Inject the directory
|
||||
// owner's completed observation while retaining its real entry traversal.
|
||||
const entries = DirectoryArtifactReader.prototype.entries;
|
||||
const traversal = vi
|
||||
.spyOn(DirectoryArtifactReader.prototype, "entries")
|
||||
.mockImplementation(async function* (
|
||||
this: DirectoryArtifactReader,
|
||||
signal,
|
||||
options,
|
||||
) {
|
||||
yield* entries.call(this, signal, options);
|
||||
if (unknownCoverage && typeof options !== "function")
|
||||
options?.onCoverage?.(producerCoverage);
|
||||
});
|
||||
const host: NativeDmgHost = {
|
||||
run: async (args) => {
|
||||
if (args[0] === "verify" || args[0] === "detach")
|
||||
return { stdout: "", exitCode: 0 };
|
||||
if (args[0] === "attach") {
|
||||
const root = args[args.indexOf("-mountroot") + 1];
|
||||
if (root === undefined) throw new Error("Missing mount root");
|
||||
mountPoint = join(root, "Volume");
|
||||
await mkdir(mountPoint);
|
||||
await writeFile(join(mountPoint, "readable.txt"), "visible");
|
||||
}
|
||||
if (args[0] === "info" || args[0] === "attach") {
|
||||
const entities =
|
||||
mountPoint === undefined
|
||||
? []
|
||||
: [{ "dev-entry": "/dev/disk800", "mount-point": mountPoint }];
|
||||
return {
|
||||
stdout: build(
|
||||
args[0] === "info"
|
||||
? {
|
||||
images:
|
||||
entities.length === 0
|
||||
? []
|
||||
: [{ "system-entities": entities }],
|
||||
}
|
||||
: { "system-entities": entities },
|
||||
),
|
||||
exitCode: 0,
|
||||
};
|
||||
}
|
||||
throw new Error(`Unexpected command ${String(args[0])}`);
|
||||
},
|
||||
};
|
||||
const reader = new NativeDmgArtifactReader("/fixture/image.dmg", {}, host);
|
||||
const paths: string[] = [];
|
||||
try {
|
||||
for await (const entry of reader.entries(undefined, {
|
||||
onCoverage: (value) => coverage.push(value),
|
||||
}))
|
||||
paths.push(entry.path);
|
||||
expect(paths).toContain("image.dmg/Volume/readable.txt");
|
||||
expect(coverage).toEqual(unknownCoverage ? [producerCoverage] : []);
|
||||
} finally {
|
||||
try {
|
||||
await reader.close();
|
||||
} finally {
|
||||
traversal.mockRestore();
|
||||
}
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
it("retains malformed successful hdiutil inventory output as a typed producer parse failure before attach", async () => {
|
||||
const calls: string[] = [];
|
||||
|
||||
Reference in new issue
Block a user