From a6d830b51c5f3a53ca5e339cf81334770b98ddef Mon Sep 17 00:00:00 2001 From: Jeremy Huntwork Date: Sun, 23 Aug 2026 22:09:04 -0400 Subject: [PATCH 1/2] Exclude manifest v5 from package content identity --- src/hash.zig | 48 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/src/hash.zig b/src/hash.zig index b83bf97..658d699 100644 --- a/src/hash.zig +++ b/src/hash.zig @@ -368,6 +368,8 @@ fn isExcludedStoreHashPath(entry_path: []const u8, include_metadata: bool) bool std.mem.eql(u8, entry_path, ".mere/manifest.v1.sig") or std.mem.eql(u8, entry_path, ".mere/manifest.v2") or std.mem.eql(u8, entry_path, ".mere/manifest.v2.sig") or + std.mem.eql(u8, entry_path, ".mere/manifest.v5") or + std.mem.eql(u8, entry_path, ".mere/manifest.v5.sig") or std.mem.eql(u8, entry_path, ".mere/projection.v1"); } @@ -1077,6 +1079,52 @@ test "calculateStoreContentHashV4 authenticates canonical permission classes" { try std.testing.expect(!std.mem.eql(u8, public_read, owner_read)); } +test "calculateStoreContentHashV4 excludes manifest v5 but includes canonical metadata" { + const th = @import("test_helpers.zig"); + var test_env = try th.createTestEnv(); + defer { + test_env.cleanup(); + std.testing.allocator.destroy(test_env); + } + + const payload_path = try std.fs.path.join(test_env.ctx.allocator, &.{ test_env.path, "payload" }); + defer test_env.ctx.allocator.free(payload_path); + var payload = try std.Io.Dir.createFileAbsolute(path.currentIo(), payload_path, .{}); + try payload.writeStreamingAll(path.currentIo(), "payload\n"); + payload.close(path.currentIo()); + + const mere_dir = try std.fs.path.join(test_env.ctx.allocator, &.{ test_env.path, ".mere" }); + defer test_env.ctx.allocator.free(mere_dir); + try path.ensureDirExists(mere_dir); + const meta_path = try std.fs.path.join(test_env.ctx.allocator, &.{ mere_dir, "meta.kdl" }); + defer test_env.ctx.allocator.free(meta_path); + var meta = try std.Io.Dir.createFileAbsolute(path.currentIo(), meta_path, .{}); + try meta.writeStreamingAll(path.currentIo(), "metadata { description \"first\" }\n"); + meta.close(path.currentIo()); + + const before_manifest = try calculateStoreContentHashV4(test_env.ctx.allocator, test_env.path, null); + defer test_env.ctx.allocator.free(before_manifest); + + for ([_][]const u8{ "manifest.v5", "manifest.v5.sig" }) |name| { + const manifest_path = try std.fs.path.join(test_env.ctx.allocator, &.{ mere_dir, name }); + defer test_env.ctx.allocator.free(manifest_path); + var manifest_file = try std.Io.Dir.createFileAbsolute(path.currentIo(), manifest_path, .{}); + try manifest_file.writeStreamingAll(path.currentIo(), "derived manifest data\n"); + manifest_file.close(path.currentIo()); + } + + const with_manifest = try calculateStoreContentHashV4(test_env.ctx.allocator, test_env.path, null); + defer test_env.ctx.allocator.free(with_manifest); + try std.testing.expectEqualStrings(before_manifest, with_manifest); + + meta = try std.Io.Dir.createFileAbsolute(path.currentIo(), meta_path, .{ .truncate = true }); + try meta.writeStreamingAll(path.currentIo(), "metadata { description \"second\" }\n"); + meta.close(path.currentIo()); + const changed_meta = try calculateStoreContentHashV4(test_env.ctx.allocator, test_env.path, null); + defer test_env.ctx.allocator.free(changed_meta); + try std.testing.expect(!std.mem.eql(u8, before_manifest, changed_meta)); +} + test "calculateStoreContentHashV4 fixed protocol vector" { const th = @import("test_helpers.zig"); var test_env = try th.createTestEnv(); From bafd6f3ab5ec2606ea4309f259dc0efa66a6bf43 Mon Sep 17 00:00:00 2001 From: Jeremy Huntwork Date: Sun, 23 Aug 2026 22:15:35 -0400 Subject: [PATCH 2/2] Exclude all derived manifest formats from store identity --- src/hash.zig | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/src/hash.zig b/src/hash.zig index 658d699..ccdcdf2 100644 --- a/src/hash.zig +++ b/src/hash.zig @@ -368,6 +368,10 @@ fn isExcludedStoreHashPath(entry_path: []const u8, include_metadata: bool) bool std.mem.eql(u8, entry_path, ".mere/manifest.v1.sig") or std.mem.eql(u8, entry_path, ".mere/manifest.v2") or std.mem.eql(u8, entry_path, ".mere/manifest.v2.sig") or + std.mem.eql(u8, entry_path, ".mere/manifest.v3") or + std.mem.eql(u8, entry_path, ".mere/manifest.v3.sig") or + std.mem.eql(u8, entry_path, ".mere/manifest.v4") or + std.mem.eql(u8, entry_path, ".mere/manifest.v4.sig") or std.mem.eql(u8, entry_path, ".mere/manifest.v5") or std.mem.eql(u8, entry_path, ".mere/manifest.v5.sig") or std.mem.eql(u8, entry_path, ".mere/projection.v1"); @@ -1079,7 +1083,7 @@ test "calculateStoreContentHashV4 authenticates canonical permission classes" { try std.testing.expect(!std.mem.eql(u8, public_read, owner_read)); } -test "calculateStoreContentHashV4 excludes manifest v5 but includes canonical metadata" { +test "calculateStoreContentHashV4 excludes derived manifests but includes canonical metadata" { const th = @import("test_helpers.zig"); var test_env = try th.createTestEnv(); defer { @@ -1105,7 +1109,14 @@ test "calculateStoreContentHashV4 excludes manifest v5 but includes canonical me const before_manifest = try calculateStoreContentHashV4(test_env.ctx.allocator, test_env.path, null); defer test_env.ctx.allocator.free(before_manifest); - for ([_][]const u8{ "manifest.v5", "manifest.v5.sig" }) |name| { + for ([_][]const u8{ + "manifest.v3", + "manifest.v3.sig", + "manifest.v4", + "manifest.v4.sig", + "manifest.v5", + "manifest.v5.sig", + }) |name| { const manifest_path = try std.fs.path.join(test_env.ctx.allocator, &.{ mere_dir, name }); defer test_env.ctx.allocator.free(manifest_path); var manifest_file = try std.Io.Dir.createFileAbsolute(path.currentIo(), manifest_path, .{});