fix(artifacts): retain failed native DMG attachment provenance

This commit is contained in:
morluto committed 2026-10-24 08:22:13 +08:00
1 parent 11feab01da
commit 7c88ef14af
3 files changed
+49 -6

No files matched your search

+4 -4
View File
@@ -442,10 +442,6 @@ export class NativeDmgArtifactReader implements ArtifactReader {
try {
parsed = parseNativeDmgAttachOutput(attached.stdout);
} catch (cause: unknown) {
if (cause instanceof NativeDmgCommandFailure)
this.#provenance.push(
command(arguments_, ["read", "mount"], cause.exitCode),
);
throw new ArtifactReaderFailure(
"format",
"hdiutil returned a malformed attachment plist",
@@ -468,6 +464,10 @@ export class NativeDmgArtifactReader implements ArtifactReader {
);
this.#directory = new DirectoryArtifactReader(mountRoot);
} catch (cause: unknown) {
if (cause instanceof NativeDmgCommandFailure)
this.#provenance.push(
command(arguments_, ["read", "mount"], cause.exitCode),
);
if (cause instanceof NativeDmgCommandFailure && cause.stdout !== "") {
try {
this.#attachOutput = parseNativeDmgAttachOutput(cause.stdout);
@@ -42,6 +42,7 @@ it("retains authenticated carrier facts and unknown child coverage when native a
} catch (cause: unknown) {
failure = cause;
}
expect(failure).toBeDefined();
const projected = projectAnalysisError(
translateArtifactFailure("inspect_artifact", failure),
);
@@ -12,7 +12,11 @@ import { unknownDateLimitation } from "../../domain/apple/plistValue.js";
import { parseXmlPropertyList } from "../../domain/propertyListKeys.js";
import { createTestTempDirectory } from "../../../tests/fixtures/temporaryDirectory.js";
import { scanBinaryPlistScalars } from "./PlistScalars.js";
import { binaryPlistWithOverlappingDateAndData } from "../../../tests/fixtures/binaryPlistObjectTable.js";
import {
readBinaryPlistObjectOffset,
readBinaryPlistObjectTableLayout,
writeBinaryPlistObjectOffset,
} from "../../../tests/fixtures/binaryPlistObjectTable.js";
import { embedSubmillisecondDateInWideIntegerPayload } from "../../../tests/fixtures/binaryPlistScalarAlias.js";
const example = "<!DOCTYPE document [ harmless example ]>";
@@ -163,7 +167,34 @@ it("retains explicit date uncertainty for an overlapping binary plist object", a
const root = await createTestTempDirectory("rea-ib-binary-date-overlap-");
const bundle = join(root, "Example.app");
await mkdir(bundle);
const bytes = binaryPlistWithOverlappingDateAndData();
const bytes = Buffer.from(
buildBinary({
"com.apple.ibtool.document.objects": {
button: {
customClass: "UIButton",
date: new Date(0),
data: Buffer.alloc(0),
},
},
"com.apple.ibtool.document.connections": {},
"com.apple.ibtool.document.hierarchy": [],
"com.apple.ibtool.document.classes": {},
}),
);
const date = scanBinaryPlistScalars(bytes).dates[0];
if (date === undefined) throw new Error("fixture lost its date");
const table = readBinaryPlistObjectTableLayout(bytes);
let dataObjectId: number | undefined;
for (let objectId = 0; objectId < table.objectCount; objectId += 1) {
const offset = readBinaryPlistObjectOffset(bytes, table, objectId);
if (bytes[offset] === 0x40) dataObjectId = objectId;
}
if (dataObjectId === undefined)
throw new Error("fixture lost its empty data");
// Keep both objects reachable through a recognized archive while sharing source bytes.
bytes.writeDoubleBE(0.0005, date.offset + 1);
bytes[date.offset + 2] = 0x40;
writeBinaryPlistObjectOffset(bytes, table, dataObjectId, date.offset + 2);
await writeFile(join(bundle, "Panel.nib"), bytes);
const result = await analyzeInterfaceBuilderBundle({
@@ -171,6 +202,17 @@ it("retains explicit date uncertainty for an overlapping binary plist object", a
targetSha256: "a".repeat(64),
});
expect(result.documents).toContainEqual(
expect.objectContaining({
relative_path: "Panel.nib",
archive_sha256: createHash("sha256").update(bytes).digest("hex"),
}),
);
expect(
result.graph.nodes.find(
({ attributes }) => attributes.interface_builder_object_id === "button",
)?.attributes.date,
).toEqual({ $plist_type: "date", iso8601: null });
expect(result.limitations).toContainEqual(
expect.stringContaining(
"Panel.nib: 1 date value(s) are reported with unknown timestamps because their sub-millisecond source records overlap",