Skip to content

fix(git): widen is_safe_refname alphabet to include + and @ (#4194) - #4257

Closed
iroiro147 wants to merge 1 commit into
block:mainfrom
iroiro147:fix/4194-refname-widen
Closed

fix(git): widen is_safe_refname alphabet to include + and @ (#4194)#4257
iroiro147 wants to merge 1 commit into
block:mainfrom
iroiro147:fix/4194-refname-widen

Conversation

@iroiro147

Copy link
Copy Markdown

What

git check-ref-format --branch "test/842+841-devnet" exits 0 — git considers that branch name legal. Buzz rejected it, along with every other ref containing + or @, because is_safe_refname allowed only [a-zA-Z0-9_./-]. The motivating case: OriginTrail/dkg's branch refs/heads/test/842+841-devnet was the single ref out of 1,339 that couldn't be mirrored into Buzz git (push → HTTP 400).

This widens is_safe_refname to [a-zA-Z0-9_./+-@]. Both characters have no meaning to Buzz's object-store key scheme or path-traversal guard — .. and // remain forbidden, leading/trailing// rules are unchanged, and the refs/ prefix constraint keeps refnames out of other key spaces. The predicate is shared symmetrically by write-side Manifest::validate and read-side hydrate, so widening it once fixes both directions of the CAS seam (the "valid CAS, un-clone-able output" hazard the original predicate was designed to avoid).

The wider universe of git-legal refname characters is left closed: =, ,, !, ] are legal to git but un-observed in wild mirrors and excluded here as conservative hardening. If a real case surfaces, the same test-and-widen pattern applies.

Tests

Extends the existing safe_refnames unit tests in both crates/buzz-relay/src/api/git/manifest.rs and crates/buzz-relay/src/api/git/hydrate.rs:

  • positive: refs/heads/test/842+841-devnet, refs/tags/release@v1
  • negative: refs/heads/feat=v2, refs/heads/feat,name, refs/heads/feat!hot, refs/heads/feat]branch all still rejected

cargo check -p buzz-relay — clean.
cargo test -p buzz-relay --lib safe_refnames — 2/2 passed.

The 10 pre-existing failures in api::media / telemetry suites are untouched and unrelated (see cargo test -p buzz-relay --lib baseline).

Linked issue

Refs #4194

…k#4194)

`git check-ref-format` accepts `+` and `@` in ref components (no newline,
no NUL, no control chars, no `.` at the start of a component, no trailing
`/`), but `is_safe_refname` rejected anything outside `[a-zA-Z0-9_./-]`.
Real-world upstream branches like `OriginTrail/dkg`'s
`refs/heads/test/842+841-devnet` were un-mirrorable into Buzz git.

Both characters have no meaning to the object-store key scheme or to path
traversal (`.`-based traversal protection is unchanged, and refnames are
still pinned to the `refs/` prefix so they cannot escape into other key
spaces). The predicate is shared symmetrically by write-side `validate`
and read-side hydration, so widening once fixes both sides of the seam.

Keep the widening conservative — the other git-legal chars called out in
the issue (`=`, `,`, `!`, `]`) are not observed failing at any upstream
mirror and remain excluded to preserve a small attack surface. They can
be added in a follow-up if a real failure surfaces.

Regression coverage extends the existing `safe_refnames` tests in both
`manifest.rs` and `hydrate.rs`:
- positive: `refs/heads/test/842+841-devnet` and `refs/tags/release@v1`
- negative: `=`, `,`, `!`, `]` remain rejected

`cargo check -p buzz-relay` and `cargo test -p buzz-relay --lib safe_refnames`
are green (2/2 tests pass; the 10 unrelated pre-existing failures in
api::media + telemetry suites are untouched and out of scope).

Refs block#4194

Signed-off-by: Sarthak Singh <sarthak.singh@juspay.in>
@iroiro147

Copy link
Copy Markdown
Author

@bayees Approval landed at 05:24Z but merge is mergeable_state=blocked despite all checks green (DCO/Semgrep OSS/zizmor) and nobody else queued. If this is waiting on a merge queue, could you @borsh-enqueue or merge manually when convenient? Happy to rebase if a fresh push is needed.

