fix(relay/git): accept git-legal + and @ in ref names - #4214
Open
alanshurafa wants to merge 1 commit into
Open
Conversation
is_safe_refname allowed only [a-zA-Z0-9_./-], so git-legal refs like
refs/heads/test/842+841-devnet (a real branch in OriginTrail/dkg) or
dependabot's @-scoped npm branches could not be pushed at all: the push
400s at pre-CAS manifest validation after receive-pack already ran.
Widen the shared predicate's alphabet with '+' and '@'. Both characters
are inert everywhere the predicate guards: refnames live inside manifest
JSON (never object-store keys), hydrate writes them as loose-ref paths
(both are valid filename bytes on Unix and NTFS), the info/refs fast
path emits them into pkt-line payloads where they carry no meaning, and
refnames never reach a shell or subprocess argv. The @-forms git forbids
stay unreachable: a whole-refname '@' cannot occur behind the mandatory
refs/ prefix, and '@{' needs '{', still outside the alphabet.
Widen RefPattern::parse's literal-segment alphabet identically so every
ref the relay now accepts can also be named by a branch-protection rule
(matching is segment-literal equality; no glob semantics involved).
Fixes block#4194
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Signed-off-by: Agent57 <agent57@shurafa.com>
alanshurafa
force-pushed
the
fix/refname-plus-at-4194
branch
from
August 2, 2026 14:29
80b1a22 to
0f5f9fe
Compare
Author
|
@block/buzz-oss-team — could a maintainer please approve the workflow runs for this fork PR and do a code-owner review? The reachable checks (Semgrep OSS, zizmor, DCO) pass and the branch is rebased on current main (28ae6cd). Thanks! |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
is_safe_refnameallows only[a-zA-Z0-9_./-], so git-legal ref names containing+or@cannot be pushed at all — the push 400s at pre-CAS manifest validation. The reported real-world case isrefs/heads/test/842+841-devnetinOriginTrail/dkg(1 ref out of 1,339 blocks the mirror);@covers the equally common dependabot patternrefs/heads/dependabot/npm_and_yarn/@types/node-1.2.3.This PR widens the shared predicate's alphabet to
[a-zA-Z0-9_./+@-], and closes the two places that would otherwise re-reject or silently drop the newly accepted refs:RefPattern::parse's literal-segment alphabet inbuzz-corewidens to match, so every ref the relay now accepts can also be named by a branch-protection rule (literal matching is segment-equality, not glob, so neither character carries metacharacter meaning).is_emittable_refinmanifest_event.rshad a private re-spelling of the old alphabet and would have silently omitted+/@refs from the kind:30618 ref-state event — a branch that pushes, hydrates, and clones fine but is invisible in every branch lister (desktop and web both read kind:30618). It now delegates character/structure rules to the sharedis_safe_refname(keeping only the NIP-34 heads/tags namespace filter local), so this filter can never again drift from what the write path accepts. A test pins the round-trip.Why this is safe (verified against every consumer of the predicate):
repos/<community>/<owner>/<repo>/pointer; refnames live inside the manifest JSON body. (The issue suggested percent-encoding at the storage boundary; it turns out none is needed.) No encoding, no collision surface.+and@are valid filename bytes on Unix and NTFS, and the structural checks (..,//, control chars, prefix) are untouched.info/refsfast path re-runs the same predicate before emitting refnames into pkt-line bytes; both characters are inert there.for-each-refoutput parsing splits on whitespace, which git forbids in refnames.@forms git forbids stay unreachable: a refname that is entirely@cannot occur behind the mandatoryrefs/prefix, and@{requires{, which remains outside the alphabet. (git check-ref-formatconfirmsrefs/heads/@andrefs/@/xare legal git, so no per-component@restriction is added.)Adjacent and deliberately untouched:
.locksuffix, leading-dot components). Not reachable in practice — refs enter manifests viareceive-pack/for-each-ref, which enforce git's rules — and tightening is a separate concern from this widening.clean_branchinsrc-tauri/commands/project_git_exec.rs, a secondclean_target_refinproject_git_diff.rs, andBRANCH_CHARACTERSinsrc/features/projects/lib/projectBranches.ts). Those are visible client-side rejections (a user can't select/create such a branch from the desktop UI), not silent server-side drops, and fixing them means touching the desktop TS test suite — happy to do that here if preferred, but it seemed better as a follow-up so the relay fix isn't blocked on desktop tooling. Two hazards for whoever picks it up:clean_branch's output is passed as a bare refspec positional togit fetch(a leading+means force there — prefixrefs/heads/or guard it), and itstrim_start_matches("refs/heads/")strips repeatedly, sorefs/heads/@would reduce to@, git's HEAD alias. Server-side, this PR is complete: push → CAS → hydrate → advertisement → kind:30618 all accept the same set.is_safe_refname, so git-legal refs outside ASCII (e.g. non-ASCII branch names) still authorize, run receive-pack, and then 400 at manifest validation — the same failure shape Git: is_safe_refname rejects legal git ref names containing '+' #4194 reports, for a much bigger character class. Unifying those two gates is a larger conversation than two characters; flagging rather than fixing here.Related issue
Fixes #4194. Duplicate search at filing (2026-08-02 00:59 UTC): none found. Update: #4244 and #4257 have since been filed for the same issue. Both widen the shared predicate only; neither covers the kind:30618 emission filter (
is_emittable_ref) or theRefPatternliteral alphabet, so+/@refs would push but stay invisible to branch listers and unnameable by literal protection rules. This PR covers all three surfaces.Testing
cargo test -p buzz-core git_perms— 35 passed (new: literal patterns with+/@parse and match).cargo test -p buzz-relay --lib api::git— passed (new predicate cases in bothmanifest.rsandhydrate.rstest suites: the reported+ref, the dependabot@ref,refs/tags/v1.0.0+build.5,refs/heads/user@host,refs/heads/@,refs/@/xaccepted;@{, traversal, control chars, prefix violations still rejected).cargo clippy --workspace --all-targets -- -D warningsandcargo fmt --all -- --check— clean.x86_64-pc-windows-gnu(same setup as fix(audit): hash created_at at the precision Postgres stores #2638); Postgres-gated suites (#[ignore]) not run.