Skip to content
zit zit 0.3.24
Login

zit 0.3.24

zit 0.3.24 closes a door in branch protection: a push may no longer close, or reopen, a protected branch it is not allowed to commit to. It also fixes a crash in the container decoder that any peer could trigger with a damaged sync body, and makes that decoder verify the checksum it read.

No artifact, tag namespace, sync message, or on-disk repository format changed. One more shape of pushed tag is refused by a server that protects branches; every other push is admitted exactly as before.

Closing a protected branch is a protected move

A server given protected-branch globs refuses a pushed check-in that would join one of those branches, and a pushed tag that would move a held check-in into or out of one: sym-B under any sign, and branch=B by value. Both name the branch in the tag itself, so the gate judged tags by what they named.

A closed tag names a check-in, not a branch. The gate let it through, and its test said so on purpose: closing a leaf is not a membership change. That reasoning was right and the rule was still half a rule. An account with check-in rights but no ownership of trunk's protection could not push onto trunk, and could close its tip. The owners' next commit was then refused against a closed leaf, with a message that named neither the cause nor the actor, and the recovery was a raw tag cancel that appears in no interface. The cheapest way past a protected branch was not to push to it.

The gate now treats lifecycle as it treats membership. A closed tag aimed at a check-in this server holds resolves that check-in's branch through the resolver the gate already has, and is refused when the branch is protected, under any sign: +closed and *closed would close it, -closed would reopen it, and the refusal says which.

The line is drawn narrowly. A release name on a protected leaf is admitted. Closing a leaf off the protected branch is admitted. A closed aimed at a check-in this server does not hold is admitted, as an unknown parent is: it cannot be judged yet and its target is judged on arrival. A check-in that closes itself has no target and is judged by where it joins. A resolver that cannot answer refuses, as it always did; a protection that admits under a read fault is not one.

A damaged container is an error, not a crash and not wrong bytes

The first full nightly soak, added in the previous release, found this on its first night. The fuzz target that feeds the container codec damaged containers took a valid container, flipped one bit, and brought the process down: an integer underflow inside the standard library's inflate, in its end-of-input bit peek, that a truncated stream can arrange. Every sync request and reply passes through that decoder, so any peer could send those bytes to a server or a client.

The decoder is now the tree's own copy of the standard one, with the comparison rearranged so the stream ends instead of underflowing, and a note on where it came from and why; it goes back to the standard library when the fix is upstream. With the crash gone, the same bytes decoded to fifty-eight bytes reported as good: the flip had made a block final, the stream ended cleanly early, and the zlib checksum in the footer was read but never checked. The codec now requires a finished stream and verifies the footer's Adler-32 against the output. A container that lies about either is refused. The exact bytes are a fixed test.

Compatibility

Qualification

zig fmt --check, zig build test, e2e, smoke, muscle, git-import-e2e, git-export-e2e, dst, and cross, plus the three-runner CI battery.