fix(artifacts): stop offering integrity-policy retries for checksum-invalid DMGs

integrity_policy=record-and-continue only records declared-versus-observed
digest contradictions, which carry declared and observed facts. Integrity
failures without those facts, such as hdiutil checksum verification, fail
closed under either policy, so the policy retry advice could never succeed.
Explain the limitation and recommend a valid copy instead.

Fixes #5549

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Kaoru0822andClaude Opus 5.5 committed 2026-10-20 15:24:12 +00:00
1 parent 080c0e2b2b
commit c76e981dcd
2 files changed
+90

No files matched your search

+10
View File
@@ -447,6 +447,16 @@ const artifactMessage = ({
return `ASAR unpacked companion bytes are unavailable for ${details.logicalPath}. Select the archive in place with its .unpacked directory beside it, then retry.`;
if (reason === "unavailable")
return "Artifact processing is unavailable for the current target or host. Check artifact support and required tools.";
// integrity_policy records only declared-versus-observed digest
// contradictions, which carry their declared and observed facts. Container
// self-verification (such as a DMG checksum) and source changes fail closed
// under either policy, so offering a policy retry would be ineffective.
if (
reason === "integrity" &&
details === undefined &&
INTEGRITY_POLICY_OPERATIONS.has(operation)
)
return "Artifact failed an integrity check that integrity_policy cannot relax, such as a container checksum or a source that changed while being read. Get a valid, unchanged copy and try again.";
if (reason === "integrity" && INTEGRITY_POLICY_OPERATIONS.has(operation))
return `${
details?.unpacked === true &&
+80
View File
@@ -22,6 +22,7 @@ import { createTestBinarySession } from "../../fixtures/binarySession.js";
import { ArtifactProvider } from "../../../src/artifacts/ArtifactProvider.js";
import { ARTIFACT_COMPARISON_EXAMPLE } from "../../../src/contracts/artifactComparisonExample.js";
import { createServer } from "../../../src/server/createServer.js";
import { execFileOutput } from "../../../src/process/ExecFileOutput.js";
import { observed } from "../../fixtures/analysisExecution.js";
const integrityMessage = (unpacked: boolean): string =>
@@ -104,6 +105,85 @@ it.each([
20_000,
);
it.skipIf(process.platform !== "darwin")(
"does not offer an ineffective integrity-policy retry for a checksum-invalid DMG",
async () => {
const root = await createTestTempDirectory("rea-dmg-checksum-mcp-");
const writableImagePath = join(root, "writable.dmg");
const imagePath = join(root, "fixture.dmg");
await execFileOutput("/usr/bin/hdiutil", [
"create",
"-quiet",
"-size",
"1m",
"-layout",
"NONE",
"-type",
"UDIF",
writableImagePath,
]);
await execFileOutput("/usr/bin/hdiutil", [
"convert",
"-quiet",
writableImagePath,
"-format",
"UDRO",
"-o",
imagePath,
]);
const image = await readFile(imagePath);
const footer = image.subarray(-512);
expect(footer.subarray(0, 4).toString("ascii")).toBe("koly");
const corruptionOffset = Number(footer.readBigUInt64BE(24)) + 4096;
const originalByte = image[corruptionOffset];
if (originalByte === undefined)
throw new Error("expected a byte in the generated image data fork");
image[corruptionOffset] = originalByte ^ 0x01;
await writeFile(imagePath, image);
const session = createTestBinarySession(new ArtifactProvider(process.env));
const server = createServer({ kind: "session", session });
const client = new Client({ name: "dmg-integrity-test", version: "1" });
const [clientTransport, serverTransport] =
InMemoryTransport.createLinkedPair();
try {
await server.connect(serverTransport);
await client.connect(clientTransport);
const opened = await client.callTool({
name: "open_binary",
arguments: { path: imagePath },
});
expect(opened.isError).not.toBe(true);
const message =
"Artifact failed an integrity check that integrity_policy cannot relax, such as a container checksum or a source that changed while being read. Get a valid, unchanged copy and try again.";
for (const integrity_policy of ["fail", "record-and-continue"]) {
const result = await client.callTool({
name: "inspect_artifact",
arguments: { integrity_policy },
});
expect(result.isError).toBe(true);
expect(parseMcpToolError(result)).toMatchObject({
error: {
code: "artifact_operation_failed",
category: "integrity_mismatch",
message,
retryable: false,
remediation: { action: message },
},
});
}
} finally {
await Promise.allSettled([
client.close(),
server.close(),
session.close(),
]);
}
},
60_000,
);
it("extracts an active archive through MCP when requested", async () => {
const root = await createTestTempDirectory("rea-artifact-extract-mcp-");
const archive = join(root, "fixture.zip");