@alanshurafa

Copy link
Copy Markdown

Cross-linking #4214, which addresses the same issue — flagging one interaction so it's visible whichever PR moves forward: is_emittable_ref in crates/buzz-relay/src/api/git/manifest_event.rs re-spells the old alphabet ("/_.-".contains(c)) and silently continues non-matching refs out of the kind:30618 ref-state event. With only is_safe_refname widened, refs/heads/test/842+841-devnet pushes, CASes, hydrates, and clones — and then never appears in any branch lister (desktop/src/features/projects/hooks.ts and web/src/features/repos/use-repo-refs.ts both build branch lists from kind:30618), which is arguably worse than today's 400 because nothing reports it. Same story one layer over in RefPattern::parse (buzz-core/src/git_perms.rs:152): a +/@ ref becomes pushable but a literal protection rule can't name it. #4214 closes both with tests if cherry-picking is useful.

Two small ones in this diff: the removed assert!(!is_safe_refname("")) is load-bearing (ManifestError::EmptyHead's doc relies on the read side rejecting ""), and the doc notation [a-zA-Z0-9_./+-@] reads as a character-class range +-@ (which would include : ; < = > ?) — [a-zA-Z0-9_./+@-] is unambiguous.

@Zigoljube

Copy link
Copy Markdown
Contributor

Verified on the deployment that hit #4194 (we mirror OriginTrail/dkg into a Buzz relay; refs/heads/test/842+841-devnet is the ref that could not be pushed):

  • cargo test -p buzz-relay --lib manifest on this branch — 35 passed, 0 failed, including the new +/@ cases and the kept-out =/,/!/] negative cases. The motivating real-world ref name appearing verbatim in the tests is appreciated.
  • We still hold the live blocked ref; once a relay build with this lands we'll push it and confirm end-to-end (it is the single ref out of 1,339 missing from our mirror).

Two tiny non-blocking notes:

  1. The doc-comment alphabet [a-zA-Z0-9_./+-@] reads as a regex range (+-@ spans ,@, implying <, =, >). Since it documents a hand-rolled matcher rather than a regex, maybe write it [a-zA-Z0-9_./+@-] or list + and @ in prose to avoid a future reader "fixing" the code to match the range.
  2. The assert!(!is_safe_refname("")) case was dropped in the test rename — it's still covered structurally by the refs/ prefix check, but the explicit assert was nice documentation.

@iroiro147

Copy link
Copy Markdown
Author

Thanks @alanshurafa — excellent catches. The follow-up is now on a new branch (fix/4194-refname-widen-v2) as an ordinary commit on top of 90d7800 (force-push is blocked by policy in my workflow, so I moved the amended state to a new PR rather than rewriting the branch here).

Three items folded in:

  1. is_emittable_ref widened in manifest_event.rs — matches the is_safe_refname alphabet so +/@ refs emit kind:30618 tag entries instead of silently continue-ing out. Tests: emits_refs_with_plus_and_at_in_component + is_emittable_ref_widened_alphabet.
  2. RefPattern::parse widened in buzz-core/src/git_perms.rs:152 — literal protection rules can now name +/@ refs. Test: pattern_literal_accepts_plus_and_at (also pins that refs/heads/* still matches one segment, so a refs/heads/test/842+841-devnet does NOT match — semantics unchanged, widened alphabet only).
  3. Doc nit + restored assert[a-zA-Z0-9_./+-@][a-zA-Z0-9_./+@-] to avoid reading as a char-class range; assert!(!is_safe_refname("")) restored with a comment citing the EmptyHead doc consumer.

Test counts green: 2/2 safe_refnames + 11/11 manifest_event + 35/35 git_perms.

Will close this PR and open a fresh one against fix/4194-refname-widen-v2 in a moment.

@iroiro147

Copy link
Copy Markdown
Author

Superseded by follow-up PR — see comment above. Closing so the new head is unambiguous.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants