diff --git a/src/artifacts/NativeDmgArtifactReader.ts b/src/artifacts/NativeDmgArtifactReader.ts index a1ff070381..9a3f9959a1 100644 --- a/src/artifacts/NativeDmgArtifactReader.ts +++ b/src/artifacts/NativeDmgArtifactReader.ts @@ -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); diff --git a/src/artifacts/NativeDmgRootObservation.test.ts b/src/artifacts/NativeDmgRootObservation.test.ts index c9f4a884da..58b1e25cdb 100644 --- a/src/artifacts/NativeDmgRootObservation.test.ts +++ b/src/artifacts/NativeDmgRootObservation.test.ts @@ -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), ); diff --git a/src/artifacts/apple/InterfaceBuilderDecodeBudget.test.ts b/src/artifacts/apple/InterfaceBuilderDecodeBudget.test.ts index dd87e23353..a929afc319 100644 --- a/src/artifacts/apple/InterfaceBuilderDecodeBudget.test.ts +++ b/src/artifacts/apple/InterfaceBuilderDecodeBudget.test.ts @@ -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 = ""; @@ -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",